【算法系列-二叉树】二叉树遍历系列(递归+迭代)

【算法系列-二叉树】二叉树遍历系列(递归+迭代)

欢迎来到【算法系列 】第六弹 🏆 二叉树 ,接下来我们将围绕二叉树这类型的算法题进行解析与练习!一起加油吧!!( •̀ ω •́ )✧✨

文章目录

  • 【算法系列-二叉树】二叉树遍历系列(递归+迭代)
    • [1. 算法分析🛸](#1. 算法分析🛸)
    • [2. 递归遍历](#2. 递归遍历)
      • [2.1 思路分析🎯](#2.1 思路分析🎯)
      • [2.2 代码示例🌰](#2.2 代码示例🌰)
        • [2.2.1 前序遍历](#2.2.1 前序遍历)
        • [2.2.2 中序遍历](#2.2.2 中序遍历)
        • [2.2.3 后序遍历](#2.2.3 后序遍历)
    • [3. 迭代遍历](#3. 迭代遍历)
      • [3.1 思路分析🎯](#3.1 思路分析🎯)
      • [3.2 代码示例🌰](#3.2 代码示例🌰)
        • [3.2.1 前序遍历](#3.2.1 前序遍历)
        • [3.2.2 后序遍历](#3.2.2 后序遍历)
        • [3.2.3 中序遍历](#3.2.3 中序遍历)
      • [3.3 统一迭代法🎬](#3.3 统一迭代法🎬)

1. 算法分析🛸

在二叉树中,前中后序遍历其实就是中间节点的遍历顺序

  • 前序遍历:中左右
  • 中序遍历:左中右
  • 后序遍历:左右中

如图所示:

前序遍历结果:1 2 3 4 5 6

中序遍历结果:3 2 1 5 4 6

后序遍历结果:3 2 5 6 4 1

对此,有两种方式来对二叉树进行遍历,即 递归迭代

2. 递归遍历

2.1 思路分析🎯

通过递归的方式来对二叉树进行遍历,而使用递归需要明确三个部分:

  1. 递归参数
  2. 终止条件
  3. 执行内容

在这里,我们递归参数需要一个遍历节点和一个存放返回数据的数组,而终止条件就是当遍历到的节点为空时则返回,执行内容即按当前遍历方式取中节点的值存入返回数组,并按顺序遍历其它分支节点

2.2 代码示例🌰

2.2.1 前序遍历

【题目链接】144. 二叉树的前序遍历 - 力扣(LeetCode)

java 复制代码
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        preorder(root, ret);
        return ret;
    }

    public void preorder(TreeNode cur, List<Integer> list) {
        if (cur == null) {
            return;
        }
        list.add(cur.val);
        preorder(cur.left, list);
        preorder(cur.right, list);
    }
}
2.2.2 中序遍历

【题目链接】94. 二叉树的中序遍历 - 力扣(LeetCode)

java 复制代码
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        inorder(root, ret);
        return ret;
    }

    void inorder(TreeNode cur, List<Integer> list) {
        if (cur == null) {
            return;
        }
        inorder(cur.left, list);
        list.add(cur.val);
        inorder(cur.right, list);
    }
}
2.2.3 后序遍历

【题目链接】145. 二叉树的后序遍历 - 力扣(LeetCode)

java 复制代码
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        postorder(root, ret);
        return ret;
    }

    void postorder(TreeNode cur, List<Integer> list) {
        if (cur == null) {
            return;
        }
        postorder(cur.left, list);
        postorder(cur.right, list);
        list.add(cur.val);
    }
}

3. 迭代遍历

3.1 思路分析🎯

迭代遍历的方式是通过 来操作我们的二叉树节点(递归的底层实现便是用到了调用栈)

在不同的顺序中,节点进入栈的顺序也是不同的**(因为栈是先进后出的**):

  • 前序遍历:因为要实现中左右,则 遇到中节点则将中节点的值加入结果集,之后右节点先入栈,紧接着左节点再入栈;
  • 后序遍历:因为要实现左右中,相当于 中右左翻转,则 中节点顺序同上,之后左节点先入栈,紧接着右节点再入栈,最后反转数组;

3.2 代码示例🌰

3.2.1 前序遍历

【题目链接】144. 二叉树的前序遍历 - 力扣(LeetCode)

java 复制代码
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> ret = new ArrayList<>();
        TreeNode cur = root;
        stack.push(cur);
        while (!stack.isEmpty()) {
            cur = stack.pop();
            if (cur != null) {
                ret.add(cur.val);
                stack.push(cur.right);
                stack.push(cur.left);
            }
        }
        return ret;
    }
}
3.2.2 后序遍历

【题目链接】145. 二叉树的后序遍历 - 力扣(LeetCode)

前中后序的实现只需要交换一下左右节点入栈顺序即可,记得反转数组

java 复制代码
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> ret = new ArrayList<>();
        TreeNode cur = root;
        stack.push(cur);
        while (!stack.isEmpty()) {
            cur = stack.pop();
            if (cur != null) {
                ret.add(cur.val);
                stack.push(cur.left);
                stack.push(cur.right);
            }
        }
        Collections.reverse(ret);
        return ret;
    }
}
3.2.3 中序遍历

【题目链接】94. 二叉树的中序遍历 - 力扣(LeetCode)

中序遍历有些不同,无法按上述的顺序进行排列,开始它需要先一直遍历左节点直到左节点为空时再进行中节点结果存储,并开始遍历右节点

java 复制代码
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> ret = new ArrayList<>();
        TreeNode cur = root;
        
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            else {
                cur = stack.pop();
                ret.add(cur.val);
                cur = cur.right;
            }
        }
        return ret;
    }
}

3.3 统一迭代法🎬

上述迭代方式中,中序遍历与前后续遍历的迭代方式存在着一些不同,但想让迭代遍历能和递归遍历一样有一个大概统一的模板,对此提供下述的方法:

java 复制代码
// 中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> ret = new ArrayList<>();
        TreeNode cur = root;
        stack.push(cur);
        while (!stack.isEmpty()) {
            cur = stack.peek();
            if (cur != null) {
                stack.pop(); // 避免重复使用节点
                if (cur.right != null) {
                    stack.push(cur.right);
                }
                stack.push(cur);
                stack.push(null); // 加入空节点表示当前中间节点并未处理
                if (cur.left != null) {
                    stack.push(cur.left);
                }
            }
            else {
                stack.pop(); // 弹出空节点
                cur = stack.pop();
                ret.add(cur.val);
            }
        }
        return ret;
    }
}

