樹
樹形結構是一類非常重要的非線性結構,它可以很好地描述客觀世界中廣泛存在的具有分支關系或層次特性的對象,因此在計算機領域里有着廣泛應用,如操作系統中的文件管理、編譯程序中的語法結構和數據庫系統信息組織形式等。
樹的相關定義
- 節點的度:一個節點含有的子樹的個數稱為該節點的度;
- 樹的度:一棵樹中,最大的節點的度稱為樹的度;
- 葉節點或終端節點:度為零的節點;
- 非終端節點或分支節點:度不為零的節點;
- 雙親節點或父節點:若一個結點含有子節點,則這個節點稱為其子節點的父節點;
- 孩子節點或子節點:一個節點含有的子樹的根節點稱為該節點的子節點;
- 兄弟節點:具有相同父節點的節點互稱為兄弟節點;
- 節點的層次:從根開始定義起,根為第1層,根的子節點為第2層,以此類推;
- 樹的高度或深度:樹中節點的最大層次;
- 堂兄弟節點:雙親在同一層的節點互為堂兄弟;
- 節點的祖先:從根到該節點所經分支上的所有節點;
- 子孫:以某節點為根的子樹中任一節點都稱為該節點的子孫。
- 森林:由m(m>=0)棵互不相交的樹的集合稱為森林;
二叉樹
二叉樹是樹形結構的一個重要類型。許多實際問題抽象出來的數據結構往往是二叉樹的形式,即使是一般的樹也能簡單地轉換為二叉樹,而且二叉樹的存儲結構及其算法都較為簡單,因此二叉樹顯得特別重要。
二叉樹(BinaryTree)是n(n≥0)個結點的有限集,它或者是空集(n=0),或者由一個根結點及兩棵互不相交的、分別稱作這個根的左子樹和右子樹的二叉樹組成。
完全二叉樹:對於一顆二叉樹,假設其深度為d(d>1)。除了第d層外,其它各層的節點數目均已達最大值,且第d層所有節點從左向右連續地緊密排列,這樣的二叉樹被稱為完全二叉樹。
1.二叉樹的遍歷
所謂遍歷二叉樹,就是遵從某種次序,訪問二叉樹中的所有結點,使得每個結點僅被訪問一次。
二叉樹的遍歷包括三種:
- DLR稱為前根遍歷(前序遍歷)
- 訪問結點的操作發生在遍歷其左右子樹之前
- LDR稱為中根遍歷(中序遍歷)
- 訪問結點的操作發生在遍歷其左右子樹之中(間)
- LRD稱為后根遍歷(后序遍歷)
- 訪問結點的操作發生在遍歷其左右子樹之后
遞歸實現:
void preorder(NODE *p) { if(p!=NULL) {printf(“%d ”,p->data); preorder(p->lchild); preorder (p->rchild);} } void InOrder(BinTree T) { if(T) { // 如果二叉樹非空 InOrder(T->lchild); printf("%c",T->data); // 訪問結點 InOrder(T->rchild); } } // InOrder void posorder(NODE *p) { if(p!=NULL) { posorder(p->lchild); posorder (p->rchild); printf(“%d ”,p->data); } }
非遞歸實現:
/*算法思想: 利用隊列基本操作 1.初始化:根結點入隊列 2.while(隊列非空) { a.隊首元素出隊列 b.原隊首元素對應的左、右孩子(非空)入隊列 }*/ void traverse(NODE *T) { NODE *q[100]; int head,tail, i; q[0]=T;head=0;tail=1; while(head<tail) { p=q[head++]; printf(“%c”,T->data); if(p->lchild!=NULL) q[tail++]=p->lchild; if(p->rchild!=NULL) q[tail++]=p->rchild; } }
2.二叉樹的構造
基於先序遍歷的構造,即以二叉樹的先序序列為輸入構造。
void CreateBinTree (BinTree *T) { char ch; if((ch=getchar())=='') *T=NULL; //讀人空格,將相應指針置空 else{ //讀人非空格 *T=(BinTNode *)malloc(sizeof(BinTNode)); //生成結點 (*T)->data=ch; CreateBinTree(&(*T)->lchild); //構造左子樹 CreateBinTree(&(*T)->rchild); //構造右子樹 } }
最優二叉樹
在權為wl,w2,…,wn的n個葉子所構成的所有二叉樹中,帶權路徑長度最小(即代價最小)的二叉樹稱為最優二叉樹或哈夫曼樹。
假設有n個權值,則構造出的哈夫曼樹有n個葉子結點。 n個權值分別設為 w1、w2、…、wn,則哈夫曼樹的
構造規則為:
- 將w1、w2、…,wn看成是有n 棵樹的森林(每棵樹僅有一個結點);
- 在森林中選出兩個根結點的權值最小的樹合並,作為一棵新樹的左、右子樹,且新樹的根結點權值為其左、右子樹根結點權值之和;
- 從森林中刪除選取的兩棵樹,並將新樹加入森林;
- 重復(2)、(3)步,直到森林中只剩一棵樹為止,該樹即為所求得的哈夫曼樹。
具體代碼:
#include "stdio.h" #include "stdlib.h" #define m 100 struct ptree //定義二叉樹結點類型 { int w; //定義結點權值 struct ptree *lchild; //定義左子結點指針 struct ptree *rchild; //定義右子結點指針 }; struct pforest //定義鏈表結點類型 { struct pforest *link; struct ptree *root; }; int WPL=0; //初始化WTL為0 struct ptree *hafm(); void travel(); struct pforest *inforest(struct pforest *f,struct ptree *t); void travel(struct ptree *head,int n) { //為驗證harfm算法的正確性進行的遍歷 struct ptree *p; p=head; if(p!=NULL) { if((p->lchild)==NULL && (p->rchild)==NULL) //如果是葉子結點 { printf("%d ",p->w); printf("the hops of the node is: %d/n",n); WPL=WPL+n*(p->w); //計算權值 }//if travel(p->lchild,n+1); travel(p->rchild,n+1); }//if }//travel struct ptree *hafm(int n, int w[m]) { struct pforest *p1,*p2,*f; struct ptree *ti,*t,*t1,*t2; int i; f=(pforest *)malloc(sizeof(pforest)); f->link=NULL; for(i=1;i<=n;i++) //產生n棵只有根結點的二叉樹 { ti=(ptree*)malloc(sizeof(ptree));//開辟新的結點空間 ti->w=w[i]; //給結點賦權值 ti->lchild=NULL; ti->rchild=NULL; f=inforest(f, ti); //按權值從小到大的順序將結點從上到下地掛在一顆樹上 }//for while(((f->link)->link)!=NULL)//至少有二棵二叉樹 { p1=f->link; p2=p1->link; f->link=p2->link; //取出前兩棵樹 t1=p1->root; t2=p2->root; free(p1); //釋放p1 free(p2); //釋放p2 t=(ptree *)malloc(sizeof(ptree));//開辟新的結點空間 t->w = (t1->w)+(t2->w); //權相加 t->lchild=t1; t->rchild=t2; //產生新二叉樹 f=inforest(f,t); //每次構造一顆二叉樹的時候,都要從新排列一下 }//while p1=f->link; t=p1->root; free(f); return(t); //返回t } pforest *inforest(struct pforest *f,struct ptree *t) { //按權值從小到大的順序將結點從上到下地掛在一顆樹上 struct pforest *p, *q, *r; struct ptree *ti; r=(pforest *)malloc(sizeof(pforest)); //開辟新的結點空間 r->root=t; q=f; p=f->link; while (p!=NULL) //尋找插入位置 { ti=p->root; if(t->w > ti->w) //如果t的權值大於ti的權值 { q=p; p=p->link; //p向后尋找 }//if else p=NULL; //強迫退出循環 }//while r->link=q->link; q->link=r; //r接在q的后面 return(f); //返回f } void InPut(int &n,int w[m]) { printf("please input the sum of node/n"); //提示輸入結點數 scanf("%d",&n); //輸入結點數 printf ("please input weight of every node/n"); //提示輸入每個結點的權值 for(int i=1;i<=n;i++) scanf("%d",&w[i]); //輸入每個結點權值 } int main( ) { struct ptree *head; int n,w[m]; InPut(n,w); head=hafm(n,w); travel(head,0); printf("The length of the best path is WPL=%d", WPL);//輸出最佳路徑權值之和 return 1; }