二叉树的中序遍历算法(Java三种实现方法)

文章目录

  • 题目
  • 一、二叉树的节点定义
  • 二、三种遍历方法
    • 1.递归
      • 算法思想
    • 2.迭代
      • 算法思想
    • 3.Morris 中序遍历
      • 算法思想
  • 总结


题目

给定一个二叉树的根节点 root ,返回它的 中序 遍历


一、二叉树的节点定义

public class TreeNode {
      int val;
      TreeNode left;
      TreeNode right;
      TreeNode() {}
      TreeNode(int val) { this.val = val; }
      TreeNode(int val, TreeNode left, TreeNode right) {
          this.val = val;
          this.left = left;
          this.right = right;
     }
 }

二、三种遍历方法

1.递归

算法思想

我们都知道中序遍历就是先遍历节点的左子树节点,然后访问根节点,最后再遍历右子树。而在访问左子树或者右子树的时候我们按照同样的方式遍历,直到遍历完整棵树。因此整个遍历过程天然具有递归的性质。

代码如下:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        inoder(root, list);
        return list;
    }
    // 递归遍历
    public void inoder(TreeNode root,List list){
        if(root == null){  //如果节点为null  这直接退出递归
             return;
         }
        inoder(root.left, list); // 访问左子树
        list.add(root.val);  	// 将节点的值存入到list中
        inoder(root.right,list);  //访问右子树
    }
  }

复杂度分析:

  • 时间复杂度:O(n)。其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

  • 空间复杂度:O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别。

2.迭代

算法思想

递归函数我们也可以用迭代的方式实现,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其他都相同。

代码如下:

class Solution {
	// 迭代遍历    通过压栈和出栈来模拟递归过程  本质和上面的递归是一样的
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Deque<TreeNode> stk = new LinkedList<TreeNode>();  //初始化栈
        while (root != null || !stk.isEmpty()) {  // 如果节点为空或栈为空  则遍历结束
            while (root != null) {  //如果根节点不为空
                stk.push(root);  // 将根节点压栈
                root = root.left;  //访问左子树
            }
            root = stk.pop(); // 存在节点为空  此时栈中从栈顶的元素一定为子树的最左边一个子节点
            res.add(root.val); // 将子节点存入列表中
            root = root.right;  //访问右节点
        }
        return res;
    }
}

复杂度分析:

  • 时间复杂度:O(n)。其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

  • 空间复杂度:O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别。

3.Morris 中序遍历

算法思想

Morris 遍历算法是另一种遍历二叉树的方法,它能将非递归的中序遍历空间复杂度降为 O(1)。

Morris 遍历算法整体步骤如下(假设当前遍历到的节点为 x):

  1. 如果 x 无左孩子,先将 x 的值加入答案数组,再访问 x 的右孩子,即 x=x.right。
  2. 如果 x 有左孩子,则找到 xx左子树上最右的节点(即左子树中序遍历的最后一个节点,x在中序遍历中的前驱节点),我们记为 predecessor。根据 predecessor 的右孩子是否为空,进行如下操作。
    • 如果 predecessor 的右孩子为空,则将其右孩子指向 x,然后访问 x的左孩子,即 x=x.left。
    • 如果 predecessor 的右孩子不为空,则此时其右孩子指向 x,说明我们已经遍历完 x 的左子树,我们将 predecessor 的右孩子置空,将 x 的值加入答案数组,然后访问 x 的右孩子,即 x = x.right。
      重复上述操作,直至访问完整棵树

代码如下:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        TreeNode predecessor = null;

        while (root != null) {
            if (root.left != null) {
                // predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
                predecessor = root.left;
                while (predecessor.right != null && predecessor.right != root) {
                    predecessor = predecessor.right;
                }
                
                // 让 predecessor 的右指针指向 root,继续遍历左子树
                if (predecessor.right == null) {
                    predecessor.right = root;
                    root = root.left;
                }
                // 说明左子树已经访问完了,我们需要断开链接
                else {
                    res.add(root.val);
                    predecessor.right = null;
                    root = root.right;
                }
            }
            // 如果没有左孩子,则直接访问右孩子
            else {
                res.add(root.val);
                root = root.right;
            }
        }
        return res;
    }
}

大概执行过程:
二叉树的中序遍历算法(Java三种实现方法)_第1张图片
二叉树的中序遍历算法(Java三种实现方法)_第2张图片
二叉树的中序遍历算法(Java三种实现方法)_第3张图片
二叉树的中序遍历算法(Java三种实现方法)_第4张图片
二叉树的中序遍历算法(Java三种实现方法)_第5张图片
二叉树的中序遍历算法(Java三种实现方法)_第6张图片

二叉树的中序遍历算法(Java三种实现方法)_第7张图片
二叉树的中序遍历算法(Java三种实现方法)_第8张图片
二叉树的中序遍历算法(Java三种实现方法)_第9张图片
二叉树的中序遍历算法(Java三种实现方法)_第10张图片
二叉树的中序遍历算法(Java三种实现方法)_第11张图片
二叉树的中序遍历算法(Java三种实现方法)_第12张图片
二叉树的中序遍历算法(Java三种实现方法)_第13张图片
二叉树的中序遍历算法(Java三种实现方法)_第14张图片
二叉树的中序遍历算法(Java三种实现方法)_第15张图片
二叉树的中序遍历算法(Java三种实现方法)_第16张图片
二叉树的中序遍历算法(Java三种实现方法)_第17张图片
二叉树的中序遍历算法(Java三种实现方法)_第18张图片
复杂度分析

  • 时间复杂度:O(n)。其中 n 为二叉搜索树的节点个数。Morris 遍历中每个节点会被访问两次,因此总时间复杂度为 O(2n)=O(n)。

  • 空间复杂度:O(1)。


总结

本篇文章主要就是说明了三种实现二叉树中序遍历的方法,并且对其时间和空间复杂度有分析了一遍,最后morris算法总体来说是最高效的一个。

你可能感兴趣的:(算法,java,数据结构)