实现一个二叉树层级遍历_算法总结:左神class5—二叉树递归和非递归实现遍历(后序用一个栈彩蛋)...

二叉树类型的题目为常考题型

1、能够结合队列、栈、链表、字符串等很多数据结构。

2、需要掌握图的基本遍历方式,比如BFS和DFS。

3、需要掌握递归函数的使用,并自己设计出递归过程。

4、与实际工作结合紧密。

面试中,二叉树节点类型仅包括:数据项、左孩子、右孩子。
工程上的二叉树节点类型,往往多一条指向父节点的指针。
一般默认面试中的二叉树节点结构不包含指向父节点的指针,除非特别说明。

实现二叉树的先序、中序、后序遍历,包括递归方式和非递归方式

要清楚前序、中序、后序传统的递归遍历其实都是一次遍历访问节点三次,当在哪一次访问做动作就出现了三种遍历方式。而morris遍历(进阶)只访问每个节点两次,且利用了每个叶子节点的空指针。

(依次访问节点)

5d47f4885b0e8dc379543ab3db328c39.png

打印时机放在

第一次来到节点,先序

第二次来到节点,中序

第三次来到节点,后序

import java.util.Stack;public class Code_01_PreInPosTraversal {    public static class Node {        public int value;        public Node left;        public Node right;        public Node(int data) {            this.value = data;        }    }    //递归版先中后序遍历    public static void preOrderRecur(Node head) {        if (head == null) {            return;        }        System.out.print(head.value + " ");// 头节点        preOrderRecur(head.left);        preOrderRecur(head.right);    }    public static void inOrderRecur(Node head) {        if (head == null) {            return;        }        inOrderRecur(head.left);        System.out.print(head.value + " ");        inOrderRecur(head.right);    }    public static void posOrderRecur(Node head) {        if (head == null) {            return;        }        posOrderRecur(head.left);        posOrderRecur(head.right);        System.out.print(head.value + " ");    }

用非递归的方式实现二叉树的先序遍历,(回到节点两次):
1. 申请一个新的栈, 记为 stack。然后将头节点 head 压入 stack 中。
2. 从 stack 中弹出栈顶节点, 记为 head, 然后打印head 节点的值, 再将节点 head的右孩子节点(不为空的话) 先压入 stack 中, 最后将head 的左孩子节点(不为空的话) 压入 stack 中。
3. 不断重复步骤 2, 直到 stack 为空, 全部过程结束。

举例:

52d6e5c806e0f68cfcf90c0d632cf0c0.png

377046a073b609aa41cc30d791a95b3e.png

中序遍历具体过程:(自己压栈只能压入node类型,需要精细设计结构,比起递归丢失很多信息)

1、申请一个新的栈,记为stack,申请一个变量cur ,初始时令cur等于头节点。

2、当前结点不为空时,先把head节点压入栈中,对head节点为头的整棵子树来说,只要依次把整棵树的左边界压入栈中,即不断令head=cur.left,然后重复步骤2。当前结点指针向它左结点移动,一压压一条边。

3、不断重复步骤2,当前结点为空、栈不为空时,此时从stack中弹出一个节点,记为node。打印node的值,并让head=node.right当前结点指针向右移动,然后继续重复步骤2。

4、当stack为空并且head为空时,整个过程结束。

后序遍历用两个栈。由前面的先序遍历,中左右(先压右再压左),改为中右左, 不打印,然后放入辅助栈中逆序,得到左右中,即后序遍历

具体体过程如下∶

1、申请一个栈,记为s1,然后将头节点压入s1中。 

2、从s1中弹出的节点记为cur,然后先把cur的左孩子压入s1中,然后把cur1的右孩子压入s1中。

3、在整个过程中,每一个从s1中弹出的节点都放进第二个栈s2中。

4、不断重复步骤2和步骤3,直到s1为空,过程停止。 

5、从s2中依次弹出节点并打印,打印的顺序就是后序遍历的顺序了。

 //非递归版先序遍历     // 往栈中压入根结点    // 弹出栈中一个结点并打印     // 压入刚弹出结点的右结点和左结点     // 弹出栈中一个结点并打印    public static void preOrderUnRecur(Node head) {        System.out.print("pre-order: ");        if (head != null) {            Stack 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();    }//  中序遍历具体过程:    public static void inOrderUnRecur(Node head) {        System.out.print("in-order: ");        if (head != null) {            Stack 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();    }       // 后序遍历用了两个栈       public static void posOrderUnRecur(Node head) {        System.out.print("pos-order: ");        if (head != null) {            Stack s1 = new Stack();            Stack 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();    }

用一个栈进行后序遍历

1、申请一个栈,记为stack,将头节点压入stack ,同时设置两个变量h和c。在整个流程中,h代表最近一次弹出并打印的节点,c代表当前stack的栈顶节点,初始时令h为头节点,c为null.

2、每次令c等于当前stack的栈顶节点,但是不从stack中弹出节点,此时分以下三种情况。

(1)如果c的左孩子不为空,并且h不等于c的左孩子,也不等于c的右孩子,则把c的左孩子压入stack中。等于的时候就说明左孩子或者右孩子打印完毕

(2)如果情况1不成立,并且c的右孩子不为空并且h不等于c的右孩子,则把c的右孩子压入stack中。

(3)如果情况1和情况2都不成立,左右都打印完毕,那么从stack中弹出c并打印,然后令h等于c.

3、一直重复步骤2,直到stack为空,过程停止。

举例说明:

601582f38416ac768e1d990f11007190.png

【步骤】节点1压入stack中,准备变量h=头节点1和c=null,命中步骤2情况1,压入节点2,栈顶结点为2,命中步骤2的情况1,压入结点4,此时栈顶结点为4,命中步骤2的情况3,将结点4从栈中弹出并打印,h变为结点4,此时栈顶结点为2,命中情况2,压入结点5,栈顶节点变为5,命中步骤2的情况3,弹出结点5并打印,h变成结点5,栈顶节点变为2,命中步骤2的情况3,弹出结点2打印,h变为结点2,栈顶结点为结点1,命中步骤2的情况2,压入结点3,栈顶结点为结点3,命中步骤2的情况1,压入结点6;栈顶结点为结点6,命中步骤2的情况3,弹出结点6打印;h变成结点6,栈顶节点变为3,命中步骤2的情况2,压入结点7;栈顶结点为结点7,命中步骤2的情况3,弹出结点7打印,栈顶结点变为3,命中步骤2的情况3,弹出结点3打印,h变为3栈顶变为1,命中步骤2的情况3,弹出结点1打印。

public static void posorderUnRecur2(Node h) {        System.out.print(pos-order: ");         if(h != null) {          stack stack = new stack();          stack.push(h);          Node c= null;        while ( !stack.isEmpty(){           c = stack.peek();            if(c.ieft != null &&_h != c.left && h != c.right){                stack.push(c.left);                }else if (c.right != nu11 && h != c.right) {                  stack.push(c.right);                }else {                   system.out.print(stack.pop().value + " " );                            h= c;             system.out.println();}

不管是递归方法还是非递归方法,遍历整棵树的时间复杂度都是O(N),N为二叉树的节点数,额外空间复杂度为O(L),L为二叉树的层数。

测试主函数:

 public static void main(String[] args) {        Node head = new Node(5);        head.left = new Node(3);        head.right = new Node(8);        head.left.left = new Node(2);        head.left.right = new Node(4);        head.left.left.left = new Node(1);        head.right.left = new Node(7);        head.right.left.left = new Node(6);        head.right.right = new Node(10);        head.right.right.left = new Node(9);        head.right.right.right = new Node(11);        // recursive        System.out.println("==============recursive==============");        System.out.print("pre-order: ");        preOrderRecur(head);        System.out.println();        System.out.print("in-order: ");        inOrderRecur(head);        System.out.println();        System.out.print("pos-order: ");        posOrderRecur(head);        System.out.println();        // unrecursive        System.out.println("============unrecursive=============");        preOrderUnRecur(head);        inOrderUnRecur(head);        posOrderUnRecur(head);    }}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值