算法分析之二叉树遍历

news/2024/7/20 22:17:52 标签: 算法, 深度优先, leetcode, 二叉树的遍历, Java

算法相关数据结构总结:

序号数据结构文章
1动态规划动态规划之背包问题——01背包
动态规划之背包问题——完全背包
动态规划之打家劫舍系列问题
动态规划之股票买卖系列问题
动态规划之子序列问题
算法Java)——动态规划
2数组算法分析之数组问题
3链表算法分析之链表问题
算法Java)——链表
4二叉树算法分析之二叉树
算法分析之二叉树遍历
算法分析之二叉树常见问题
算法Java)——二叉树
5哈希表算法分析之哈希表
算法Java)——HashMap、HashSet、ArrayList
6字符串算法分析之字符串
算法Java)——字符串String
7栈和队列算法分析之栈和队列
算法Java)——栈、队列、堆
8贪心算法算法分析之贪心算法
9回溯Java实现回溯算法入门(排列+组合+子集)
Java实现回溯算法进阶(搜索)
10二分查找算法Java)——二分法查找
11双指针、滑动窗口算法Java)——双指针
算法分析之滑动窗口类问题

二叉树的基础知识已经在上一篇文章算法分析之二叉树学习过了,这篇文章主要是二叉树的遍历方式,包括递归和迭代版本。

二叉树的相关算法,如属性,操作,二叉搜索树等,请参考:算法分析之二叉树常见问题。

文章目录

      • 一、二叉树的遍历
      • 二、二叉树的递归遍历
        • 1. 二叉树的前序遍历
        • 2. 二叉树的中序遍历
        • 3. 二叉树的后序遍历
      • 三、二叉树的迭代遍历
        • 1. 二叉树的前序遍历
        • 2. 二叉树的中序遍历
        • 3. 二叉树的后序遍历
      • 四、二叉树的层序遍历
        • 1. 层序遍历的迭代实现
        • 2. leetcode关于层序遍历的算法
          • 107. 二叉树的层序遍历 II
          • 199. 二叉树的右视图

一、二叉树的遍历

二叉树的遍历方式主要有两种:

  1. 深度优先遍历(DFS):前序、中序、后续遍历
  2. 广度优先遍历(BFS):层序遍历

leetcode相关题目:

144. 二叉树的前序遍历

94. 二叉树的中序遍历

145. 二叉树的后序遍历

102. 二叉树的层序遍历

二、二叉树的递归遍历

先写一下递归的三要素:

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

  2. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

  3. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

1. 二叉树的前序遍历

前序遍历:根左右

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        // 递归版本
        ArrayList<Integer> res = new ArrayList<>();
        preOrder(root, res);
        return res;
    }

    public void preOrder(TreeNode root, ArrayList<Integer> res) {
        if(root == null) return;
        res.add(root.val);
        preOrder(root.left, res);
        preOrder(root.right, res);
    }
}

2. 二叉树的中序遍历

中序遍历:左根右

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        // 递归版本
        ArrayList<Integer> res = new ArrayList<>();
        inOrder(root, res);
        return res;
    }
    public void inOrder(TreeNode root, ArrayList<Integer> res) {
        if(root == null) return;
        inOrder(root.left, res);
        res.add(root.val);
        inOrder(root.right, res);
    }
}

3. 二叉树的后序遍历

后序遍历:左右根

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        // 递归版本
        ArrayList<Integer> res = new ArrayList<>();
        postOrder(root, res);
        return res;
    }
    public void postOrder(TreeNode root, ArrayList<Integer> res) {
        if(root == null) return;
        postOrder(root.left, res);
        postOrder(root.right, res);
        res.add(root.val);
    }
}

三、二叉树的迭代遍历

用栈来迭代实现二叉树的遍历,下面是用一个统一的模板来实现前中后序的迭代遍历,前序和中序比较简单,只需要修改加入的顺序,后续遍历稍微复杂,需要判断右节点。

1. 二叉树的前序遍历

用栈来迭代实现二叉树的前序遍历:

前序遍历是根左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。这样出栈的时候才是根左右的顺序。

在这里插入图片描述

一边把根节点和左节点加入list,一边左压栈,全部入栈后,出栈找右节点,将右节点加入list

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {

        // 非递归迭代版本,栈
        ArrayList<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()) {  
            while(cur != null) {  // 一直左压栈
                res.add(cur.val);
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            cur = cur.right;
        }
        return res;
    }
}

2. 二叉树的中序遍历

用栈来迭代实现二叉树的中序遍历:

中序遍历是左根右。

在这里插入图片描述

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        // 非递归迭代版本,栈
        ArrayList<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()) {  
            while(cur != null) {  // 一直左压栈
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            res.add(cur.val);
            cur = cur.right;
        }
        return res;
    }
}

3. 二叉树的后序遍历

用栈来迭代实现二叉树的后序遍历:

有很多写法都是把前序遍历反转来实现后序遍历,但这并不是后序遍历的迭代实现。

后序遍历稍微复杂的地方是需要设置一个节点,来保存上一个节点。遇到右节点的时候需要判断是不是上一个节点。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {        
        // 非递归,迭代版本
        ArrayList<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack();
        TreeNode cur = root;
        TreeNode pre = null;  // 记录上一个节点,用来判断右节点是不是上一个节点
        while(cur != null || !stack.isEmpty()) {  
            while(cur != null) {  // 一直左压栈
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.peek();
            // 后续遍历左节点加入list,遇到根节点,判断有没有右节点,有加右节点,无加根节点
            // 如果是从右边返回根节点,则应该返回上层,主要判断出来是不是右子树
            if(cur.right == null || cur.right == pre) {
                res.add(cur.val);
                stack.pop();
                pre = cur;
                cur = null;
            } else {
                cur = cur.right;
            }
        }
        return res;
    }
}

