美文网首页
二叉树遍历

二叉树遍历

作者: stoneyang94 | 来源:发表于2018-07-08 22:58 被阅读0次

看了左程云老师的算法课,记录学习过程,整理思路和形成系统认识。

题目(算法课第五课)

二叉树遍历。
二叉树定义,和二叉树的前中后三种遍历方式的递归和非递归的实现。

二叉树

定义

每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒。

二叉树实现

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

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

二叉树性质

1、非空二叉树的第n层上至多有2(n-1)个元素
2、深度为h的二叉树至多有2h-1个结点
3、对任何一棵二叉树T,如果其终端结点数(即叶子结点数)为n0,度为2的结点数为n2,则n0 = n2 + 1

特殊的二叉树

满二叉树

  • 定义
    所有终端都在同一层次,且非终端结点的度数为2。

  • 性质
    在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1。

完全二叉树

  • 定义
    除了最大的层次即成为一颗满二叉树且层次最大那层所有的结点均向左靠齐,即集中在左面的位置上,不能有空位置。

  • 性质
    对于完全二叉树,设一个结点为i则其父节点为i/2,2i为左子节点,2i+1为右子节点。(理解这个对堆的理解很重要)

遍历

  • 定义
    (按规则)访问二叉树的所有结点且仅访问一次。
  • 分类
    按照根节点位置的不同分为前序遍历,中序遍历,后序遍历。
  1. 前序遍历preOrder
    根节点->左子树->右子树(根节点在前面)
  2. 中序遍历inOrder
    左子树->根节点->右子树(根节点在中间)
  3. 后序遍历posOrder
    左子树->右子树->根节点(根节点在后边)

代码实现

递归

  • 递归的前中后序遍历,逻辑是一样的,区别在于打印节点的时机。
递归过节点
  • 递归结束的关键是,理解叶子节点的后继节点是null节点,代码if (head == null) return;导致一个节点有可能被“经过三次”,这三次中:
  1. 第一次经过时打印节点,就是前序遍历
  2. 第二次经过时打印节点,就是中序遍历
  3. 第三次经过时打印节点,就是后序遍历

递归前序

    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 + " ");
    }

非递归

非递归,借助栈结构,实现类似递归的返回过程。

非递归前序

总体入栈顺序:根 -> 右 -> 左

  • 每次栈不空,就弹出栈顶,并且压入其右节点 (如果不空),然后压入左节点(如果不空)
非递归前序
    public static void preOrderUnRecur(Node head) {
        System.out.print("pre-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();//借助栈结构
            stack.add(head);
            while (!stack.isEmpty()) { //栈不空
                Node cur = stack.pop();//每次while都弹出栈顶
                System.out.print(cur.value + " ");
                if (cur.right != null) {//当前节点有右就 先入右(所以右后出),注意是`if`
                    stack.push(cur.right);
                }
                if (cur.left != null) {
                    stack.push(cur.left);
                }
            }
        }
        System.out.println();
    }

非递归中序

  1. 当前节点的整个“左边界”全部入栈
  2. 否则(遇到空节点) 栈顶出栈,指针变成 弹出元素的右孩子
非递归中序
    public static void inOrderUnRecur(Node head) {
        System.out.print("in-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            while (!stack.isEmpty() || head != null) {
                if (head != null) {  //当前节点不空,则把的整个“左边界”全部入栈
                    stack.push(head);
                    head = head.left;
                } else {   // 此时当前节点为null了,但是栈里不空;弹出/打印/右窜
                    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<Node> s1 = new Stack<Node>();
            Stack<Node> s2 = new Stack<Node>();  //辅助栈;
            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
            /          \
        2              3
      /    \          /    \
    4      5        6       7

遍历结果
前:1 - 2 - 4 - 5 - 3 - 6 - 7
中:4 - 2 - 5 - 1 - 6 - 3 - 7
后:4 - 5 - 2 - 6 - 7 - 3 - 1

代码:

public class 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 + " ");
    }

    public static void preOrderUnRecur(Node head) {
        System.out.print("pre-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            stack.add(head);
            while (!stack.isEmpty()) {
                Node cur = stack.pop();
                System.out.print(cur.value + " ");
                if (cur.right != null) {
                    stack.push(cur.right);
                }
                if (cur.left != null) {
                    stack.push(cur.left);
                }
            }
        }
        System.out.println();
    }

    public static void inOrderUnRecur(Node head) {
        System.out.print("in-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            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<Node> s1 = new Stack<Node>();
            Stack<Node> s2 = new Stack<Node>();
            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();
    }

    public static void main(String[] args) {
        Node head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.left.right = new Node(5);
        head.right.left = new Node(6);
        head.right.right = new Node(7);
        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();

        System.out.println("============unrecursive=============");
        preOrderUnRecur(head);
        inOrderUnRecur(head);
        posOrderUnRecur(head);
    }
}
输出

无论递归非递归,本质都是使用了栈,由于二叉树整体是向下的,当遍历到某一结点时需要回去时,就是刚刚好逆序的栈。

相关文章

  • 二叉树 基础操作

    二叉树的使用 二叉树结构 先序创建二叉树 DFS 先序遍历二叉树 中序遍历二叉树 后序遍历二叉树 BFS 层次遍历...

  • 关于二叉树的算法题

    前序遍历中序遍历后序遍历判断是否是平衡二叉树判断是否是对称二叉树判断二叉树高度按照层遍历二叉树判断二叉树宽度

  • 二叉树遍历

    二叉树 二叉树的存储结构 前序遍历 中序遍历 后序遍历 遍历代码 反转二叉树 深入学习二叉树 二叉树-你必须要懂!...

  • 二叉树操作

    树节点 逐行顺序解析二叉树 前序遍历二叉树 中序遍历二叉树 后序遍历二叉树 删除指定数值的节点 前序遍历顺序存储的...

  • 数据结构与算法之二叉树遍历(七)

    目录 前序遍历中序遍历后序遍历层序遍历遍历方式的选择条件根据遍历结果重构二叉树翻转二叉树计算二叉树的高度判断一棵树...

  • 二叉树三种遍历Swift代码实现

    二叉树的三种遍历 二叉树 前序遍历 中序遍历 后序遍历 另外 不得不说,得到二叉树的前序遍历和中序遍历的结果或者后...

  • 二叉树的遍历

    二叉树的遍历 二叉树遍历 分为前序遍历、中序遍历和后序遍历。 前序遍历 (DLR) 先访问根节点,然后前序遍历左子...

  • 前端二叉树

    (一)构造二叉树 (二)中序遍历 (三)前序遍历 前序遍历可以复制二叉树,效率比重新构造二叉树高 (四)后序遍历 ...

  • 数据结构:树的实现和遍历(c++)

    (一)二叉树的遍历——递归实现 二叉树常见的遍历方式分为前序遍历、中序遍历和后序遍历。 1 前序遍历 前序遍历也叫...

  • leetcode 144 145 94

    二叉树遍历 前序遍历 中序遍历 后序遍历

网友评论

      本文标题:二叉树遍历

      本文链接:https://www.haomeiwen.com/subject/qathuftx.html