CS61B:Hello, 树先生!(二):树先生变二叉树到二猪家看厂去啦~

122 阅读4分钟

1:万树基于二叉树!

二猪(红黑树、B树、B+树等集合):你说,你有三棵子树,也是树,四棵子树,也是树,要不你干脆就别超过2棵子树,做个二叉树,给我看树厂去得了......

4c3da9d63fc417a5f1dc27b7bb349335.jpeg

我看程序员,谁敢不给咱面子!!!!

言归正传,万树基于二叉树,确实夸张了点,你也不知道那群计算机科学家哪天又给你整出点骚结构出来。但是可以肯定的是,大部分程序员,这辈子能打上交道的树,基本都是基于二叉树!

每个节点最多有两棵子树的树,就是二叉树!树结构,通常情况下,不会存储相同的元素,即每个节点都是唯一的!

2:要创造,先遍历啦!

在谈如何用代码构造一棵二叉树前,我们不妨先从如何遍历一棵二叉树开始入手。

二叉树的遍历,主要是三种:

  • 先序遍历
  • 中序遍历
  • 后序遍历

所谓的序,就是根。所以可以这样理解:

  • 先序遍历:整棵树的根是第一位,即 树根 -> 左子树 -> 右子树
  • 中序遍历:整棵树的根是在中间位置,即 左子树 -> 树根 -> 右子树
  • 后序遍历:整棵树的根在最后一位,即 左子树 -> 右子树 -> 树根

所以整个遍历的过程,就是拆子树的过程。
以下图为例,学习如何根据遍历要求拆树:

binaryTree2.png

先序遍历拆树法:

之前提到,先序遍历的顺序是: 根,左,右

左子树右子树
A左子树B右子树C
B左子树D右子树E(只有一个元素)
C左子树G右子树H

好了,可以不必继续拆下去了,再拆下去就变单节点了!
OK,开始遍历!从下往上!

C—— 左子树G按照根左右的顺序得到:G,I,J
C—— 右子树H按照根左右的顺序得到:H,NULL,K。NULL可以忽略,得到:H,K

B—— 左子树D = D,F
B—— 右子树E = E

组合:

左子树右子树
AB,D,F,EC,G,I,J,H,K

最后先序遍历的结果就是:A,B,D,F,E,C,G,I,J,H,K;

中序遍历拆树法:

左子树右子树
左子树BA右子树C
左子树DB右子树E(只有一个元素)
左子树GC右子树H

这里就不多展开来说了,读者可以根据先序遍历的拆树法,套一套,这里我只给出最后结果供大家对比:
F,D,B,E,A,I,G,J,C,H,K

后序遍历拆树法:

左子树右子树
左子树B右子树CA
左子树D右子树EB
左子树G右子树HC

结果:
F,D,E,B,I,J,G,K,H,C,A

3:开始创建一棵二叉树!

我们在上述遍历二叉树的时候,不难发现,我们是通过一步步的拆树,把整棵树拆成一棵棵子树,来去遍历再重新组装结果的。

所以我们创建一棵二叉树,也不妨从一棵棵小树苗种起,慢慢嫁接起来形成一棵大树!

定义二叉树:

public class BinaryTree<T> {

    //这里只存储了双亲节点的数据域,也可以存储双亲节点的引用
    //即: private BinaryTree<T> parent;
    private T parent;
    private T data;

    private BinaryTree<T> leftTree;

    private BinaryTree<T> rightTree;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public BinaryTree<T> getLeftTree() {
        return leftTree;
    }

    public void setLeftTree(BinaryTree<T> leftTree) {
        this.leftTree = leftTree;
    }

    public BinaryTree<T> getRightTree() {
        return rightTree;
    }

    public void setRightTree(BinaryTree<T> rightTree) {
        this.rightTree = rightTree;
    }

    public void setParent(T parent) {
        this.parent = parent;
    }

    public T getParent() {
        return this.parent;
    }
}

