递归法与迭代法实现树的遍历

本文详细介绍了如何使用递归和迭代法实现树的前序、中序和后序遍历。递归法通过调整函数调用顺序实现不同遍历,而迭代法则需显式维护一个栈来模拟递归过程。代码示例展示了两种方法的具体实现,帮助读者理解这两种遍历策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

递归法与迭代法实现树的遍历

递归法

首先树的遍历分为,前序遍历,中序遍历,后序遍历。

前序遍历: 对于当前结点,先对该结点进行操作,然后对他的左孩子进行操作,最后对他的右孩子进行操作。

中序遍历: 对于当前结点,先对该结点的左孩子进行操作,然后对该结点进行操作,最后对他的右孩子进行操作。

后序遍历: 对于当前结点,先对该结点的左结点进行操作,然后对右孩子进行操作,最后多该结点进行操作。

递归序:

其实对于树的遍历,递归法实现是比较简单的,只需要调整递归函数调用的位置即可,可是为什么递归能实现前序遍历,中序遍历和后序遍历呢?实际上是由递归时访问结点的顺序决定的。

public void recur(Node head){
    if(head == null) return;
    // 1
    recur(head.left);
    // 2
    recur(head.right);
    // 3
}

以上为递归遍历树时的通用代码,我们发现,对于当前结点head,在当前这个recur中,其实有三次都会返回到该结点,如注释1,这时当前结点为head结点;对于注释2,由于调用recur(head.left)结束,会再次返回到该结点;对于注释3,由于调用recur(head.right)结束,会再次返回到该结点,因此总共有三次会访问同一结点。

image-20211106101338970

对于该树,递归序为,1,2,4,4,4,2,5,5,5,2,1,3,6,6,6,3,7,7,7,3,1。

因此前序中序后序遍历,就是在分别在第一次第二次第三次到达该结点时进行操作,例如,前序遍历则是在1时,此时是第一次访问到该结点,因此先对该结点进行操作,就可以实现先序遍历。知道了递归序,递归实现树的三种遍历就很容易了,代码如下:

先序遍历:

public void preOrderRecur(Node head){
    if(head == null) return;
    System.out.print(head.val);
    recur(head.left);
    recur(head.right);
}

中序遍历:

public void inOrderRecur(Node head){
    if(head == null) return;
    recur(head.left);
    System.out.print(head.val);
    recur(head.right);
}

后序遍历:

public void posOrderRecur(Node head){
    if(head == null) return;
    recur(head.left);
    recur(head.right);
    System.out.print(head.val);
}

迭代法:

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

先序遍历迭代法

首先我们可以先定义一个栈,然后重复进行一下操作:

  1. 从栈中弹出当前结点
  2. 对当前结点进行处理
  3. 如果该结点有右结点或左结点,按先右后左的顺序压入栈中。
  4. 循环上述操作。
// 先序遍历!
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if (root == null){
            return res;
        }
        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        if(root != null){
            stack.push(root);
        }
        while(root != null && !stack.isEmpty() ){
            root = stack.pop();   //要注意弹出的不能为null,因此循环中需要判断stack不为空
            res.add(root.val);  // 弹出后进行操作。
            if(root.right != null){
                stack.push(root.right);
            }
            if(root.left != null){
                stack.push(root.left);
            }
        }
        return res;
    }
}

中序遍历迭代法:

  • 首先定义一个栈,将每棵子树的左边依次进栈
  • 依次弹出结点,对该结点进行操作
  • 对于弹出结点的右子树,重复上述操作。

即一直向左遍历,并且在遍历途中先将根结点和压入栈中,然后弹出左叶子结点后先判断是否其存在右结点(左结点在循环中已经判断),如果不存在则将其根结点进行弹出并输出,若存在,则继续向该右结点重复上述向左遍历的操作。

public static void inOrderUnRecur(Node root) {
    System.out.print("in-order: ");
    if (root != null) {
        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        while (!stack.isEmpty() || root != null) {
            if (root != null) {
                stack.push(root);
                root = root.left;
            } else {
                root = stack.pop();
                System.out.print(root.value + " ");
                root = root.right;
            }
        }
    }
    System.out.println();
}

后序遍历迭代法

对于后序遍历,在树的知识点与算法中给出了一种遍历方法,但是可能会比较难理解,因此这里给出第二种方法。

首先由于根节点需要最后访问,而找到它的左右结点一定要先经过根结点,因此需要两个栈来维护访问顺序。栈1为访问所有结点的中介栈,栈2为最终所有结点的收集栈。我们要通过栈1实现压入栈2的顺序为头右左,这样在栈2中打印出来的顺序则可以变为左右头,实现后序遍历。

  • 从栈1弹出当前结点,放入栈2
  • 如果有左右结点,按先左后右的方式放入栈1中。
  • 循环上述操作直至所有结点压入栈2
  • 依次对栈2的结点进行操作。
public void posOrderUnrecur(TreeNode root){
    if(root!=null){
        Deque<TreeNode> s1 = new LinkedList<TreeNode>(); // 中介栈
        Deque<TreeNode> s1 = new LinkedList<TreeNode>(); // 收集栈
        s1.push(root);
        while(!s1.isEmpty()){
            root = s1.pop();
            s2.push(root);
            if(root.left != null){
                s1.push(root.left);   // 先放左
            }
            if(root.right != null){
                s1.push(root.right);  // 后方右 
            }
        }
        while(!s2.isEmpty()){
            System.out.print(s2.pop().val);
        }
    }
}

如有错误,请大家指出,大家一起进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值