二叉樹與堆

jun發表於2022-04-16

樹的定義

樹是一種資料結構,樹結構只有一個根節點,除根節點外,其餘節點被分成M(M>0) 個互不相交的集合T1,T2,T3,......,Tm. 其中每一個集合Ti(1 < i < m)又是一顆與樹結構類似的子樹。每個子樹的根節點有且只有一個前驅,可以有0個或多個後繼。因此,樹是遞迴定義的。
如圖是一顆樹結構:
image-20220404092746094
由於樹結構只有一個前驅,所以樹結構不能出現交叉,如下不是一棵樹結構
image-20220404093339573
這種結構稱為樹結構是因為它像生活中倒著的樹:
R-C

樹的相關概念

image-20220404094339763
節點的度:一個節點含有子樹的個數成為節點的度;(上圖,B節點的度為3)
葉節點或終端節點:度為0的節點稱為葉節點或終端節點;(一般稱葉節點,上圖H節點是葉節點)
非終端節點或分支節點:度不為零的節點;(上圖的非葉節點都是)
雙親結點或父節點:若一個節點含有一個子節點,則這個節點稱為其子節點的父節點;(樹中父節點只有一個,子節點可以有很多個;上圖A節點是B節點的父節點)
孩子節點或子節點:一個節點的子樹的根節點稱為該點的子節點;(上圖K節點是F節點的子節點)
兄弟節點:具有相同父節點的子節點互稱兄弟節點;(上圖H,I,J互稱兄弟節點)
樹的度:一棵樹中度最大的節點的度稱為樹的度;(上圖樹的度為6)
節點的層次:從根節點開始,根節點為第一層,其子樹一層為第二層,以此類推;(有些地方根節點從第0層開始計數)
樹的高度或深度:樹中節點的最大層數;(根節點從1開始計數,上圖樹的高度為4)
堂兄弟節點:雙親在同一層的節點互稱堂兄弟;(上圖J和K互稱堂兄弟)
節點的祖先:從根到該節點所經分支上的所有節點;(父節點也是祖先節點)
子孫節點:以某節點為根的子樹中任一節點都稱該節點的子孫節點;
森林:由m(m > 0)棵互不相交的樹的集合稱為森林;(一棵樹也可以構成森林)

樹的節點個數 m(m ≥ 0),當節點數=0時,樹為空樹

樹的表示

樹的表示由很多種方法,如雙親表示法,孩子表示法,雙親孩子表示法和孩子兄弟表示法等,這裡介紹比較優秀的孩子兄弟表示法

typedef int DateType;
struct Node
{
    struct Node* first_child;//指向第一個孩子節點
    struct Node* next_brother;//指向下一個兄弟節點
    DateType date;
}

孩子兄弟表示法是一種簡潔的表示法,表示方法為:
image-20220404111856854
如左邊那棵樹,它的孩子兄弟表示法如右邊表示。

樹的應用

樹在實際中的應用有作為計算機系統的檔案系統的目錄:
20200321143013262
如上圖為Linux系統的檔案目錄,它也是樹結構。

二叉樹

二叉樹的定義

二叉樹也是一種樹,它的每個節點的度m(0≤m≤2),二叉樹的度不能超過2。二叉樹可以為空。

二叉樹特點

二叉樹有左右之分,不能顛倒,因此二叉樹是有序樹。
滿二叉樹:一個二叉樹每一層節點都到達最大值,那麼這棵樹就是滿二叉樹。(若一顆二叉樹高度為h,節點數為 2h - 1 則這棵二叉樹是滿二叉樹)
image-20220409201040099
完全二叉樹:若二叉樹有n個節點,給每個節點按行從根節點依次連續編號,完全二叉樹編號與滿二叉樹1到n個節點編號完全對應,則稱此樹為完全二叉樹。(滿二叉樹是特殊的完全二叉樹)
image-20220409202339698

二叉樹性質

