震驚!居然還有人不懂二叉樹!99%的程式設計師都會了,不會就點進來吧!

我可是千機傘發表於2020-09-23

       什麼!你居然點進來了!看來你就是那百分之一的程式設計師吧。既然不懂什麼是二叉樹,那我就來給你講講。

1. 樹

       所謂二叉樹,本質上還是個樹呀,想要知道什麼是二叉樹,就要了解樹是什麼樣子的。納尼!樹是什麼樣子我怎麼可能不懂,不就是一根杆子嗎!那你就真笨的像個杆子了。

       上面這個奇妙形狀的物體就是樹!(我屮艸芔茻這是樹)
那有沒有發現樹的特點呢,樹由樹幹,樹枝組成,一條樹幹左右分佈著很多的樹枝,這就是自然界的樹。那麼二叉樹既然也是樹,也擺脫不了自然界樹的結構。
       計算機中的樹是一種抽象資料型別(ADT),用來模擬具有樹狀結構性質的資料集合。它是由n(n>0)個有限節點通過連線它們的邊組成一個具有層次關係的集合。把它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。
       看得懂嗎呆呆,通俗來說,計算機中的樹就是自然界中的樹倒過來,魯智深倒拔垂楊柳知道吧,就是他創造了計算機中的樹(滑稽)
      什麼你還看不懂,你比呆呆還呆呆嗎!看不懂我就給你畫張圖
這張圖生動形象了吧。
       但是這只是自然界中的樹的形狀,那計算機的樹長什麼樣子呢?

(請不要吐槽畫工)
①、節點:上圖的圓圈,比如A,B,C等都是表示節點。節點一般代表一些實體,在java物件導向程式設計中,節點一般代表物件。

  ②、邊:連線節點的線稱為邊,邊表示節點的關聯關係。一般從一個節點到另一個節點的唯一方法就是沿著一條順著有邊的道路前進。在Java當中通常表示引用。

  樹有很多種,向上面的一個節點有多餘兩個的子節點的樹,稱為多路樹,而每個節點最多隻能有兩個子節點的一種形式稱為二叉樹,這也是我們講解的重點。   

  2.樹的常用術語

  

  ①、路徑:順著節點的邊從一個節點走到另一個節點,所經過的節點的順序排列就稱為“路徑”。
  舉個例子:從A到H的路徑路過A,B,D,H四個節點。這就被稱為一個路徑  

  ②、:樹頂端的節點稱為根。一棵樹只有一個根。

  ③、父節點:若一個節點含有子節點,則這個節點稱為其子節點的父節點;B是D的父節點。
  
  簡單的來說,就是一個樹枝上又分叉了兩根樹枝,那麼這個樹枝就是那兩個樹枝的父親。

  ④、子節點:一個節點含有的子樹的根節點稱為該節點的子節點;D是B的子節點。   

  ⑤、兄弟節點:具有相同父節點的節點互稱為兄弟節點;比如上圖的D和E就互稱為兄弟節點。

  ⑥、葉節點:沒有子節點的節點稱為葉節點,也叫葉子節點,比如上圖的H、E、F、G都是葉子節點。
  
  意思就是,一個樹枝沒有分叉,那就長葉子了,長了葉子的樹枝就是葉子樹枝,也就是葉子節點

  ⑦、子樹:每個節點都可以作為子樹的根,它和它所有的子節點、子節點的子節點等都包含在子樹中。
  
  聽說過柳樹嗎,隨便拔個樹枝插土裡都能生根長成樹。子樹就是柳樹的樹枝又生的樹。

  ⑧、節點的層次:從根開始定義,根為第一層,根的子節點為第二層,以此類推。

  ⑨、深度:對於任意節點n,n的深度為從根到n的唯一路徑長,根的深度為0;
  
  通俗易懂的說就是樹的任意一個樹枝到地面的高度

  ⑩、高度:對於任意節點n,n的高度為從n到一片樹葉的最長路徑長,所有樹葉的高度為0;

3.二叉樹的結構

       二叉樹也是樹,自然和樹的結構大同小異,光從名字來說,二叉樹就是一個樹枝分兩個叉的樹。所以說二叉樹就是一個節點只能有兩個子節點的樹。現在應該都知道二叉樹是什麼了,那二叉樹是怎麼工作的呢?它查詢一個節點,插入一個新節點,以及刪除一個節點,遍歷樹等工作效率如何,下面我們來一一介紹。
       既然是二叉樹,那肯定要分左邊的樹枝和右邊的樹枝咯,那我給他寫兩個樹枝。

private String rightNode;//右邊的樹枝
private String leftNode;//左邊的樹枝

那麼樹枝有了,這個樹要做到,查詢,插入的方法我們也要給他寫出來