只有在当前节点为空的时候才处理节点的数据,不同的序列只需要交换中间节点的入栈顺序即可:

java 复制代码
// 前序遍历
if (cur.right != null) {
    stack.push(cur.right);
}
if (cur.left != null) {
    stack.push(cur.left);
}
stack.push(cur);
stack.push(null); // 加入空节点表示当前中间节点并未处理
../ 

// 后序遍历
stack.push(cur);
stack.push(null); // 加入空节点表示当前中间节点并未处理
if (cur.right != null) {
    stack.push(cur.right);
}
if (cur.left != null) {
    stack.push(cur.left);
}
../

以上便是对二叉树遍历系列问题的介绍了!!后续还会继续分享其它算法系列内容,如果这些内容对大家有帮助的话请给一个三连关注吧💕( •̀ ω •́ )✧( •̀ ω •́ )✧✨

相关推荐
Swift社区1 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman2 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
IT 青年2 小时前
数据结构 (1)基本概念和术语
数据结构·算法
Dong雨3 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展
SoraLuna3 小时前
「Mac玩转仓颉内测版24」基础篇4 - 浮点类型详解
开发语言·算法·macos·cangjie
liujjjiyun3 小时前
小R的随机播放顺序
数据结构·c++·算法
¥ 多多¥3 小时前
c++中mystring运算符重载
开发语言·c++·算法
trueEve4 小时前
SQL,力扣题目1369,获取最近第二次的活动
算法·leetcode·职场和发展
天若有情6734 小时前
c++框架设计展示---提高开发效率!
java·c++·算法
ahadee5 小时前
蓝桥杯每日真题 - 第19天
c语言·vscode·算法·蓝桥杯