代码随想录第14天 二叉树Part01 递归遍历 迭代遍历

递归遍历

前序遍历(中左右)

写递归的三件事情确认:

1.递归结束条件

2.递归的参数和返回值

3.每次递归的操作

对于二叉树的遍历,递归的条件是root结点为空,那么就返回。递归传的参数,就是每次需要一个根节点和一个结果数组。每次递归的时候递归的使用这个递归方法,让root结点的左右孩子作为下一个root结点进行遍历,直到root的孩子为空就返回。中序遍历和后续遍历只需调整顺序即可。

. - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * 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;
 * }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        preorder(root, result);// 前序遍历方法,并将root的qianxu0遍历结果保存在result当中
        return result;

    }

    public void preorder(TreeNode root, List<Integer> result) {
        if (root == null)
            return;// 终止条件
        result.add(root.val);
        preorder(root.left, result);
        preorder(root.right, result);
    }
}

中序遍历

. - 力扣(LeetCode)

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        inorder(root, result);
        return result;
    }

    public void inorder(TreeNode root, List<Integer> result) {
        if (root == null)
            return;
        inorder(root.left, result);
        result.add(root.val);
        inorder(root.right, result);
    }

}

后序遍历

. - 力扣(LeetCode)

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        postorder(root, result);
        return result;
    }

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

迭代遍历

前序遍历(中左右)

144. 二叉树的前序遍历 - 力扣(LeetCode)

迭代遍历和递归遍历的区别是,迭代遍历是使用栈这种数据结构来对模仿二叉树的遍历操作的。对于栈这种“后进先出”的结构,每次将根节点入栈,并且存入结果数组。对于栈这种特殊的数据结构,要先放根节点的右孩子,然后再放左孩子,这样弹出栈顶元素时才能将左孩子弹出来。然后每次都是弹出栈顶元素。

/**
 * Definition for a binary tree node.
 * 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;
 * }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        // 运用非递归算法实现,用栈这种数据结构来实现
        List<Integer> result = new ArrayList<Integer>();
        if (root == null)
            return result;// 特例情况
        Stack<TreeNode> t = new Stack<>();
        t.push(root);
        while (!t.isEmpty()) {
            TreeNode node = t.pop();
            result.add(node.val);
            if (node.right != null)// 由于是栈操作,先放右孩子,再放左孩子
                t.push(node.right);
            if (node.left != null)
                t.push(node.left);
        }
        return result;
    }

}

后序遍历(左右根)

145. 二叉树的后序遍历 - 力扣(LeetCode)

后序遍历就是让前序遍历(根左右)的左右调换为(根右左),然后反转结果数组即可

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        if (root == null)
            return result;
        Stack<TreeNode> t = new Stack<>();
        t.push(root);// 先将根节点放进栈中
        while (!t.isEmpty()) {
            TreeNode tr = t.pop();
            result.add(tr.val);
            if (tr.left != null)
                t.push(tr.left);
            if (tr.right != null)
                t.push(tr.right);
        }
        Collections.reverse(result);
        return result;
    }
}

中序遍历(左根右)

中序遍历和前序遍历不同,因为前序遍历的根左右,每次遍历到根后,直接将根的值放进结果数组了,但是对于中序遍历,需要一直找到最左的孩子,然后才开始往结果数组里面填数。所以指针和结果数组不是同步执行的。那么在栈内可以放每次遍历的左孩子,并由cur指针指向这个左孩子,当cur为空的时候,没有左孩子了,那么就可以开始往结果数组里面填数了,填的就是让栈里面弹出一个元素的值,此时的cur指向这个元素,然后让cur去遍历它的右子树。右子树也是一样的,也是要遍历它的左孩子。

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        // 左中右的遍历会导致遍历和处理的数字不一样,所以使用指针来访问
        List<Integer> result = new ArrayList<>();
        if (root == null)
            return result;
        // 栈里面放节点
        Stack<TreeNode> t = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !t.isEmpty()) {
            if (cur != null) {
                t.push(cur);
                cur = cur.left;
            } else {
                // 当前节点为空必须弹出一个栈里的元素
                cur = t.pop();
                result.add(cur.val);
                cur = cur.right;
            }
        }
        return result;
    }
}
  • 18
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉树的非递归遍历常用的方法有两种,一种是使用栈,一种是使用 Morris 遍历。下面我将分别给出两种方法的 Python 代码实现。 ## 栈实现非递归遍历 ```python class TreeNode: def __init__(self, val): self.val = val self.left = None self.right = None def preorderTraversal(root): if not root: return [] stack, res = [root], [] while stack: node = stack.pop() res.append(node.val) if node.right: stack.append(node.right) if node.left: stack.append(node.left) return res def inorderTraversal(root): if not root: return [] stack, res = [], [] while stack or root: if root: stack.append(root) root = root.left else: node = stack.pop() res.append(node.val) root = node.right return res def postorderTraversal(root): if not root: return [] stack, res = [root], [] while stack: node = stack.pop() res.append(node.val) if node.left: stack.append(node.left) if node.right: stack.append(node.right) return res[::-1] ``` 上述代码实现了二叉树的前序遍历、中序遍历和后序遍历,分别用 `preorderTraversal`、`inorderTraversal` 和 `postorderTraversal` 表示。这些函数的实现都是基于栈的非递归遍历方法,使用栈保存待访问的节点。在遍历时先将根节点入栈,然后循环执行以下操作: - 弹出栈顶节点,记录其值; - 将栈顶节点的右子节点(如果有)入栈; - 将栈顶节点的左子节点(如果有)入栈。 由于栈的特性,上述循环会先访问左子树,然后是右子树,符合二叉树遍历的先左后右的规则。针对后序遍历,需要将结果数组反转。 ## Morris 遍历实现非递归遍历 ```python class TreeNode: def __init__(self, val): self.val = val self.left = None self.right = None def inorderTraversal(root): if not root: return [] cur, res = root, [] while cur: if not cur.left: res.append(cur.val) cur = cur.right else: pre = cur.left while pre.right and pre.right is not cur: pre = pre.right if not pre.right: pre.right = cur cur = cur.left else: pre.right = None res.append(cur.val) cur = cur.right return res ``` 上述代码实现了二叉树的中序遍历,使用 Morris 遍历方法。Morris 遍历方法基于线索二叉树的思想,利用空闲的指针保存访问过的节点,从而避免使用栈或者递归占用额外的空间。具体实现方法如下: - 如果当前节点的左子节点为空,记录当前节点的值,然后将当前节点指向其右子节点; - 如果当前节点的左子节点不为空,找到其左子树的最右节点,记为 `pre`; - 如果 `pre` 的右指针为空,将其右指针指向当前节点,然后将当前节点指向其左子节点; - 如果 `pre` 的右指针不为空,说明当前节点的左子树已经访问完毕,记录当前节点的值,然后将 `pre` 的右指针置为空,将当前节点指向其右子节点。 由于 Morris 遍历方法只使用了常数级别的空间,因此在空间复杂度上优于基于栈的遍历方法。但是,由于需要修改树的结构,因此在时间复杂度上可能会劣于基于栈的遍历方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值