二叉树的先序、中序和后序遍历(递归与非递归方式)

目录

二叉树的基本结构

什么是序

二叉树遍历的递归版本

递归先序(中 -> 左 -> 右)

递归中序(左 -> 中 -> 右)

递归后序(右 -> 左 ->中)

二叉树遍历的非递归版本

非递归先序

非递归中序(精华)

非递归后序

扫描二维码关注公众号,回复: 11283273 查看本文章

二叉树的基本结构

class Node{
	int data;
	Node left;
	Node right;
	
	Node(int data){
		this.data = data;
	}
}

每个node拥有左和右两个子节点并包含自己的数据值。

什么是序

每个节点遍历过程中都会被访问三次,分别是: 遍历到当前节点(第一次)-> 该节点左子树 -> 返回当前节点(第二次) -> 该节点右子树 -> 返回当前节点(第三次).

如果遍历过程中指针第一次指向节点就打印出该节点的值,此为先序遍历。类似的,如果指针第二次指向该节点时打印出节点的值为中序遍历,第三次访问时打印出值为后序遍历。

二叉树遍历的递归版本

递归先序(中 -> 左 -> 右)

在递归过程中,将打印语句放在函数的开始部分,为先序遍历,先序遍历在第一次访问该节点时也就是访问其左子树前,打印当前节点的值.

public static void preOrderPrint(Node head){
    if (head == null){
        return;
    }
    System.out.println(head.data);
    preOrderPrint(head.left);
    preOrderPrint(head.right);
}

递归中序(左 -> 中 -> 右)

将打印语句放在遍历左子树和遍历右子树中间为中序遍历.访问到该节点时未打印节点值,在访问完左子树之后返回到当前节点,也就是第二次访问该节点时,打印节点值.

public static void inOrderPrint(Node head){
    if (head == null){
        return;
    }
    inOrderPrint(head.left);
    System.out.println(head.data);
    inOrderPrint(head.right);
}

递归后序(右 -> 左 ->中)

依此类推,打印节点放在遍历左子树和右子树的后边为后续遍历.顺序为访问完左子树和又子树之后.

public static void posOrderPrint(Node head){
    if (head == null){
        return;
    }
    posOrderPrint(head.left);
    posOrderPrint(head.right);
    System.out.println(head.data);
}

二叉树遍历的非递归版本

非递归先序

1-拿到一下需要遍历的根节点之后,建立一个栈,将根节点压栈.

2-判断栈中是否为空,不为空则弹出站顶内容并打印节点值

3-刚刚弹栈的节点按照先右后左的顺序将子节点压栈.

4-while(!stack.isEmpty)重复2/3步骤.

    public static void preOrderUnRecur(Node head){
        if (head == null)
            return;
        Stack<Node> stack = new Stack<>();
        stack.push(head);
        while (!stack.isEmpty()){
            Node node = stack.pop();
            System.out.println(node.data);
            if (node.right != null)
                stack.push(node.right);
            if (node.left != null)
                stack.push(node.left);
        }
    }

递归函数是一个函数栈,非递归的方式只是自己创建了一个栈里边压如我们想要的东西.

为什么用栈结构:

二叉树的结构是从根节点到子节点可寻址,但子节点到根节点不可寻址.我们需要一个结构能让我们反向回到父节点同时又可以保证遵循从上到下访问过来的顺序,栈结构天然合适.

非递归中序(精华)

二叉树是由n条左边界构成的。如下图中,

三个节点的二叉树可以理解为由左边界1 -> 2和节点3组成的。因为3没有子节点,所以自成左边界。也就是说这棵二叉树由两条左边界构成。

如果将树分为多个左边界后,从以root节点为头的左边界开始倒序遍历,遍历到每个节点都执行两个动作

1-打印当前节点的值

2-检查该节点是否有右节点,如果有,就把以这个右节点作为头结点的左边界压栈倒序打印

迭代执行1-2两步即可。

具体执行步骤如下

0-创建一个栈

1-持有头节点后,只要当前节点不为null,就压当前节点的左节点进栈。

2-当前节点为null,证明整个左边界全部压栈了。将节点依次弹出打印。

3-打印时,需要检查当前节点的右节点是不是null,如果不为null则该右节点是另一条左边界的头结点,迭代执行1、2、3步。

    public static void inOrderUnRecur(Node head){
        System.out.println("inOrderUnRecur");
        Node cur = head;
        if (cur == null)
            return;
        Stack<Node> stack = new Stack<>();
        //如果栈里还有节点就不可以停止
        //如果栈里没有节点了但是当前节点还有可能存在右节点(不为null)就不可以停止
        while (!stack.isEmpty() || cur != null){
            //从头到最左全部压栈
            if (cur != null){
                stack.push(cur);
                cur = cur.left;
            //如果压完了,弹出最后一个,检查是否有右节点,右就继续压.
            }else {
                cur = stack.pop();
                System.out.print(head.data + " ");
                cur = cur.right;
            }
        }
    }

非递归后序

后序遍历的输出顺序是左 > 右 > 中.先序遍历的顺序是中 > 左 > 右,我们可以先根据先序遍历的过程实现按照 中 > 右 > 左的顺序将节点全部压入另一个栈,然后遍历弹出新栈的全部数据达到左 > 右 > 中的顺序要求.

    public static void posOrderUnRecur(Node head){
        if (head == null)
            return;
        Stack<Node> stack1 = new Stack<>();
        Stack<Node> stack2 = new Stack<>();
        stack1.push(head);
        while (!stack1.isEmpty()){
            head = stack1.pop();
            stack2.push(head);
            if (head.left != null)
                stack1.push(head.left);
            if (head.right != null)
                stack1.push(head.right);
        }
        while (!stack2.isEmpty()){
            System.out.print(stack2.pop().data + " ");
        }
    }

猜你喜欢

转载自blog.csdn.net/weixin_39445556/article/details/105334840