Java工程师面试1000题224-使用递归和非递归实现二叉树的前、中、后序遍历

224、使用递归和非递归实现二叉树的前、中、后序遍历

使用递归来实现二叉树的前、中、后序遍历比较简单,直接给出代码,我们重点讨论非递归的实现。

class Node {
    public int value;
    public Node left;
    public Node right;

    public Node() {
    }

    public Node(int value) {
        this.value = value;
    }
}

public class Solution {

    //递归实现前序遍历
    public void preOrderRecur(Node head){
        if (head == null)
            return;
        System.out.println(head.value + " ");
        preOrderRecur(head.left);
        preOrderRecur(head.right);
    }
    
    //递归实现中序遍历
    public void inOrderRecur(Node head){
        if (head == null)
            return;
        inOrderRecur(head.left);
        System.out.println(head.value + " ");
        inOrderRecur(head.right);
    }

    //递归实现后续遍历
    public void posOrderRecur(Node head){
        if (head == null)
            return;
        posOrderRecur(head.left);
        posOrderRecur(head.right);
        System.out.println(head.value + " ");
    }
}

凡是可以使用递归可以解决的问题都可以使用非递归的方法来实现。这是因为递归方法无非就是利用函数栈来保存信息,如果用自己申请的数据结构来代替函数栈,也可以实现相同的功能。

用非递归的方法实现二叉树的先序遍历,具体过程如下:

1、申请一个新的栈,记为stack。然后将头结点head压入stack中;

2、从stack中弹出栈顶节点,记为cur,然后打印cur节点的值,再将节点cur的右孩子节点(如果不为空)先压入到stack中,最后将cur的左孩子节点(如果不为空)压入stack中。

3、不断重复步骤2,直到stack为空,过程全部结束。

下面举例说明整个过程,假如存在一颗二叉树如图所示:

  1. 节点1先入栈,然后弹出并打印。接下来把节点3压入栈,再把节点2压入栈,此时stack从顶到底依次为2、3;
  2. 节点2弹出并打印,把节点5压入栈,再把节点4压入栈,此时栈中从顶到底元素依次为4、5、3;
  3. 节点4弹出并打印,由于节点4没有孩子节点可以压入栈,所以此时栈中元素从顶到底依次为5、3;
  4. 节点5弹出并打印,由于节点5没有孩子节点可以压入栈,所以此时栈中元素仅有3;
  5. 节点3弹出并打印,先把节点7压入栈,再把节点6压入栈,此时栈中元素从顶到底依次为6、7;
  6. 节点6弹出并打印,由于节点6没有孩子节点可以压入栈,所以此时栈中元素仅有7;
  7. 节点7弹出并打印,由于节点7没有孩子节点可以压入栈,此时stack中为空,过程停止。

整个过程请参考如下代码:

    //不用递归实现前序遍历
    public void preOrderUnRecur(Node head){
        System.out.print("前序遍历:");
        if (head != null){
            Stack<Node> stack = new Stack<>();
            stack.add(head);
            while (!stack.isEmpty()){
                head = stack.pop();
                System.out.print(head.value + " ");
                if (head.right != null){
                    stack.push(head.right);
                }
                if (head.left != null){
                    stack.push(head.left);
                }
            }
        }
        System.out.println();
    }

用非递归的方式来实现二叉树的中序遍历,具体过程如下:

  1. 申请一个新的栈,记为stack。初始时,令变量cur = head;
  2. 先把cur节点压入栈中,对以cur节点为头结点的整颗子树来说,依次把左边界压入栈中,即不停的令cur= cur.left;然后重复此步骤;
  3. 不断重复步骤2,直到发现cur为空,此时从stack中弹出一个节点,记为node。打印node的值,并且让cur = node.right,然后继续重复步骤2;
  4. 当stack为空且cur为空时,整个过程停止。

