判定樹
每個結點需要查找的次數剛好為該結點所在的層數,查找成功時查找次數不會超過判定樹的深度,n個結點的判定樹的深度為[LgN]+1
平均查找長度ASL(Average Search Length)
ASL=sum(層數*個數)/各層總個數n(n>=0)個結點構成的有限集合當n=0時稱為空樹
對于任何一棵非空樹(n>0),它具備以下性質
- 樹中會有一個root特殊結點用r表示
- 其余結點可分為m(m>0)個互不相交的有限集,其中每個集合本身又是一棵樹,稱為原來樹的子樹
樹的特點:
- 子樹是不相交的
- 出了根節點外,每隔結點有且僅有一個父結點
- 一棵N個結點的樹有N-1條邊(樹是保證結點連通的最少邊鏈接方式)
樹的一些基本術語:
結點的度:結點的子樹個數(滿二叉樹單個結點的度為2)
樹的度:樹的所有結點中最大的度數
葉結點:結點度為0的結點
父節點,子節點,兄弟結點
路徑和路徑長度
祖先節點:沿樹根到某一結點路徑上的所有節點都是這個結點的祖先結點
子孫結點通;結點的層次:規定根結點在一層,其他層次隨子節點+1
-
樹的深度:樹中結點的最大層次就是這棵樹的深度
兒子兄弟表示法可以將所有的樹轉化為二叉樹
特殊二叉樹: 斜二叉樹:只有左兒子或只有右結點
完美二叉樹:滿二叉樹
完全二叉樹:結點編號與滿二叉樹結點編號相同(編號不間斷)
二叉樹的特點一個二叉樹第i層的最大節點數為:2(i-1),i>=1
深度為k的二叉樹有最大節點總數為2k-1,k>=1;
對于任何非空二叉樹T,若N0表示葉子結點的個數,N2是度為2的非葉結點個數,那么兩者滿足關系:N0=N2+1;(即葉子結點個數-1=度數為2的結點個數)
二叉樹的抽象數據類型定義
數據對象集:一個有窮的結點集合
若不為空,則由根節點和其左、右二叉樹組成
操作集:判斷樹是否為空,遍歷,創建二叉樹
常用的遍歷方法有:
先序遍歷(根左右),
中序遍歷(左根右),
后序遍歷(左右根),
層次遍歷(從上到下,從左到右)
在二叉樹中,我們知道葉結點總數n0與有兩個兒子的結點總數n2之間的關系是:n0=n2+1.
那么類似關系是否可以推廣到m叉樹中?也就是,如果在m叉樹中,葉結點總數是n0,
有一個兒子的結點總數是n1,有2個兒子的結點總數是n2,有3個兒子的結點總數是n3,
那么,ni之間存在什么關系?
- 完全二叉樹,非根節點的父節點序號是[i/2]
- 結點的左孩子結點序號是2i,若2i<=n,否則沒有左孩子結點
- 結點的右孩子結點序號是2i+1,(若2i+1<=n,否則沒有右孩子)
typedef struct BT{
int value;
struct BT *leftchild;
struct BT *rightchild;
}BinTree;
//二叉樹的每個結點遍歷都會遇到三次,第一次遇到就打印的為先序遍歷,第二次遇到就打印的為中序遍歷,第三次遇到就打印的為后序遍歷
//先序遍歷(遞歸遍歷)
void PreOrderTraversal(BinTree *BT){
if(BT){
if(!BT->leftchild&&!BT->rightchild)
printf("%d\n",BT->value);
PreOrderTraversal(BT->leftchild);
PreOrderTraversal(BT->rightchild);
}
}
//中序遍歷(遞歸遍歷)
void InOrderTraversal(BinTree *BT){
if(BT){
if(!BT->leftchild&&!BT->rightchild)
InOrderTraversal(BT->leftchild);
printf("%d\n",BT->value);
InOrderTraversal(BT->rightchild);
}
}
//后序遍歷(遞歸遍歷)
void PostOrderTraversal(BinTree *BT){
if(BT){
if(!BT->leftchild&&!BT->rightchild)
PostOrderTraversal(BT->leftchild);
PostOrderTraversal(BT->rightchild);
printf("%d\n",BT->value);
}
}
//二叉樹遍歷的本質是將二維序列轉換為一維序列
//使用隊列進行二叉樹的層級訪問(遍歷根節點,將左右兒子節點入隊列)
void LevelOrderTraversal(BinTree BT){
Queue *queue;
BinTree *T;
queue=CreateQueue();
AddQueue(queue,BT);
while(!IsEmptyQueue(queue)){
T=DeleteQueue(queue);
printf("%d\n",T->value);
if(T->leftchild)AddQueue(queue,T->leftchild);
if(T->rightchild)AddQueue(queue,T->rightchild);
}
}
//給定前中序遍歷結果或中后序遍歷結果可以唯一確定一棵二叉樹,給定前后序遍歷結果不能唯一確定二叉樹
//非遞歸實現(中序遍歷)
void InOrderTraversal(BinTree *BT){
BinTree *T=BT;
LinkedStack *stack=CreateLinkedStack();//創建并初始化堆棧
while(T||!isEmpty(stack)){
while(T){//一直向左將沿途結點壓入堆棧
Push(stack,T);
T=T->leftchild;//轉向左子樹
}
if(!isEmpty(stack)){
T=Pop(stack);//結點彈出堆棧
printf("%5d",T->value);//打印結點
T=T->rightchild;//轉向右子樹
}
}
}
//非遞歸實現(先序遍歷)
void PreOrderTraversal(BinTree *BT){
BinTree *T=BT;
LinkedStack *stack=CreateLinkedStack();//創建并初始化堆棧
while(T||!isEmpty(stack)){
while(T){//一直向左將沿途結點壓入堆棧
printf("%5d",T->value);//打印結點
Push(stack,T);
T=T->leftchild;//轉向左子樹
}
if(!isEmpty(stack)){
T=Pop(stack);//結點彈出堆棧
T=T->rightchild;//轉向右子樹
}
}
}
二叉搜索樹:BST(binary search tree)
也稱二叉排序樹或二叉查找樹
二叉搜索樹條件
1.非空左子樹的所有鍵值小于其根節點的鍵值
2.非空右子樹的所有鍵值大于其根節點的鍵值
3.左,右子樹都是二叉搜索樹
//遞歸方式實現
Position Find(BinTree *binTree,int result){
if(!binTree)return NULL;
if(result>binTree->value)return Find(binTree->rightchild,result);
else if(result<binTree->value)return Find(binTree,result);
else return binTree;//查找成功,返回結點地址(return尾遞歸)
}
//非遞歸方式實現
Position IterFind(BinTree *binTree,int value){
while(binTree){
if(result>binTree->value)
binTree=binTree->rightchild;
else if(result<binTree->value)
binTree=binTree->leftchild;
else
return binTree;
}
return NULL;
}
//尋找最小值
Position FindMin(BinTree *binTree){
if(!binTree)return NULL;
else if(!binTree->leftchild)
return binTree;
else
return FindMin(binTree->leftchild);
}
//尋找最大值
Position FindMax(BinTree *binTree){
if(binTree){
while(binTree->rightchild)
binTree=binTree->rightchild;
}
return binTree;
}
//結點插入
BinTree * Insert(BinTree *binTree, int value) {
if(!binTree){
binTree=malloc(sizeof(BinTree));
binTree->value=value;
binTree->leftchild=binTree->rightchild=NULL;
}else{
if(value<binTree->value)
binTree->leftchild=Insert(binTree->leftchild,value);
else if(value>binTree->value)
binTree->rightchild=Insert(binTree->rightchild,value);
}
return binTree;
}
//刪除結點
BinTree *Delete(BinTree *binTree,int value){
(Position)BinTree *Temp;
if(!binTree)printf("要刪除的元素未找到");
//左子樹遞歸刪除
else if(value<binTree->value)binTree->leftchild=Delete(binTree,value);
//右子樹遞歸刪除
else if(value>binTree->value)binTree->rightchild=Delete(binTree->rightchild,value);
else //找到要刪除的結點
if(binTree->leftchild&&binTree->rightchild){//被刪除結點有左右量子子節點
Temp=FindMin(binTree->rightchild);//在右子樹中招最小的元素填充刪除結點
binTree->value=Temp->value;
binTree->rightchild=Delete(binTree->rightchild,binTree->value);
}else{//被刪除的結點有一個或無子結點
Temp=binTree;
if(!binTree->leftchild)binTree=binTree->rightchild;
else if(!binTree->rightchild)binTree=binTree->leftchild;
free(Temp);
}
return binTree;
}
平衡二叉樹(Balanced Binary Tree)
(AVL樹)(AVL是提出平衡樹的學者名字首字母)
- 空樹或任一結點左右子樹高度差不超不過1|BF(T)|<=1
- 平衡因子(Balance Factor 簡稱BF:BF(T)=Hl-Hr)
- 其中hl和hr分別為T的左右子樹高度
- 高度=層數-1
- 完全二叉樹高度為log2N(平衡二叉樹)
- Nh是高度為h的平衡二叉樹的最小結點樹
- Nh=F(h+2)-1
#define MaxData 10000
typedef struct HeapStruct{
int *value;//存儲對元素的數組
int length;//堆的當前元素個數
int capacity;//堆的最大容量
}Heap;
優先隊列(PriorityQueue)
取出元素的先后順序是按照元素的優先權(關鍵字)大小,而不是元素進入隊列的先后順序
最大堆和最小堆都必須滿足完全二叉樹(切根節點最大或最小)最大堆的建立
建立最大堆:將已經存在的N個元素按最大堆的要求存放在要給一維數組中
- 方法一:通過插入操作,將N個元素一個個相繼插入到一個初始為空的堆中去,其時間代價最大為O(NlogN)
- 方法二:在線性時間復雜度下建立最大堆
- (1)將N個元素按輸入順序存入,先滿足完全二叉樹的結構特性
- (2)調整各結點位置以滿足最大堆的有序特性
建堆時,最壞的情況下需要挪動的元素次數等于樹中各節點的高度和
對由同樣n個整數構成的二叉搜索樹(查找樹)和最小堆:有以下結論
- 二叉搜索樹高度大于等于最小堆高度
- 對該二叉搜索樹進行中序遍歷可得到從小到大的序列
- 從最小堆根節點到起任何葉結點的路徑上的結點值構成從小到大的序列
Heap * Create(int MaxSize){
Heap *heap=malloc(sizeof(Heap));
heap->value=malloc((MaxSize+1)*sizeof(int));
heap->length=0;
heap->capacity=MaxSize;
heap->value[0]=MaxData;//定義哨兵,便于操作
return heap;
}
void Insert(Heap *heap,int value){
int i;
if(IsFull(heap)){
printf("最大堆已經滿了");
return;
}
i=++heap->length;
for(;heap->value[i/2]<value;i/=2)
heap->value[i]=heap->value[i/2];
heap->value[i]=value;
}
int DeleteMax(Heap *heap){
int parent,child;
int maxValue,temp;
if(IsEmpty(heap)){
printf("最大堆已空");
return 0;
}
maxValue=heap->value[1];
//用最大堆中最后一個元素從根節點開始過濾下層結點
temp=heap->value[heap->length--];
for(parent=1;parent*2<=heap->length;parent=child){
child=parent*2;
//左兒子和右兒子節點比較取較大者
if((child!=heap->length)&&(heap->value[child]<heap->value[child+1]))
child++;
if(temp>=heap->value[child])break;
else
heap->value[parent]=heap->value[child];
}
heap->value[parent]=temp;
return maxValue;
}
int IsEmpty(Heap *heap){
return heap->length==0;
}
int IsFull(Heap *heap){
return heap->length==heap->capacity;
}
typedef struct TreeNode{
int weight;
struct TreeNode *left,*right;
}HuffmanTree;
哈夫曼樹(HuffmanTree)
查找效率,查找次數乘查找概率
帶權路徑長度(WPL):設二叉樹有n個葉子結點,每隔葉子結點帶有權值Wk,從根節點到每隔葉子結點的長度是Lk,則每隔葉子結點的帶全路徑長度之和WPL=(nEk=1)WkLk
最優二叉樹或哈夫曼樹:WPL最小的二叉樹
哈夫曼樹的特點
- 沒有度為1的結點
- n個葉子結點的HuffmanTree有2n-1個結點
- HuffmanTree的任意非葉結點的左右子樹交換后仍是HuffmanTree
對于一組權值,可能有不同構的兩棵HuffmanTree
HuffmanTree *Huffman(Heap *heap){
//假設heap->length權值已經存在heap->value[]->weight里面
int i;HuffmanTree *huffmanTree;
BuildHeap(heap);//將heap->value[]按權值調整為最小堆
for(i=1;i<heap->length;i++){
huffmanTree=malloc(sizeof(HuffmanTree));//建立新結點
huffmanTree->left=DeleteMin(heap);//從最小堆中刪除一個結點,作為新huffmanTree的左子結點
huffmanTree->right=DeleteMin(heap);//從最小堆中刪除一個結點,作為新huffmanTree的右子結點
huffmanTree->weight=huffmanTree->weight+huffmanTree->right->weight;//計算新// 權值
Insert(heap,huffmanTree);
}
huffmanTree=DeleteMin(heap);
return huffmanTree;
}
/*二叉樹用于編碼
* 當被編碼字母全部在二叉樹的葉子結點的時候(即,編碼字母不會出現在有子節點的結點中)便可以保證字符編碼沒有二義性
* */