public Tree {
/*
 *key:節點引數 
 */
    //查詢節點
    public String find(Object key);
    //插入新節點
    public boolean insert(Object key);

4.二叉樹排序

       所謂排序就是一個值按一定順序排列,那二叉樹排序的方法又是什麼呢?
       這邊有七個數,分別是3,5,2,6,4,1,7。那麼按照我們正常從大到小排序,那就是1,2,3,4,5,6,7。但是在我們二叉樹中,得有一個根節點,那麼我們把這個陣列第一個數當成根,所以根就是3,比根小的數,就放在根的左邊,比根大的數就放在根的右邊。那麼我們繼續向下排序,第二個數是5,比3大 那就放在了根的右邊,接下來看2比3小,那就放在3的左邊,以此類推。最後的結果看下圖:
在這裡插入圖片描述

這時候我們來鞏固一下剛剛我們講的幾個知識點,這個二叉樹的根就是3,父節點有3,2,5,6。葉節點那就是1,4,7。
現在來看看怎麼用Java程式碼來實現這個過程。

import java.util.List;
import java.util.ArrayList;

public class NodeDemo {
    public static void main(String args[]){
        //定義一堆數字
        int[] numbers = {6,5,8,2,6,6,9};
        //宣告並例項化Node物件
        Node roots = new Node();
        //用foreach的方式把numbers的每一個值都以add方法新增到二叉樹的節點上
        for(int number : numbers){
            roots.add(number);        //呼叫新增方法
        }
        //輸出二叉樹每一個節點上的值
        System.out.println(roots.values());
    }
}

class Node {
    private Node leftNode;                //定義一個左節點
    private Node rightNode;                //定義一個右節點
    private Object value;                //定義節點當前值

    //定義二叉樹的add方法,其新增方式按上面所說新增,形參value是從numbers陣列傳遞過來的值
    public void add(Object value){
        //判斷當前節點是否有值,如果沒有則把numbers傳過來的值賦給當前節點
        if(this.value == null){
            this.value = value;
        }else{    
            //如果傳過來的值小於或等於當前節點的值則例項化一個Node物件給改節點的左節點,並且左節點呼叫add方法
            if((Integer)value <= (Integer)this.value){
                if(this.leftNode == null){
                    this.leftNode = new Node();
                }
                this.leftNode.add(value);
            }else{
                //如果傳過來的值大於當前節點的值則例項化一個Node物件給改節點的右節點,並且右節點呼叫add方法
                if(this.rightNode == null){
                    this.rightNode = new Node();
                }
                this.rightNode.add(value);
            }
        }
    }

4.查詢節點匹配值

  1. 首先給出一個值A,A會先和根比較大小。
  2. 比根大的就和右邊的子節點比。
  3. 比根小就和左邊的子節點比
  4. 找到匹配值則返回true,沒有則返回flas
    下面看程式碼
//查詢節點
//對照上面的main方法
public boolean find(int key) {
    private int findnumber = 10;
    while(key != null){
        if(findnumber > key){//當前值比查詢值大,搜尋左子樹
            findnumber = key.leftNode;
        }else if(findnumber < key){//當前值比查詢值小,搜尋右子樹
            findnumber = key.rightNode;
        }else{
            return findnumber;
        }
    }
    return null;//遍歷完整個樹沒找到,返回null
}

5.遍歷二叉樹

遍歷樹是根據一種特定的順序訪問樹的每一個節點。比較常用的有前序遍歷,中序遍歷和後序遍歷。而二叉搜尋樹最常用的是中序遍歷。

  ①、中序遍歷:左子樹——》根節點——》右子樹

  ②、前序遍歷:根節點——》左子樹——》右子樹

  ③、後序遍歷:左子樹——》右子樹——》根節點
  在這裡插入圖片描述

//中序遍歷
public void infixOrder(Node current){
    if(current != null){
        infixOrder(current.leftChild);
        System.out.print(current.data+" ");
        infixOrder(current.rightChild);
    }
}

//前序遍歷
public void preOrder(Node current){
    if(current != null){
        System.out.print(current.data+" ");
        preOrder(current.leftChild);
        preOrder(current.rightChild);
    }
}

//後序遍歷
public void postOrder(Node current){
    if(current != null){
        postOrder(current.leftChild);
        postOrder(current.rightChild);
        System.out.print(current.data+" ");
    }
}

6.為什麼要用二叉樹

對於一般的二叉搜尋樹(Binary Search Tree),其期望高度(即為一棵平衡樹時)為log2n,其各操作的時間複雜度(O(log2n))同時也由此而決定。但是,在某些極端的情況下(如在插入的序列是有序的時),二叉搜尋樹將退化成近似鏈或鏈,此時,其操作的時間複雜度將退化成線性的,即O(n)。我們可以通過隨機化建立二叉搜尋樹來儘量的避免這種情況,但是在進行了多次的操作之後,由於在刪除時,我們總是選擇將待刪除節點的後繼代替它本身,這樣就會造成總是右邊的節點數目減少,以至於樹向左偏沉。這同時也會造成樹的平衡性受到破壞,提高它的操作的時間複雜度。
查詢速度和二叉樹的高度有關,高度越高,查詢越慢,成反比關係,當二叉樹是有序節點,就會退化為一條連結串列結構,那麼查詢一個資料,就要去遍歷連結串列,那麼效率大大降低,所有也就有了平衡二叉樹。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章