数据结构-二叉树的遍历

遍历是数据结构中的常见操作
把所有元素都访问一遍

线性数据结构的遍历比较简单

  • 正序遍历
  • 逆序遍历

根据结点访问顺序的不同,二叉树的常见遍历方式有4种

  • 前序遍历(Preorder Traversal
  • 中序遍历(Inorder Traversal
  • 后序遍历(Postorder Traversal
  • 层序遍历(Level Order Traversal

前序遍历(Preorder Traversal)

访问顺序
根节点、前序遍历左子树、前序遍历右子树
7、4、2、1、3、5、9、8、11、10、12

    /**
     * 前序遍历
     */
    public void preorderTraversal() {
        preorderTraversal(root);
    }
    
    private void preorderTraversal(Node<E> node) {
        if (node == null) return;
        System.out.println(node.element);
        preorderTraversal(node.left);
        preorderTraversal(node.right);
    }

中序遍历(Inorder Traversal)

访问顺序

  • 中序遍历左子树、根节点、中序遍历右子树
    1、2、3、4、5、7、8、9、10、11、12


  • 中序遍历右子树、根节点、中序遍历左子树
    12、11、10、9、8 、7、5、4、3、2、1

二叉搜索树的中序遍历结果是升序或者降序的

    /**
     * 中序遍历
     */
    public void inorderTraversal() {
        inorderTraversal(root);
    }
    
    private void inorderTraversal(Node<E> node) {
        if (node == null) return;
        inorderTraversal(node.left);
        System.out.println(node.element);
        inorderTraversal(node.right);
    }

后序遍历(Postorder Traversal)

后序遍历左子树、后序遍历右子树、根节点
1、3、2、5、4、8、10、12、11、9、7


    /**
     * 后序遍历
     */
    public void postorderTraversal() {
        postorderTraversal(root);
    }
    
    private void postorderTraversal(Node<E> node) {
        if (node == null) return;
        postorderTraversal(node.left);
        postorderTraversal(node.right);
        System.out.println(node.element);
    }

层序遍历(Level Order Traversal)

从上到下、从左到右依次访问每一个节点
7、4、9、2、5、8、11、1、3、10、12


实现思路:使用队列

  1. 将根节点入队
  2. 循环执行以下操作,直到队列为空
    将队头节点 A 出队,进行访问
    A 的左子节点入队
    A 的右子节点入队
    /**
     * 层序遍历
     */
    public void levelOrderTraversal(){
        if (root == null) return;
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            System.out.println(node.element);
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }

遍历接口设计

    /**
     * 前序遍历
     */
    public void preorderTraversal(Visitor<E> visitor) {
        preorderTraversal(root,visitor);
    }
    
    private void preorderTraversal(Node<E> node,Visitor<E> visitor) {
        if (node == null || visitor == null) return;
        visitor.visit(node.element);
        preorderTraversal(node.left,visitor);
        preorderTraversal(node.right,visitor);
    }
    
    /**
     * 中序遍历
     */
    public void inorderTraversal(Visitor<E> visitor) {
        inorderTraversal(root,visitor);
    }
    
    private void inorderTraversal(Node<E> node,Visitor<E> visitor) {
        if (node == null || visitor == null) return;
        inorderTraversal(node.left,visitor);
        visitor.visit(node.element);
        inorderTraversal(node.right,visitor);
    }
    
    /**
     * 后序遍历
     */
    public void postorderTraversal(Visitor<E> visitor) {
        postorderTraversal(root,visitor);
    }
    
    private void postorderTraversal(Node<E> node,Visitor<E> visitor) {
        if (node == null || visitor == null) return;
        postorderTraversal(node.left,visitor);
        postorderTraversal(node.right,visitor);
        visitor.visit(node.element);
    }
    
    /**
     * 层序遍历
     */
    public void levelOrderTraversal(Visitor<E> visitor){
        if (root == null || visitor == null) return;
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            visitor.visit(node.element);
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }
    
    public static interface Visitor<E> {
        void visit(E elemet);
    }

利用上一章节二叉搜索树调用层序遍历

 package njf;
import java.util.Comparator;

import njf.printer.BinaryTreeInfo;
import njf.printer.BinaryTrees;
import njf.BinarySearchTree.Visitor;
import njf.file.Files;

public class Main {
    static void test1() {
        Integer data[] = new Integer[] {
                7, 4, 9, 2, 5, 8, 11, 3, 12, 1
        };
        
        BinarySearchTree<Integer> bst = new BinarySearchTree<>();
        for (int i = 0; i < data.length; i++) {
            bst.add(data[i]);
        }
        BinaryTrees.println(bst);
        bst.levelOrderTraversal(new Visitor<Integer>() {
            @Override
            public void visit(Integer elemet) {
                System.out.print("_" + elemet + "_");
            }
        });
//      bst.preorderTraversal(new Visitor<Integer>() {
//          @Override
//          public void visit(Integer elemet) {
//              System.out.print("_" + elemet + "_");
//              
//          }
//      });
    }
    
    public static void main(String[] args) {
        test1();
    }
}

打印结果如下:

             ┌───7_p(null)───┐
             │               │
        ┌─4_p(7)─┐       ┌─9_p(7)─┐
        │        │       │        │
   ┌─2_p(4)─┐  5_p(4) 8_p(9)   11_p(9)─┐
   │        │                          │
1_p(2)    3_p(2)                    12_p(11)
_7__4__9__2__5__8__11__1__3__12_

遍历接口增强

/**
     * 前序遍历
     */
    public void preorderTraversal(Visitor<E> visitor) {
        if (visitor == null) return;
        preorderTraversal(root,visitor);
    }
    
    private void preorderTraversal(Node<E> node,Visitor<E> visitor) {
        if (node == null || visitor.stop) return;
        visitor.stop = visitor.visit(node.element);
        preorderTraversal(node.left,visitor);
        preorderTraversal(node.right,visitor);
        return;
    }
    
    /**
     * 中序遍历
     */
    public void inorderTraversal(Visitor<E> visitor) {
        if (visitor == null) return;
        inorderTraversal(root,visitor);
    }
    
    private void inorderTraversal(Node<E> node,Visitor<E> visitor) {
        if (node == null || visitor.stop) return;
        inorderTraversal(node.left,visitor);
        if (visitor.stop) return;
        visitor.stop = visitor.visit(node.element);
        inorderTraversal(node.right,visitor);
    }
    
    /**
     * 后序遍历
     */
    public void postorderTraversal(Visitor<E> visitor) {
        if (visitor == null) return;
        postorderTraversal(root,visitor);
    }
    
    private void postorderTraversal(Node<E> node,Visitor<E> visitor) {
        if (node == null || visitor.stop) return;
        postorderTraversal(node.left,visitor);
        postorderTraversal(node.right,visitor);
        if (visitor.stop) return;
        visitor.stop = visitor.visit(node.element);
    }
    
    /**
     * 层序遍历
     */
    public void levelOrderTraversal(Visitor<E> visitor){
        if (root == null || visitor == null) return;
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            if (visitor.visit(node.element)) return;
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }
    /**
     * java抽象类
     */
    public static abstract class Visitor<E> {
        boolean stop;
        /**
         * @return 如果返回true,就代表停止遍历
         */
        public abstract boolean visit(E elemet);
    }

利用上一章节二叉搜索树调用层序遍历

package njf;
import java.util.Comparator;

import njf.printer.BinaryTreeInfo;
import njf.printer.BinaryTrees;
import njf.BinarySearchTree.Visitor;
import njf.file.Files;

public class Main {
    static void test5() {
        Integer data[] = new Integer[] {
                7, 4, 9, 2, 1
        };
        
        BinarySearchTree<Integer> bst = new BinarySearchTree<>();
        for (int i = 0; i < data.length; i++) {
            bst.add(data[i]);
        }
        BinaryTrees.println(bst);
        
        bst.preorderTraversal(new Visitor<Integer>() {
            public boolean visit(Integer element) {
                System.out.print(element + " ");
                return element == 2 ? true : false;
            }
        });
        System.out.println();
        
        bst.inorderTraversal(new Visitor<Integer>() {
            public boolean visit(Integer element) {
                System.out.print(element + " ");
                return element == 4 ? true : false;
            }
        });
        System.out.println();
        
        bst.postorderTraversal(new Visitor<Integer>() {
            public boolean visit(Integer element) {
                System.out.print(element + " ");
                return element == 4 ? true : false;
            }
        });
        System.out.println();
        
        bst.levelOrderTraversal(new Visitor<Integer>() {
            public boolean visit(Integer element) {
                System.out.print(element + " ");
                return element == 9 ? true : false;
            }
        });
        System.out.println();
    }
    
    public static void main(String[] args) {
        test5();
    }
}

打印结果如下:

             ┌─7_p(null)─┐
             │           │
        ┌─4_p(7)       9_p(7)
        │
   ┌─2_p(4)
   │
1_p(2)
7 4 2 
1 2 4 
1 2 4 
7 4 9 

前序遍历 – 非递归

    public void preorder(Visitor<E> visitor) {
        if (visitor == null || root == null) return;
        Node<E> node = root;
        Stack<Node<E>> stack = new Stack<>();
        while (true) {
            if (node != null) {
                if (visitor.visit(node.element)) return;
                if (node.right != null) {
                    stack.push(node.right);
                }
                node = node.left;
            }else if(stack.isEmpty()){
                return; 
            }else {
                node = stack.pop();
            }
        }
    }
    public void preorder(Visitor<E> visitor) {
        if (visitor == null || root == null) return;
        Stack<Node<E>> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            Node<E> node = stack.pop();
            if (visitor.visit(node.element)) return;
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
    }

中序遍历 – 非递归

public void inorder(Visitor<E> visitor) {
        if (visitor == null || root == null) return;
        Stack<Node<E>> stack = new Stack<>();
        Node<E> node = root;
        while (true) {
            if (node != null) {
                stack.push(node);
                node = node.left;
            }else if (stack.isEmpty()) {
                return;
            }else {
                node = stack.pop();
                if (visitor.visit(node.element)) return;
                node = node.right;
            }
        }
    }

后序遍历 – 非递归

public void postorder(Visitor<E> visitor) {
        if (visitor == null || root == null) return;
        // 记录上一次弹出访问的节点
        Node<E> prev = null;
        Stack<Node<E>> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            Node<E> top = stack.peek();
            if (top.isLeaf()|| (prev != null && prev.parent == top)) {//没有子节点
                prev = stack.pop();
                if (visitor.visit(prev.element)) return;
            }else {
                if (top.right != null) {
                    stack.push(top.right);
                }
                if (top.left != null) {
                    stack.push(top.left);
                }
            }
        }
    }

练习:计算二叉树的高度

  • 递归法
public int height() {
      return height(root);
}
    
    /**
     * 计算二叉树的高度,利用递归
     */
private int height(Node<E> node) {
    if (node == null) return 0;
    return 1 + Math.max(height(node.left), height(node.right));
}
  • 层序遍历
    /**
     * 计算二叉树的高度,利用层序遍历
     */
    public int height() {
        if (root == null) return 0;
        // 树的高度
        int height = 0;
        // 存储着每一层的元素数量
        int levelSize = 1;
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            levelSize --;
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
            if (levelSize == 0) {// 意味着即将要访问下一层
                levelSize = queue.size();
                height ++;
            }
        }
        return height;
    }

练习: 判断一棵树是否为完全二叉树


◼ 如果树为空,返回 false
◼ 如果树不为空,开始层序遍历二叉树(用队列)
如果 node.left!=null,将 node.left 入队
如果 node.left==null && node.right!=null,返回 false
如果 node.right!=null,将 node.right 入队
如果 node.right==null
那么后面遍历的节点应该都为叶子节点,才是完全二叉树
否则返回 false

代码实现
public boolean isCompleteTree() {
        if (root == null) return false;
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        boolean isLeaf = false;
        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            //如果出现叶子结点,但是后面结点又不是叶子结点,false
            if (isLeaf && !(node.left == null && node.right == null)) return false;
            if (node.left != null) {
                queue.offer(node.left);
            }else if(node.right != null) {// node.left == null && node.right != null
                return false;
            }
            if(node.right != null) {
                queue.offer(node.right);
            }else {// node.right == null
                isLeaf = true;
            }
        }
        return true;
    }

利用上一章节二叉搜索树验证是否为完全二叉树

package njf;
import java.util.Comparator;

import njf.printer.BinaryTreeInfo;
import njf.printer.BinaryTrees;
import njf.BinarySearchTree.Visitor;
import njf.file.Files;

public class Main {
    static void test7() {
        Integer data[] = new Integer[] {
                7, 4, 10, 2, 5, 9, 11, 3, 1
        };
        
        BinarySearchTree<Integer> bst = new BinarySearchTree<>();
        for (int i = 0; i < data.length; i++) {
            bst.add(data[i]);
        }
        BinaryTrees.println(bst);
        System.out.println("是否为完全二叉树" + bst.isCompleteTree());
    }
    
    public static void main(String[] args) {
        test7();
    }
}

结果如下:

             ┌───7_p(null)────┐
             │                │
        ┌─4_p(7)─┐       ┌─10_p(7)─┐
        │        │       │         │
   ┌─2_p(4)─┐  5_p(4) 9_p(10)   11_p(10)
   │        │
1_p(2)    3_p(2)
是否为完全二叉树true

根据遍历结果重构二叉树

◼ 以下结果可以保证重构出唯一的一棵二叉树

  • 前序遍历 + 中序遍历
    例如:
    前序遍历:4 2 1 3 6 5
    中序遍历:1 2 3 4 5 6
  • 后序遍历 + 中序遍历

◼ 前序遍历 + 后序遍历
✓ 如果它是一棵真二叉树(Proper Binary Tree),结果是唯一的
✓ 不然结果不唯一

前驱节点(predecessor)

◼ 前驱节点:中序遍历时的前一个节点

如果是二叉搜索树,前驱节点就是前一个比它小的节点

实现思路:
◼ 如果node.left != null
举例:6、13、8

predecessor = node.left.right.right.right...

终止条件:rightnull

◼ 如果node.left == null && node.parent != null
举例:7、11、9、1

predecessor = node.parent.parent.parent...

终止条件:nodeparent 的右子树中

◼ 如果node.left == null && node.parent == null
那就没有前驱节点
举例:没有左子树的根节点

代码实现:

    /**
     * 是否为前驱结点
     */
    private Node<E> predecessor(Node<E> node) {
        if (node == null) return node;
        // 前驱节点在左子树当中(left.right.right.right....)
        Node<E> p = node.left;
        if (p != null) {
            while (p.right != null) {
                p = p.right;
            }
            return p;
        }
        // 从父结点、祖父节点中寻找前驱节点
        while (node.parent != null && node.parent.left == node) {
            node = node.parent;
        }
        // node.parent == null
        // node == node.parent.right
        return node.parent;
    }

后继节点(successor)

后继节点:中序遍历时的后一个节点

如果是二叉搜索树,后继节点就是后一个比它大的节点


◼ 如果node.right != null
举例:1、8、4

successor = node.right.left.left.left...

终止条件:leftnull

◼ 如果node.right == null && node.parent != null
举例:7、6、3、11

successor = node.parent.parent.parent...

终止条件:nodeparent 的左子树中

◼ 如果node.right == null && node.parent == null
那就没有前驱节点
举例:没有右子树的根节点

    /**
     * 是否为后继结点
     */
    private Node<E> successor(Node<E> node) {
        if (node == null) return node;
        // 后继节点在右子树当中(right.left.left.left....)
        Node<E> p = node.right;
        if (p != null) {
            while (p.left != null) {
                p = p.left;
            }
            return p;
        }
        // 从父结点、祖父节点中寻找前驱节点
        while (node.parent != null && node.parent.right == node) {
            node = node.parent;
        }
        // node.parent == null
        // node == node.parent.left
        return node.parent;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352