1.若規定根節點層數為1,則一顆非空二叉樹第h層最多有2(h-1)個節點。
2.若規定根節點層數為1,則深度為h的二叉樹的最大節點數是2h - 1。
3.對任何一顆二叉樹,如果度為0的節點個數為n0,度為2的節點個數為n2,則有n0 = n2 + 1.
4.若規定根節點的層數為1,具有n個節點的滿二叉樹的深度,h = log₂(n + 1)。
5.對於具有n個節點的完全二叉樹,如果按照從上至下從左至右的陣列順序對所有節點從0開始編號,則對於序號為i的節點有:

1.若i > 0,i位置節點的雙親序號:(i - 1) / 2; i = 0,i為根節點編號,無雙親節點
2.若2i+1<n,2i+1為編號i節點的左孩子序號;2i+1≥n,則i節點無左孩子
3.若2i+2<n,2i+2為編號i節點的右孩子序號;2i+2≥n,則i節點無右孩子

二叉樹的儲存結構

二叉樹一般有兩種儲存結構,一種順序結構,一種鏈式結構

  1. 順序儲存
    順序結構儲存就是使用陣列儲存,一般使用陣列只適合表示完全二叉樹,因為不是完全二叉樹會有空間浪費。一般只有堆用陣列來儲存。二叉樹順序儲存在物理上是一個陣列,在邏輯上是一棵二叉樹。
    image-20220410151815593

  2. 鏈式儲存
    二叉樹的鏈式儲存結構是指用連結串列來表示一棵二叉樹,用鏈來指示元素的邏輯關係。通常方法是連結串列中每個節點由三個域組成,資料域和左右指標域,左右指標用來給出該節點左孩子和右孩子所在的鏈節點的儲存地址。鏈式結構分為二叉鏈和三叉鏈。
    Lesson5--二叉樹

    typedef int BTDataType;
    //二叉鏈
    struct BinaryTreeNode
    {
        struct BinaryTreeNode *lChild;
        struct BinaryTreeNode *rChild;
        BTDataType Data;
    }
    
    //三叉鏈
    struct BinaryTreeNode
    {
        struct BinaryTreeNode *parent;
        struct BinaryTreeNode *lChild;
        struct BinaryTreeNode *rChild;
        BTDataType Data;
    }
    

這裡所說的堆是一種資料結構,它不同於堆疊區所謂的堆,上面說了,堆是一種二叉樹形式,它儲存形式是順序儲存,邏輯上又是二叉樹形式。堆有大堆和小堆。大堆就是任何節點它的父節點資料都大於孩子節點。小堆反之。
堆在儲存結構上看與陣列沒有區別,但它在邏輯上是一棵所有節點的資料都大於(大堆)或小於(小堆)孩子節點的特殊二叉樹。將一個堆從零建起來需要用到之前提到的一些二叉樹的父節點與孩子節點之間的關係,建起一個小堆,每向堆尾插入資料,都需要向上調整一次;若父節點資料大於孩子節點,則需要交換父節點與子節點的資料,若不大於則停止調整,這個過程就是向上調整。
image-20220416204819095
將上面的陣列建為一個小堆,如:
image-20220416204932934
將資料一個一個插入堆中,向上調整,最終建好的堆為:
image-20220416205726249

建堆需要實現的函式有:

//Heap.h:
typedef int DataType;
typedef struct Heap
{
	size_t size;
	DataType* a;
	size_t capacity;
}heap;

void HeapInit(heap* hp);//初始化堆
void HeapPush(heap* hp, DataType x);//入堆
void AdjustUp(heap* hp, size_t child);//向上調整
void BuySpace(heap* hp);//申請空間
void Swap(DataType* x, DataType* y);//交換值
void HeapPrint(heap* hp);//列印堆
void AdjustDown(heap* hp, size_t parent);//向上調整
void HeapInsert(heap* hp, DataType x, size_t pos);//向堆任意位置插入值

上述函式有些不是必須,如AdjustDown(),若只是建堆,沒有其他作用則此函式用處不大,該函式主要用在堆排序中。
建堆的主要函式實現:

void AdjustUp(heap* hp,size_t child)
{
	assert(hp);
	size_t parent = (child - 1) / 2;
	while (child != 0)
	{
		if (hp->a[child] < hp->a[parent])
			Swap(hp->a + child, hp->a + parent);
		child = parent;
		parent = (child - 1) / 2;
	}
}

相關文章