创建二叉树:

public class BinaryTreeLauncher {

    public static void main(String[] args) {

        //create Root
        BinaryTree<String> root = new BinaryTree<>();
        root.setData("A");
        root.setParent(null);
        root.setLeftTree(null);
        root.setRightTree(null);


        //create the whole left tree;
        BinaryTree<String> leftTreeB = new BinaryTree<>();
        leftTreeB.setData("B");
        leftTreeB.setLeftTree(null);
        leftTreeB.setRightTree(null);

        BinaryTree<String> leftTreeD = new BinaryTree<>();
        leftTreeD.setData("D");
        leftTreeD.setRightTree(null);
        leftTreeD.setLeftTree(null);

        BinaryTree<String> leftTreeF = new BinaryTree<>();
        leftTreeF.setData("F");
        leftTreeF.setLeftTree(null);
        leftTreeF.setRightTree(null);

        BinaryTree<String> rightTreeEofLeft = new BinaryTree<>();
        rightTreeEofLeft.setData("E");
        rightTreeEofLeft.setLeftTree(null);
        rightTreeEofLeft.setRightTree(null);


        //composing the whole left tree
        leftTreeF.setParent(leftTreeD.getData());
        leftTreeD.setLeftTree(leftTreeF);

        leftTreeD.setParent(leftTreeB.getData());
        leftTreeB.setLeftTree(leftTreeD);

        rightTreeEofLeft.setParent(leftTreeB.getData());
        leftTreeB.setRightTree(rightTreeEofLeft);

        leftTreeB.setParent(root.getData());
        root.setLeftTree(leftTreeB);

        //Create the whole right tree
        BinaryTree<String> rightTreeC = new BinaryTree<>();
        rightTreeC.setData("C");

        BinaryTree<String> leftTreeGofRight = new BinaryTree<>();
        leftTreeGofRight.setData("G");

        BinaryTree<String> leftTreeIofRight = new BinaryTree<>();
        leftTreeIofRight.setData("I");

        BinaryTree<String> rightTreeJ = new BinaryTree<>();
        rightTreeJ.setData("J");

        BinaryTree<String> rightTreeH = new BinaryTree<>();
        rightTreeH.setData("H");

        BinaryTree<String> rightTreeK = new BinaryTree<>();
        rightTreeK.setData("K");

        //Let's compose the whole right tree;
        leftTreeIofRight.setParent(leftTreeGofRight.getData());
        leftTreeGofRight.setLeftTree(leftTreeIofRight);

        rightTreeJ.setParent(leftTreeGofRight.getData());
        leftTreeGofRight.setRightTree(rightTreeJ);

        leftTreeGofRight.setParent(rightTreeC.getData());
        rightTreeC.setLeftTree(leftTreeGofRight);

        rightTreeK.setParent(rightTreeH.getData());
        rightTreeH.setRightTree(rightTreeK);

        rightTreeH.setParent(rightTreeC.getData());
        rightTreeC.setRightTree(rightTreeH);

        rightTreeC.setParent(root.getData());
        root.setRightTree(rightTreeC);

        System.out.println(root);
    }
}

到这里,你就种了一棵像本章图里的那样的二叉树了!

4:Found anything 因垂丝汀??

仔细观察我们创建这棵二叉树的流程,有没有发现,我们把每一个节点,都当成了一棵只有一个元素的小树,最后一层层的向上挂载,直到根节点?

A,B,C,D,E...K。在一开始,是一片孤立的元素,且不重复。

这完全就是我们之前提到的一群并查集

而挂载过程中,比如子树F挂到D下,其实就是并查集 connect(T D, T F)算法的一个变种!

有兴趣的,不妨改一下这个算法(参考并查集章节贴出来的代码,CV一下就可以跑的),通过connect(T D, T F)算法,把二叉树嫁接起来!

5:What's the next ?

下一章节,将会介绍二叉树的操作的代码实现,比如前文提到的遍历!