數據結構之:樹結構

一、樹的概念

1、定義

我們先來看看樹的形式化定義:
算法的集合樹(Tree)是由一個或多個結點組成的有限集合T,其中有一個特定的稱爲根的結點;其餘結點可分爲(m≥0)個互不相交的有限集T1,T2,T3,…,Tm,每一個集合本身又是一棵樹,且稱爲根的子樹。

按照自己的理解,我們可以認爲樹其實是一種抽象的數據結構,數據之間的關係就像是樹的形狀一樣如下圖所示:從A(根節點出發),可以散發子節點(B、C、D),子節點又可以散發子節點,直到散發到葉子節點(E、F、G、H、I、J)結束。

在這裏插入圖片描述

2、樹的專用術語

在這裏插入圖片描述

1、根節點
節點的最頂層我們稱之爲樹的根節點,如下圖所示:A就是這棵樹的根節點
2、雙親節點
有兩個子節點的父節點我們稱之爲雙親節點(A、B、C)就是雙親節點
3、路徑
訪問目標節點經過的路徑,如訪問E節點,那麼該路徑爲:A-B-E
4、節點的度
節點的子節點,例如A的度是2,因爲它有兩個子節點
5、節點的權
節點的值我們稱之爲權,假設在F點中的值是5,那麼我們稱節點F的權爲5
6、葉子節點:
葉子節點,我們可以理解爲樹結構最後的分支(其實就是葉子)即沒有子節點的節點,我們稱爲葉子節點,例如:E、F、G就是葉子節點

在這裏插入圖片描述

二、二叉樹

1、概述

二叉樹是樹結構的一種,只是二叉樹有一些額外的要求,如下圖所示就是一個二叉樹的結構。顧名思義,二叉樹的節點肯定是最多分二個分支,所以二叉樹概念如下:
1、任何一個節點的子節點最大節點數爲2。
2、二叉樹的節點分爲左子節點和右子節點。(例如:F的左節點爲C,右節點爲E)
在這裏插入圖片描述

2、二叉樹遍歷

二叉樹的遍歷方式有三種:前序遍歷、中序遍歷、後序遍歷。在這裏我們所說的遍歷方式都是相對與根節點的位置來實現的。下面我們根據下圖來介紹這三種遍歷方式。
在這裏插入圖片描述

2.1、前序遍歷

前序遍歷其實就是將我們的父節點放在前面,所以遍歷的順序是:父節點、左節點、右節點。
所以上圖的遍歷順序就是:FC(AD)E(HG)

2.2、中序遍歷

中序遍歷就是將我們的父節點放在中間,遍歷順序爲:左節點、父節點、右節點
上圖的遍歷順序爲:(ACD) F (HEG)

2.3、後序遍歷

同理,後序遍歷就是將我們的父節點放在最後,遍歷順序變成:左節點、右節點、父節點
上圖遍歷順序爲: (ADC) (HGE) F

3、滿二叉樹與完全二叉樹

在我們的二叉樹結構中,設計到兩種特殊的二叉樹:滿二叉樹、完全二叉樹。因這兩種數據結構規律都是可控的,可計算的,後面的很多二叉樹都會依賴這兩種數據結構。

3.1、滿二叉樹

滿二叉樹:1、所有的葉子節點都在同一層 ;2、節點的總數爲2n-1。像下圖所示就是一個滿二叉樹,最後一層都是葉子節點,且節點總數爲23-1 = 7。

在這裏插入圖片描述

3.2 完全二叉樹

完全二叉樹:所有葉子節點都在最後一層,或者倒數第二層、並且最後一層的葉子節點左連續,倒數第二層的葉子節點右連續
意思就是說我們的葉子節點如果是在最後一層的話,那麼最後一層的葉子節點的左節點必須是連續的;如果在倒數第二層存在葉子節點,那麼第二層的葉子節點必須右連續。像下圖就是完全二叉樹:


像下圖最後一層左節點不連續的,就是非完全二叉樹:

4、二叉樹的代碼實現

1、鏈表結構實現

package tree;

/**
 * 二叉樹對象
 */
public class BinaryTree<T> {
    // 用於存放節點的權
    private T value;
    // 左節點
    private BinaryTree<T> leftNode;
    // 右節點
    private BinaryTree<T> rightNode;

    public BinaryTree(T value) {
        this.value = value;
    }

    public BinaryTree<T> getLeftNode() {
        return leftNode;
    }

    public void setLeftNode(BinaryTree<T> leftNode) {
        this.leftNode = leftNode;
    }

    public BinaryTree<T> getRightNode() {
        return rightNode;
    }

    public void setRightNode(BinaryTree<T> rightNode) {
        this.rightNode = rightNode;
    }

