完全二叉樹
在完全二叉樹中,只有最下面兩層的結點的度可以小於2,最下面一層的葉子結點編號連續集中在靠左的位置上。
滿二叉樹
一棵深度為𝑘,並且有2^𝑘−1個節點的二叉樹,為滿二叉樹。
二叉樹的性質
- 在非空二叉樹的第i層上最多有個2^(𝑖−1)節點
- 深度為𝑘的二叉樹最多有2^𝑘−1個節點
- 具有n個節點的完全二叉樹的深度k=⌊log_2 (n) ⌋+1=⌈log_2 (n+1) ⌉
二叉樹的遍歷
- 先序遍歷:若二叉樹為空,則空操作返回;否則①先訪問根結點;②然后先序遍歷左子樹;③先序遍歷右子樹。
- 中序遍歷:若二叉樹為空,則空操作返回;否則①中序遍歷根結點的左子樹;②接着訪問根結點;③中序遍歷根結點的右子樹。
- 后序遍歷:若二叉樹為空,則空操作返回;否則①后序遍歷根結點的左子樹;②后序遍歷根結點的右子樹;③最后訪問根結點。
- 層次遍歷:先訪問第一層的結點,再對第二層的所有結點從左往右依次訪問,直到訪問完最后一層的所有結點。
(注意:先序、中序、后序都是遞歸實現即可,層次遍歷類似於廣度優先搜索,需要使用隊列結構)
由“先序+中序”或者“后序+中序”的遍歷結果可以得到唯一的一顆二叉樹。
- 先/后序確定當前根節點:先序序列的第一個元素為根節點,后序序列的最后一個元素為根節點;
- 中序遍歷結果確定左右子樹的節點范圍:在中序序列中找到根節點所在位置,前面的是左子樹,后面的是右子樹
struct Node///結點 { int data; int lc, rc; }node[maxn*2]; ///還要算上內部節點的開銷,所以開兩倍maxn int tot;///統計已經建立的頂點數量 int create(int len, int pre_st, int in_st) ///遞歸建樹 { if (len == 0) return -1; int me = tot++; node[me].data = pre[pre_st];///先序序列第一個元素為根結點 for (int left = 0; left < len; ++left) { if (in[in_st+left] == pre[pre_st]) { node[me].lc = create(left, pre_st + 1, in_st);///建左子樹 node[me].rc = create(len - left - 1, pre_st + left + 1, in_st + left + 1); return me; } } } vector<int>post; void postorder(int root) ///后序遍歷 { if(root!=-1) { postorder(node[root].lc); postorder(node[root].rc); post.push_back(node[root].data); } }
堆
二叉堆是完全二叉樹。二叉堆滿足二個特性:1.父結點的鍵值總是大於或等於(小於或等於)任何一個子節點的鍵值。2.每個結點的左子樹和右子樹都是一個二叉堆(都是最大堆或最小堆)。一般都用數組來表示堆,i結點的父結點下標就為(i-1)/2。它的左右子結點下標分別為2i+1和2i+2。
- 插入元素
目前數組所存元素0->n-1,新插入的元素都將插入到n位置上,然后再從(n-1)/2父結點開始從下至上調整最大堆。
void Insert_heap(int a[], int i) { for (int j = (i - 1) / 2; (j >= 0 && i != 0)&& a[i] > a[j]; i = j, j = (i - 1) / 2) Swap(a[i], a[j]); }///(i-1)/2表示父結點
- 刪除元素
對堆的刪除都是取出堆頂元素(最大、最小元素),此時先將n-1位置上的元素放到堆頂0號位置,再對0->n-2這樣的堆進行從上至下的調整。
///從i節點開始調整,n為節點總數。從0開始計算i節點的子節點為 2*i+1, 2*i+2 void AdjustDown(int a[], int i, int n) { int j, temp; temp = a[i]; j = 2 * i + 1;///左孩子 while (j < n) { if (j + 1 < n && a[j + 1] > a[j]) //在左右孩子中找最大的 j++; if (a[j] <= temp)///最大的孩子也比父節點小 break; a[i] = a[j]; //把較大的子結點往上移動,替換它的父結點 i = j; j = 2 * i + 1; } a[i] = temp; } //在最大堆中刪除數 void Delete_heap(int a[], int n) { Swap(a[0], a[n - 1]); AdjustDown(a, 0, n - 1); }
哈夫曼樹
概念:
- 擴充二叉樹:只存在度為0和2的結點組成的二叉樹;
- 結點間的路徑長度:樹中一個結點到另一個結點所包括的邊的數目;
- 樹的內路徑長度:從根到所有非葉子結點的路徑長度之和;
- 樹的外路徑長度:從根到所有葉子結點的路徑長度之和;
- 樹的路徑長度之和:從根到樹 的所有結點的路徑長度之和;
- 結點的帶權路徑長度:從根到該結點的路徑長度與該結點的權值乘積;
- 樹的帶權路徑長度WPL:樹中所有葉子結點的帶權路徑長度之和
- 定理:設I和E分別是一棵擴充二叉樹的內路徑長度和外路徑長度,n是樹中非葉子結點的數目,則E=I+2n
哈夫曼樹的特點:
- 哈夫曼樹的結點個數不能是偶數(因為擴充二叉樹只有度為0和2的結點);
- 一棵哈夫曼樹的加權路徑長度等於其中所有分支結點(不包括葉子結點)的權值之和;
- 哈夫曼樹是帶權路徑長度最短的擴充二叉樹,權值越大的結點離根結點越近
構建哈夫曼樹:
用給定的權值構建n個左右子樹都為空的二叉樹,所有結點都包含在F集合中;從F中選擇權值最小和次最小的兩棵樹作為左右子樹(規定左子樹比右子樹小),然后構建新的二叉樹,新二叉樹的權值為左右子樹權值之和,在F中刪除左右子樹權值,把新生成的結點權值加入其中;然后重復上述操作,直到F中只有一個結點即為根結點。
假設哈夫曼樹中有n個葉子結點,則哈夫曼樹中總共有2n-1個結點。實際存儲時采用一維數組存儲,0號存儲單元不使用,1->n號單元分別存儲葉子結點,n+1->2n-1單元存儲哈夫曼樹形成過程中的非葉子結點。
typedef struct { ElementType Data;///結點的數據域 int w; ///結點的權值 int parent,lchild,rchild; }HFMTNode; void CreateHFMTNode(HFMTNode Ht[],int N) { int i,j,k,lmin,rmin;///lmin和rmin為最小權值的兩個結點位置 int min1,min2;///min1和min2為最小兩個結點的權值 for(i=1;i<2N;i++)///初始化數組 Ht[i].parent=Ht[i].lchild=Ht[i].rchild[i]=-1; for(i=N+1;i<2N;i++)///n+1->2n-1的位置存儲新生成的非葉子結點 { min1=min2=MAX; lmin=rmin=-1; for(k=1;k<=i-1;k++)///尋找當前最小的兩個結點 { if(Ht[k].parent==-1)///該結點還沒被構造二叉樹 { if(Ht[k].w<min1) { min2=min1;rmin=lmin; min1=Ht[k].w;lmin=k; } else if(Ht[k].w<min2) { min2=Ht[k].w;rmin=k; } } } Ht[lmin].parent=i;Ht[rmin].parent=i; Ht[i].w=Ht[lmin].w+Ht[rmin].w; Ht[i].lchild=lmin;Ht[i].rchild=rmin; } }
哈夫曼編碼:
給使用頻率較高的字符進行較短的編碼,是一種可變長編碼,同時哈夫曼編碼也是一種前綴編碼(任何一個字符編碼都不是其它字符編碼的前綴),可以保證解碼不會產生二義性。一邊規定左分支為0,右分支為1
二叉判定樹
二叉判定樹是用於描述解決問題的思路。二叉判定樹的根結點是有序表中序號為mid=(n+1)/2的記錄,左子樹是與有序表1->mid-1的范圍存儲,根結點的右子樹是mid+1]-> n相對應的位置。左子樹都比根結點小,右子樹都比根結點大。(注意:二叉判定樹的形態只與表中元素個數n有關,與表中元素的關鍵字無關)
平均搜索長度:
- 搜索成功:As(n)=(每層結點數 × 比較次數(層數) / n
- 搜索失敗:Af(n)=(每層外節點數×比較次數(上一層層數))/(n+1)
二叉搜索樹
概念:所有結點的關鍵字的值都不一樣,左子樹都比根結點小,右子樹都比根結點大,而且根結點的左右子樹也都是二叉搜索樹。
- 若中序遍歷二叉搜索樹,會得到一個關鍵字遞增排列的有序序列。
- 已知一棵二叉搜索樹的先序遍歷序列,可以唯一確定一棵二叉樹。
刪除結點:
- 待刪除的結點沒有子樹:直接刪除
- 待刪除的結點僅有一顆子樹:用唯一子樹覆蓋父結點,原本指向被刪除結點的指針改為指向被刪除結點的子樹
- 待刪除的結點有兩顆子樹:使待刪除結點的左子樹代替被刪除的結點,將被刪除結點的右子樹放置於被刪除結點的左子樹的最右邊
二叉平衡樹
- 可以是空樹
- 假如不是空樹,任何一個結點的左子樹與右子樹都是平衡二叉樹,並且高度之差的絕對值不超過1
- 二叉平衡樹是建立在二叉搜索樹的基礎上,為了維護二叉搜索樹的高度,設置了平衡因子
調整二叉平衡樹:
- 從新插入結點回溯,找到第一個非平衡結點標為s,s向下的子樹為r
- s-r為LL型:以r為結點向上提;
- s-r為LR型:再將r的孩子標為u,以u為根結點,r和s分別為u的孩子(根據左小右大的原則分布)