CS61B:Hello, 树先生!(一)

200 阅读3分钟

1:从一棵普通的树开始!

现在,如果你去谷歌树的实现,他们会教你这样定义:

public class Tree <T> {
    private T item;
    private Tree left;
    private Tree right;
    .......
}

然后得到如下的示例图

6e1a3e9e82d3313a26da6ad88f84c63.png

But, Fck it! It's a tree too !!!!

7bce3cb2f2ba555709b517b510b660b.png

一开始灌输二叉树的概念(最多只有2个子节点),或许是件好事,不过我喜欢从最愚蠢的方式开始,这样可以更加透彻的了解什么是树!

所以,这是我对一棵普通的树的定义:

public class NormalTree <T> {  
  
private NormalTree<T> parent;  
private T item;  
  
private List<NormalTree<T>> descendant ;  
  
public NormalTree(NormalTree<T> parent,T item, List<NormalTree<T>> descendant) {  
this.parent = parent;  
this.item = item;  
this.descendant = Objects.requireNonNullElseGet(descendant, () -> new ArrayList<>(1));  
}  
  
public NormalTree() {  
this.parent = null;  
this.item = null;  
this.descendant = new ArrayList<>(1);  
}  
  
public NormalTree<T> getParent() {  
return parent;  
}  
  
public void setParent(NormalTree<T> parent) {  
this.parent = parent;  
}  
  
public T getItem() {  
return item;  
}  
  
public void setItem(T item) {  
this.item = item;  
}  
  
public List<NormalTree<T>> getDescendant() {  
return descendant;  
}  
  
public void setDescendant(List<NormalTree<T>> descendant) {  
this.descendant = descendant;  
}  
}

有了树的定义,就可以种一棵和图二一样的树了:

public class TreeLauncher {  
  
public static void main(String[] args) {  
//从ROOT节点开始  
NormalTree<Integer> normalTree = new NormalTree<>(null,1,null);  
  
//让我们为root节点添加子节点  
List<NormalTree<Integer>> descendantOfRoot = new ArrayList<>();  
normalTree.setDescendant(descendantOfRoot);  
  
NormalTree<Integer> descendant1 = new NormalTree<>();  
descendant1.setItem(2);  
descendant1.setParent(normalTree);  
  
NormalTree<Integer> descendant2 = new NormalTree<>();  
descendant2.setItem(4);  
descendant2.setParent(normalTree);  
  
descendantOfRoot.add(descendant1);  
descendantOfRoot.add(descendant2);  
  
//此时root节点的儿子节点,都创建好了,但是还没完,儿子还有儿子呢!!!  
NormalTree<Integer> descendant1OfItem2 = new NormalTree<>();  
descendant1OfItem2.setItem(5);  
descendant1OfItem2.setParent(descendant1);  
  
  
NormalTree<Integer> descendant2OfItem2 = new NormalTree<>();  
descendant2OfItem2.setItem(6);  
descendant2OfItem2.setParent(descendant1);  
  
NormalTree<Integer> descendant3OfItem2 = new NormalTree<>();  
descendant3OfItem2.setItem(7);  
descendant3OfItem2.setParent(descendant1);  
  
descendant2.getDescendant().add(descendant1OfItem2);  
descendant2.getDescendant().add(descendant2OfItem2);  
descendant2.getDescendant().add(descendant3OfItem2);  
  
//Assert == 6  
System.out.println(normalTree.getDescendant().get(1).getDescendant().get(1).getItem());  
}  
}

2:树的重要属性有哪些?

  • leave —— 叶节点:没有子节点的节点,就是叶子节点

  • edge —— 边:边就是两个节点之间的那条直线了。N个元素的树,edge = N-1

  • degree —— 度:即节点的子节点数,其中最大的那个,就是树的度!

  • depth —— 节点的深度、树的深度:从root开始数,而且是从1开始(程序员的惯性从0开始),到目标节点经过的节点数,就是节点的深度。而树的深度,则是root到叶子节点经过的节点数的最大值。

  • height —— 节点的高度、树的高度:深度是从root往下数,高度则是从叶子从下往上数,也是从1开始。

以图二举个例子:

  • Leave有4个,分别是5,6,7,4。因为他们都没有子节点了。
  • edge有5条。
  • root节点1的degree是2,因为root只有2和4两个子节点,5,6,7是儿子的孙子节点,不算!
  • 2节点的degree是3,因为它有5,6,7三个儿子节点。
  • 4的degree是0,因为它没有儿子节点。
  • 整棵树的degree是3,因为degree的最大值就是3。
  • root节点1的depth是1。
  • 值为6的节点的depth是3。
  • 整棵树的depth是3。
  • root节点1的height是3,因为从最远的叶节点5,6,7往上到1,经过的最大节点数是3。
  • 4的高度是1。
  • 2的高度是2。
  • 整棵树的高度是3。

3:森林的概念!

森林是由一棵棵不相交的树组成的。那么问题来了,下面两棵树,是森林么?

1689598788658.png

当然不是,因为这两棵树,可以通过数值2连通起来。

这像不像是我们在之前提到的并查集概念?简直就是一样好么!!!!!

所以,如果要合并两棵树,你现在应该知道怎么做了吧?

4:What's next ?

好吧,说完了这种普通的树,下一篇,我们正式开始谈谈二叉树!

BTW,你们有没有发现树目前最大的缺点?

没错,就是太吉尔占内存了!不过万幸的是,对于应用层来说,内存如今并不值钱!