    /**
     * 前序遍歷
     */
    public void frontShow() {
        if (this == null) {
            return;
        }
        //打印根節點
        System.out.println(this.value);
        if (this.leftNode != null) {
            this.leftNode.frontShow();
        }
        if (this.rightNode != null) {
            this.rightNode.frontShow();
        }
    }

    /**
     * 中序遍歷
     */
    public void middelShow() {
        if (this == null) {
            return;
        }
        if (this.leftNode != null) {
            this.leftNode.middelShow();
        }
        //打印根節點
        System.out.println(this.value);
        if (this.rightNode != null) {
            this.rightNode.middelShow();
        }

    }

    /**
     * 後續遍歷
     */
    public void afterShow() {
        if (this == null) {
            return;
        }
        if (this.leftNode != null) {
            this.leftNode.afterShow();
        }
        if (this.rightNode != null) {
            this.rightNode.afterShow();
        }
        //打印根節點
        System.out.println(this.value);

    }

    /**
     * 前序查找
     *
     * @param value
     */
    public BinaryTree frontSearch(T value) {
        BinaryTree treeNode = null;
        if (this.value == value) {
            return this;
        }
        if (this.leftNode != null) {
            treeNode = this.leftNode.frontSearch(value);
        }
        if (treeNode != null) {
            return treeNode;
        }
        if (this.rightNode != null) {
            treeNode = this.rightNode.frontSearch(value);
        }
        return treeNode;
    }

    /**
     * 前序查找
     *
     * @param value
     */
    public BinaryTree middleSearch(T value) {
        BinaryTree treeNode = null;
        if (this.leftNode != null) {
            treeNode = this.leftNode.frontSearch(value);
        }
        if (treeNode != null) {
            return treeNode;
        }
        if (this.value == value) {
            return this;
        }
        if (this.rightNode != null) {
            treeNode = this.rightNode.frontSearch(value);
        }
        return treeNode;

    }

    /**
     * 前序查找
     *
     * @param value
     */
    public BinaryTree afterSearch(T value) {
        BinaryTree treeNode = null;
        if (this.leftNode != null) {
            treeNode = this.leftNode.frontSearch(value);
        }
        if (treeNode != null) {
            return treeNode;
        }
        if (this.rightNode != null) {
            treeNode = this.rightNode.frontSearch(value);
        }
        if (treeNode != null) {
            return treeNode;
        }
        if (this.value == value) {
            return this;
        }
        return treeNode;
    }
    
}

2、順序存儲結構實現

順序存儲我們可以用數組來表示二叉樹,如圖所示:
在這裏插入圖片描述
順序存儲的二叉樹必須是一顆完全二叉樹的形態,對於不是完全二叉樹轉數組時,需要在數組中補null佔位,如下圖所示:
在這裏插入圖片描述
所以順序存儲的二叉樹,我們一般只考慮完全二叉樹。接下來我們來介紹一下順序存儲的性質:
1、第N個元素的左子節點:2N+1
2、第N個元素的右子節點:2
N+2
3、第N個元素的父節點: (n-1)/2

package tree;

/**
* 順序存儲的二叉樹
*/
public class ArrayBinaryTree<T> {
   private T[] value;

   public ArrayBinaryTree(T[] value) {
       this.value = value;
   }
   public void frontShow(){
       frontShow(0);
   }
   public void middelShow(){
       middleShow(0);
   }
   public void afterShow(){
       afterShow(0);
   }

   private void frontShow(int start){
       if (value == null|| value.length==0){
           return;
       }
       if (start<value.length){
           //先遍歷當前節點的內容
           System.out.println(value[start]);
       }

       if (2*start+1<value.length){
           //遍歷左節點的內容
          frontShow(2*start+1);
       }
       if (2*start+2<value.length){
           //遍歷右節點的內容
           frontShow(2*start+2);
       }

   }
   private void middleShow(int start){
       if (value == null|| value.length==0){
           return;
       }

       if (2*start+1<value.length){
           //遍歷左節點的內容
           frontShow(2*start+1);
       }
       if (start<value.length){
           //先遍歷當前節點的內容
           System.out.println(value[start]);
       }
       if (2*start+2<value.length){
           //遍歷右節點的內容
           frontShow(2*start+2);
       }

   }
   private void afterShow(int start){
       if (value == null|| value.length==0){
           return;
       }

       if (2*start+1<value.length){
           //遍歷左節點的內容
           frontShow(2*start+1);
       }
       if (2*start+2<value.length){
           //遍歷右節點的內容
           frontShow(2*start+2);
       }
       if (start<value.length){
           //先遍歷當前節點的內容
           System.out.println(value[start]);
       }

   }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章