前言
PS:樹型結構是一種重要的非線性數據結構,教科書上一般都是樹與二叉樹,由此可見,樹和二叉樹是有區別和聯系的,網上有人說二叉樹是樹的一種特殊形式,但經過查資料,樹和二叉樹沒有一個肯定的說法,但唯一可以肯定都是樹型結構。但是按照定義來看二叉樹並不是樹的一種特殊形式(下面解釋)。樹型數據結構的作用可以表示數據元素之間一對多的關系,一個公司里的各個部門都可以用樹形來表示。
二叉樹不是樹的一種特殊情形,主要差別:
- 樹中結點的最大度數沒有限制,而二叉樹結點的最大度數為2;
- 樹的結點無左、右之分,而二叉樹的結點有左、右之分。
二叉樹類型
(1)完全二叉樹 ——若設二叉樹的高度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h層有葉子結點,並且葉子結點都是從左到右依次排布,這就是完全二叉樹。
(2)滿二叉樹 ——除了葉結點外每一個結點都有左右子葉且葉子結點都處在最底層的二叉樹。
(3)平衡二叉樹——平衡二叉樹又被稱為AVL樹(區別於AVL算法),它是一棵二叉排序樹,且具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。
二叉樹的鏈式
1:結構體
/*
劉志通
**/
typedef struct twoCha { char data; struct twoCha *Lchild, *Rchild;//左右孩子結點 } twoCha, *twoChaL;
2:創建二叉樹
當我們在鍵盤上敲了一行的char類型數據,可以按照一定的結構儲存在計算機上,就要寫一個算法,二叉樹分左右孩子,所以,如果沒有左(右)孩子就輸入一個空格或者#,代表空。如果有左(右)孩子就新建一個結點,來存放該結點的數據data。
int createTwo(twoChaL &tL) { char ch; scanf("%c",&ch); if (ch == '#') { tL = NULL; return 0; } else { tL= (twoChaL)malloc(sizeof(twoCha)); if (tL == NULL) { printf("錯誤tl=NULL\n"); return 0; } else { tL->data = ch; createTwo(tL->Lchild); createTwo(tL->Rchild); } } return 0; }
3:遞歸遍歷
畫的不好,湊活着看吧。
我們以這個二叉樹為例子,來分析他的遍歷形式,遍歷分為三種
- 先序遍歷 -- 根左右 -- ABDCE
- 中序遍歷 -- 左根右 -- DBAEC
- 后序遍歷 -- 左右根 -- DBECA
void diGuiBianLi(twoChaL &tL,int xl) { if (tL == NULL) { return; } else { if(xl == 1){ //先序遍歷 printf("%c ",tL->data); diGuiBianLi(tL->Lchild,xl); diGuiBianLi(tL->Rchild,xl); }else if(xl == 2){ //中序遍歷 diGuiBianLi(tL->Lchild,xl); printf("%c ",tL->data); diGuiBianLi(tL->Rchild,xl); } else if(xl == 3){ //后序遍歷 diGuiBianLi(tL->Lchild,xl); diGuiBianLi(tL->Rchild,xl); printf("%c ",tL->data); } } }
遞歸的代碼非常少,但是一開始接觸 理解起來還是比較難的,當然,你一旦理解后,那就非常簡單了。遞歸的思想就是把一個大問題分成多個類似的小問題解決。
4:葉子個數
方法:查找一個結點沒有左右孩子,就是葉子結點。可以定義一個static int count變量,如果該結點沒有左右結點那么就讓count++;
int leafCount(twoChaL &tL) { static int count; if (tL != NULL) { if (tL->Lchild == NULL && tL->Rchild == NULL) { count++; } leafCount(tL->Lchild); leafCount(tL->Rchild); } return count; }
5:深度
方法:看結點的左孩子和右孩子哪一個更長,當深入到最低結點時,左右孩子比較,誰大誰加一(相等左孩子加一)。
int treeDeep(twoChaL &tL) { int deep = 0; if (tL != NULL) { int leftdeep = treeDeep(tL->Lchild); int rightdeep = treeDeep(tL->Rchild); deep = leftdeep >= rightdeep?leftdeep+1:rightdeep+1; } return deep; }
6:非遞歸遍歷
這里以中序為例,利用棧的知識點(順序棧),首先把根節點進棧,然后依次左孩子進棧,直到左孩子為NULL時結束,但是NULL也是入棧的,然后再把NULL出棧,下一步就是把棧頂元素取出並打印,再把該棧頂元素的右孩子進棧,不管右孩子是不是NULL都要入棧,入棧之前把棧頂元素彈棧。
定義棧的data域,要用結點的結構體
int top = -1; //棧 void push(twoChaL *a,twoChaL elem){ a[++top]=elem; } //彈棧函數 void pop(){ if (top==-1) { return ; } top--; } //拿到棧頂元素 twoChaL getTop(twoChaL *a){ return a[top]; } void zhongXu2(twoChaL Tree){ //順序棧 twoChaL a[100]; twoChaL p; push(a, Tree);//根結點進棧 while (top!=-1) {//top!=-1說明棧內不為空 while ((p=getTop(a)) &&p){//取棧頂元素,且不能為NULL push(a, p->Lchild);//將該結點的左孩子進棧,如果沒有左孩子,NULL進棧 } pop();//跳出循環,棧頂元素肯定為NULL,將NULL彈棧 /* //測試數據,主要觀察棧中NULL出棧后,還有什么在棧中(棧頂), if(a[top]){ printf(" 跳出循環 %c\n",a[top]->data); }*/ if (top!=-1) { p=getTop(a);//取棧頂元素 pop();//棧頂元素彈棧 printf("%c ",p->data); push(a, p->Rchild);//將p指向的結點的右孩子進棧 } } }
結果圖:
討論群