还是以上图的二叉树为例子来说明整个过程:

  1. 初始时cur为节点1,将节点1压入栈,令cur= cur.left,此时cur变为节点2;
  2. cur为节点2,将节点2压入栈中,令cur = cur.left,即此时cur变为节点4;
  3. cur为节点4,将节点4压入栈中,令cur = cur.left,此时cur变为了null,此时栈中元素从顶到底依次为4、2、1;
  4. cur为null,从栈中弹出节点4(node)并打印,并令cur = node.right,cur仍然为null,此时stack中从顶到底依次为2、1;
  5. cur为null,从栈中弹出节点2(node)并打印,并令cur = node.right,此时cur变为节点5,此时栈中元素只有1;
  6. cur为节点5,将节点5压入stack中,并令cur = cur.left,此时cur变为null,栈中元素有5、1;
  7. cur为null,从栈中弹出节点5(node)并打印,并令cur = node.right,即cur仍为null,此时栈中元素为1;
  8. cur为null,从栈中弹出节点1(node)并打印,并令cur = node.right,即cur变为节点3,此时栈中为空;
  9. cur为节点3,将节点3压入栈中,令cur = cur.left,此时cur变为节点6,此时栈中仅有元素3;
  10. cur为节点6,将节点6压入栈中,令cur = cur.left,此时cur变为null,此时栈中有元素6、3;
  11. cur为null,弹出节点6(node)并打印,令cur = node.right,cur仍为null,此时栈中元素仅有3;
  12. cur为null,弹出节点3(node)并打印,令cur = node.right,cur变为节点7,此时栈中为空;
  13. cur为节点7,将节点7压入栈,令cur = cur.left,此时cur变为null,此时栈中仅有元素7;
  14. cur为null,弹出节点7(node)并打印,令cur = node.right,cur仍然为null,此时栈stack也为null。
  15. cur和stack同时为null,整个过程终止。

代码如下:

    //不用递归实现中序遍历
    public void inOrderUnrecur(Node head){
        System.out.println("in-Order:");
        if (head != null){
            Stack<Node> stack = new Stack<>();
            while (!stack.isEmpty() || head != null){
                if (head != null){
                    stack.push(head);
                    head = head.left;
                }else {
                    head = stack.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
        }
        System.out.println();
    }

用非递归方式实现二叉树后序遍历有点麻烦。下面介绍使用两个栈来实现后序遍历的过程,具体步骤如下:

  1. 申请一个栈记为s1,然后将头结点压入s1中;
  2. 从s1中弹出的节点记为cur,然后依次将cur的左孩子和右孩子压入s1中;
  3. 在整个过程中,每一个从s1中弹出的节点都放进s2中;
  4. 不断重复步骤2和步骤3,直到s1为空,过程停止;
  5. 然后从s2中依次弹出节点并打印,打印的顺序就是后序遍历的顺序。

下面还是使用上面的图示二叉树来演示整个过程:

  1. 申请两个栈s1和s2,将头结点1压入s1中,然后再从s1中弹出节点1,再把节点1压入s2中,再把节点2和节点3一次压入s1,此时s1从栈顶到栈底元素为3、2;s2从栈顶到栈底只有1;
  2. 从s1中弹出节点3,节点3放入s2中,然后将节点3的两个孩子节点6和节点7一次压入s1,此时s1从栈顶到栈底元素依次为7、6、2;s2中为3、1;
  3. 从s1中弹出节点7,节点7放入s2中,由于节点7没有孩子,不需要往s1中添加任何元素,此时s1中保存6、2;s2中保存7、3、1;
  4. 从s1中弹出节点6,节点6放入s2中,由于节点6没有孩子,不需要往s1中添加任何元素,此时s1中保存2,s2中保存6、7、3、1;
  5. 从s1中弹出节点2,节点2放入s2中,然后将节点2的两个孩子节点4和节点5一次压入s1中,此时s1从栈顶到栈底元素为5、4;s2中存放的元素为2、6、7、3、1;
  6. 从s1中弹出节点5,节点5放入s2中,节点5无孩子节点,不需要往s1中添加任何元素,此时s1中保存4;s2中保存5、2、6、7、3、1;
  7. 从s1中弹出节点4,节点4放入s2中,节点4无孩子节点,不需要往s1中添加任何节点,此时s1中为空,s2中保存的元素从栈顶到栈底依次是4、5、2、6、7、3、1;
  8. 过程结束,依次弹出并打印s2中的节点即可。

通过如上过程我们知道,每棵子树的头结点都是先从s1中弹出,然后再把该节点的两个孩子节点按照先左再右的顺序依次压入s1,那么从s1弹出的顺序就是先右后左,所以从s1中弹出的顺序就是中、右、左。然后,s2重新收集的过程就是把s1的弹出顺序逆序,所以s2从栈顶到栈底的顺序就变成了左、右、中。

代码如下:

    //不用递归实现后续遍历
    public void posOrderUnRecur(Node head){
        System.out.println("pos-order: ");
        if (head != null){
            Stack<Node> s1 = new Stack<>();
            Stack<Node> s2 = new Stack<>();
            s1.push(head);
            while (!s1.isEmpty()){
                head = s1.pop();
                s2.push(head);
                if (head.left != null){
                    s1.push(head.left);
                }
                if (head.right != null){
                    s1.push(head.right);
                }
            }
            while (!s2.isEmpty()){
                System.out.print(s2.pop().value + " ");
            }
        }
        System.out.println();
    }

猜你喜欢

转载自blog.csdn.net/qq_21583077/article/details/89243068