四、二叉树的层序遍历

1. 层序遍历的迭代实现

层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。

需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而是用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。

而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。

在这里插入图片描述

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        // 使用队列实现层序遍历,逐层将队列元素加入到list
        if(root == null) return new ArrayList<>();
        
        List<List<Integer>> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()) {
            int count = queue.size();
            List<Integer> list = new ArrayList<>();
            while(count > 0) {
                TreeNode node = queue.poll();
                list.add(node.val);
                if(node.left != null) {
                    queue.add(node.left);
                }
                if(node.right != null) {
                    queue.add(node.right);
                }
                count--;
            }
            res.add(list);
        }
        return res;
    }
}

leetcode_242">2. leetcode关于层序遍历的算法

107. 二叉树的层序遍历 II

leetcode题目链接:107. 二叉树的层序遍历 II

给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其自底向上的层序遍历为:

[
  [15,7],
  [9,20],
  [3]
]

解题思路:
这道题与 102. 二叉树的层序遍历 相同,只需要在最后输出的时候反转列表即可。

这里有几种反转列表的方法:

// 1. for循环遍历反转列表
List<List<Integer>> result = new ArrayList<>();
for (int i = res.size() - 1; i >= 0; i-- ) {
    result.add(res.get(i));
}
return result;
// 2. 利用LinkedList的addFirst()函数直接将list加到队首
LinkedList<List<Integer>> res = new LinkedList<>();
……
res.addFirst(list);

Java代码实现:

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        // 使用队列实现层序遍历,逐层将队列元素加入到list
        // 使用LinkedList的addFirst()
        LinkedList<List<Integer>> res = new LinkedList<>();
        // List<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()) {
            int count = queue.size();
            List<Integer> list = new ArrayList<>();
            while(count > 0) {
                TreeNode node = queue.poll();
                list.add(node.val);
                if(node.left != null) {
                    queue.add(node.left);
                }
                if(node.right != null) {
                    queue.add(node.right);
                }
                count--;
            }
            res.addFirst(list);
        }
        // // 反转列表
        // List<List<Integer>> result = new ArrayList<>();
        // for (int i = res.size() - 1; i >= 0; i-- ) {
        //     result.add(res.get(i));
        // }
        // return result;
        return res;
    }
}
199. 二叉树的右视图

leetcode题目链接:199. 二叉树的右视图

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

在这里插入图片描述

输入: [1,2,3,null,5,null,4]
输出: [1,3,4]

Java代码实现:

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        // 使用队列实现层序遍历,逐层将队列元素加入到list
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()) {
            int count = queue.size();
            for(int i = 0; i < count; i++) {
                TreeNode node = queue.poll();
                if(node.left != null) {
                    queue.add(node.left);
                }
                if(node.right != null) {
                    queue.add(node.right);
                }
                if(i == count - 1) {  // 将每一层的最后一个节点放入列表
                    res.add(node.val);
                }
            }
        }
        return res;
    }
}

参考:

代码随想录:二叉树的遍历

二叉树的层序遍历


http://www.niftyadmin.cn/n/1727812.html

相关文章

C# Tuple的用法

Tuple是返回多个参数&#xff0c;C# 4.0引入 最多支持8个参数&#xff0c;第八个参数是Tuple&#xff0c;意思就是参数多于8个就开始嵌套调用了 一个函数返回多个类型&#xff0c;这样就不在用out , ref等输出输入参数了&#xff0c;可以直接定义一个tuple类型就可以了。非常方…

算法分析之二叉树常见问题

算法相关数据结构总结&#xff1a; 序号数据结构文章1动态规划动态规划之背包问题——01背包 动态规划之背包问题——完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 算法&#xff08;Java&#xff09;——动态规划2数组算法分析之数…

C# LazyT延时加载类

概念&#xff1a;延时加载&#xff08;延时实例化或延时初始化&#xff09;重点是延时&#xff0c;用时加载。意思是对象在使用的时候创建而不是在实例化的的时候才创建。 延时加载主要应用的场景&#xff1a; 1.数据层&#xff08;ADO.NET或Entity Framework等ORM&#xff0c;…

C# _ = { }是什么情况

说起lambda表达式其实简单理解为一个方法&#xff0c;什么方法呢--是个匿名方法&#xff08;就是一个没名字的方法&#xff09; 第一种 () > { };是一个既没有参数又没有返回值的方法第二种 x > x2;是一个参数为x返回值为x2的方法第三种 (x, y) > x y;是一个参数为…

算法分析之贪心算法

算法相关数据结构总结&#xff1a; 序号数据结构文章1动态规划动态规划之背包问题——01背包 动态规划之背包问题——完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 算法&#xff08;Java&#xff09;——动态规划2数组算法分析之数…

C# :base的用法(冒号后面的base)

我前写过一篇文章叫 C# :this的用法&#xff08;冒号后面的this&#xff09; 文字写的多了感觉是没有必要的&#xff0c;能说明白&#xff0c;或者看的人能够悟到就可以了 1.this是标识当前资源对象的&#xff0c;而base是基于父级的。 2.base发挥了期灵魂级的作用——多态 3.…

vs2013 error c4996: 'fopen': This function or variable may be unsafe

在用VS2013开发一AntiRootkit程序时遇到以下错误&#xff1a; error C4996: fopen: This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. d:\用户目录\documents\v…

C# operator 关键字的用法

operator 只要是运算符都能重载 operator 关键字的主要作用是用来重载运算符的&#xff0c;还可以用于类或结构中类型的自定义转换。 下面看个例子 class Feige{//定义两个全局变量int a, b;//声明带两个参数的构造函数public Feige(int a, int b){this.a a;this.b b;}//重…