線索二叉樹和二叉樹基本操作的實現


2018-11-20-18:25:23

一:二叉樹

1.二叉樹的性質

  ①:在二叉樹的第i層上至多有pow(2,i-1)個結點(i>=1)。

  ②:深度為k的二叉樹至多有pow(2,k)-1個結點(k>=1)。

  ③:對任何一顆二叉樹T,如果其終端結點的個數為n0,度為2的結點數為n2,則n0==n2+1。

  ④:具有n個結點的完全二叉樹的深度為log2n(取不大於它的最大正整數)+1。

  ⑤:對於一顆有n個結點的完全二叉樹,對任一結點i(i>=1&&i<=n)有:

    <1>:如果i==1,則結點i是二叉樹的根,無雙親;如果i>1,則其雙親是結點i/2(取不大於它的最大正整數)。

    <2>:如果2*i>n,則結點i無左孩子(結點i為葉子結點),否則其左孩子是結點2*i。

    <3>:如果2*i+1>n,則結點i無右孩子,否則其右孩子是結點2i+1。

2.二叉樹的層序遍歷:初始時讓根結點入隊列,然后while循環判斷隊列不空時,彈出一個結點並且訪問它,並把它的所有孩子結點入隊列,隊列空時結束循環。

 

 1 void PrintBiTree(BiTree T){
 2 //按照層序遍歷輸出二叉樹
 3     if(T==NULL)  return;
 4     queue<BiTree>QueueTreeNode;
 5     QueueTreeNode.push(T);//首先將二叉樹的頭結點放入隊列
 6     while(!QueueTreeNode.empty()){//如果隊列為空則結束遍歷
 7         BiTree QueueNode=QueueTreeNode.front();//每次訪問隊列的第一個元素並將其彈出
 8         QueueTreeNode.pop();
 9         cout<<QueueNode->Elem<<' ';
10         if(QueueNode->Left_Child)//將第一個元素的左右子樹的結點都放入隊列
11             QueueTreeNode.push(QueueNode->Left_Child);
12         if(QueueNode->Right_Child)
13             QueueTreeNode.push(QueueNode->Right_Child);
14     }
15 }

 

 

3.二叉樹的非遞歸遍歷:

  ① 非遞歸前序遍歷:

      <1> 算法思路:

         創建一個結點用來遍歷二叉樹,從二叉樹根結點開始訪問二叉樹,若cur結點存在左子樹則將其打印並且入棧等待下一步操作,cur結點不存在左子樹時判斷棧是否空,棧非空時讓cur等於其棧頂元素並且

        彈出棧頂元素,接着訪問改結點的右子樹(左子樹剛剛都打印完了才放入棧的呀)。

      代碼:

 

 1 void PreOrderTraverse_Stack_one(BiTree T){
 2     //非遞歸實現二叉樹的先序遍歷
 3     BiTree cur=T;
 4     stack<BiTree>StackNode;
 5     while(cur||!StackNode.empty()){
 6         while(cur){
 7             cout<<cur->Elem;//先訪問元素
 8             StackNode.push(cur);//然后入棧以便訪問他的子樹
 9             cur=cur->Left_Child;//循環結束時這個結點的所有子樹的左子樹已經輸出完畢並且儲存到棧中等待下一步調用
10         }
11         if(!StackNode.empty()){
12             cur=StackNode.top();//如果棧非空,取棧頂元素並且將其彈出
13             StackNode.pop();
14             cur=cur->Right_Child;//如果存在右子樹則對右子樹進行上述操作,不存在右子樹則跳過該結點,對下一個結點進行上述操作
15         }
16     }
17 }

 

      <2> 算法思路:

          創建一個結點用來遍歷二叉樹,初始時將二叉樹的根結點壓入棧頂,對其棧頂進行訪問並彈出棧頂元素,先將棧頂輸出,然后依次將棧頂元素的右子樹和左子樹壓入棧(如果子樹結點非空),重復上述步驟,

        當棧空時結束遍歷。

      代碼:

 1 void PreOrderTraverse_Stack_two(BiTree T){
 2     if(!T)  return;
 3     stack<BiTree>StackNode;
 4     BiTree cur=T;//初始時讓cur指向二叉樹的根結點
 5     StackNode.push(cur);
 6     while(!StackNode.empty()){
 7         cur=StackNode.top();//每次都讓cur指向棧頂結點並且彈出棧頂元素
 8         StackNode.pop();
 9         if(cur){//如果棧頂元素存在則輸出其並且將它的右子樹和左子樹依次壓入棧中
10             cout<<cur->Elem;
11             StackNode.push(cur->Right_Child);
12             StackNode.push(cur->Left_Child);
13         }
14     }
15 }

 

  ② 非遞歸中序遍歷:

      算法思路:

          創建一個結點來遍歷二叉樹,從二叉樹的根結點開始訪問二叉樹,因為每個結點又是另一顆二叉樹,所以將路徑上所有的左子樹的結點保存,直至二叉樹不存在左子樹時訪問該結點並且將其彈出,接着對其右子樹

        進行上述操作,直至棧為空時訪問完畢。

      代碼:

 1 void Stack_InOrderTraverse(BiTree T){
 2     //非遞歸實現二叉樹的中序遍歷
 3     BiTree cur=T;
 4     stack<BiTree>StackNode;
 5     while(cur||!StackNode.empty()){
 6         while(cur){
 7             StackNode.push(cur);//然后入棧以便訪問他的子樹
 8             cur=cur->Left_Child;//循環結束時這個結點的所有子樹的左子樹已經輸出完畢並且儲存到棧中等待下一步調用
 9         }
10         if(!StackNode.empty()){
11             cur=StackNode.top();//如果棧非空,取棧頂元素並且將其彈出
12             cout<<cur->Elem;//訪問元素
13             StackNode.pop();
14             cur=cur->Right_Child;//如果存在右子樹則對右子樹進行上述操作,不存在右子樹則跳過該結點,對下一個結點進行上述操作
15         }
16     }
17 }

   

  ③非遞歸后序遍歷:

      算法思路:創建一個結點指向二叉樹的根結點用來遍歷二叉樹,首先利用循環找到后續遍歷的第一個結點並將路徑上的所有元素入棧,接着讓cur等於當前的棧頂元素,每次訪問棧頂元素並且將其彈出,判斷

    如果當前結點等於棧頂元素的左兒子則說明右兒子還未處理,則將右兒子插入棧頂,否則用NULL表示可以處理當前棧頂元素,循環上述操作,當棧為空時結束循環。

      代碼:

 1 void PostOrderTraverse_Stack(BiTree T){
 2     stack<BiTree>StackNode;
 3     BiTree  cur=T;//遍歷二叉樹所用的結點
 4     while(cur||!StackNode.empty()){//棧空或者樹不存在時結束遍歷
 5         while(cur){
 6             StackNode.push(cur);
 7             cur=cur->Left_Child?cur->Left_Child:cur->Right_Child;//找到后序遍歷的第一個結點,並且將路徑上的結點都放入棧
 8         }
 9         cur=StackNode.top();//每次讓cur等於棧頂元素
10         StackNode.pop();
11         cout<<cur->Elem<<' ';//訪問棧頂元素並且將其彈出棧
12         if(!StackNode.empty()&&cur==(StackNode.top())->Left_Child)
13             cur=(StackNode.top())->Right_Child;//如果當前元素為棧頂元素的左兒子則說明右兒子還未處理,則讓當前元素等於棧頂元素的,否則用NULL表示可以處理當前棧頂元素
14         else
15             cur=NULL;
16     }
17 }

 

 

二叉樹完整代碼:

  1 /*********************************************************
  2     鏈式二叉樹基本操作的實現
  3     mian函數操作:
  4         1.輸入一個二叉樹,葉結點以‘#’表示
  5 
  6 **********************************************************/
  7 #include <cstdio>
  8 #include <iostream>
  9 #include <stack>
 10 #include <queue>
 11 using namespace std;
 12 #define Maxn 0xffff
 13 typedef char TreeElemtype;
 14 typedef struct Bitree{
 15     TreeElemtype Elem;
 16     struct Bitree*Right_Child;
 17     struct Bitree*Left_Child;
 18 }BiNode,*BiTree;
 19 
 20 void CreatBiTree(BiTree &T);
 21 void PreOrderTraverse(BiTree T);
 22 void InOrderTraverse(BiTree T);
 23 void PostOrderTraverse(BiTree T);
 24 void PrintBiTree(BiTree T);
 25 void Stack_InOrderTraverse_one(BiTree T);
 26 void Stack_InOrderTraverse_two(BiTree T);
 27 void PreOrderTraverse_Stack_one(BiTree T);
 28 void PreOrderTraverse_Stack_two(BiTree T);
 29 void PostOrderTraverse_Stack(BiTree T);
 30 int Depth_of_BiTree(BiTree T);
 31 int Count_of_BiNode(BiTree T);
 32 int LeafBiNode(BiTree T);
 33 int Node_one_Count(BiTree T);
 34 void PrintAllPath(BiTree T, char path[], int pathlen);
 35 //main函數內所有數據均為測試數據,讀者可根據自己測試方式自行調換
 36 
 37 int main(){
 38     BiTree T;
 39     CreatBiTree(T);
 40     cout<<"遞歸式前序遍歷輸出:"<<'\t';
 41     PreOrderTraverse(T);
 42     cout<<endl;
 43     cout<<"非遞歸式前序遍歷輸出:"<<'\t';
 44     PreOrderTraverse_Stack_one(T);
 45     cout<<endl;
 46     cout<<"非遞歸式前序遍歷輸出:"<<'\t';
 47     PreOrderTraverse_Stack_two(T);
 48     cout<<endl;
 49     cout<<"遞歸式中序遍歷輸出:"<<'\t';
 50     InOrderTraverse(T);
 51     cout<<endl;
 52     cout<<"非遞歸式中序遍歷輸出:"<<'\t';
 53     Stack_InOrderTraverse_one(T);
 54     cout<<endl;
 55     cout<<"遞歸式后序遍歷輸出:"<<'\t';
 56     PostOrderTraverse(T);
 57     cout<<endl;
 58     cout<<"非遞歸式后序遍歷輸出:"<<'\t';
 59     PostOrderTraverse_Stack(T);
 60     cout<<endl;
 61     cout<<"層序遍歷輸出:"<<'\t';
 62     PrintBiTree(T);
 63     cout<<endl;
 64     cout<<"二叉樹的深度為"<<'\t'<<Depth_of_BiTree(T)<<endl;
 65     cout<<"二叉樹的結點個數為"<<'\t'<<Count_of_BiNode(T)<<endl;
 66     cout<<"二叉樹的葉子結點個數為"<<'\t'<<LeafBiNode(T)<<endl;
 67     cout<<"二叉樹的度為1的結點的個數為"<<'\t'<<Node_one_Count(T)<<endl;
 68     int pathlen=0;
 69     char path[Maxn];
 70     PrintAllPath(T,path,pathlen);
 71     return 0;
 72 }
 73 void CreatBiTree(BiTree&T){
 74     //先序創建二叉樹,以'#'代表該結點為葉子結點
 75     TreeElemtype ch;
 76     cin>>ch;
 77     if(ch=='#')
 78         T=NULL;
 79     else{
 80         T=new BiNode;
 81         T->Elem=ch;
 82         CreatBiTree(T->Left_Child);
 83         CreatBiTree(T->Right_Child);
 84     }
 85 }
 86 
 87 void PreOrderTraverse(BiTree T){
 88     if(T){
 89         cout<<T->Elem<<' ';
 90         PreOrderTraverse(T->Left_Child);
 91         PreOrderTraverse(T->Right_Child);
 92     }
 93 }
 94 
 95 void InOrderTraverse(BiTree T){
 96     if(T){
 97         InOrderTraverse(T->Left_Child);
 98         cout<<T->Elem<<' ';
 99         InOrderTraverse(T->Right_Child);
100     }
101 }
102 
103 void PostOrderTraverse(BiTree T){
104     if(T){
105         PostOrderTraverse(T->Left_Child);
106         PostOrderTraverse(T->Right_Child);
107         cout<<T->Elem<<' ';
108     }
109 }
110 
111 void PrintBiTree(BiTree T){
112 //按照層序遍歷輸出二叉樹
113     if(T==NULL)  return;
114     queue<BiTree>QueueTreeNode;
115     QueueTreeNode.push(T);//首先將二叉樹的頭結點放入隊列
116     while(!QueueTreeNode.empty()){//如果隊列為空則結束遍歷
117         BiTree QueueNode=QueueTreeNode.front();//每次訪問隊列的第一個元素並將其彈出
118         QueueTreeNode.pop();
119         cout<<QueueNode->Elem<<' ';
120         if(QueueNode->Left_Child)//將第一個元素的左右子樹的結點都放入隊列
121             QueueTreeNode.push(QueueNode->Left_Child);
122         if(QueueNode->Right_Child)
123             QueueTreeNode.push(QueueNode->Right_Child);
124     }
125 }
126 
127 void Stack_InOrderTraverse_one(BiTree T){
128     //非遞歸實現二叉樹的中序遍歷
129     BiTree cur=T;
130     stack<BiTree>StackNode;
131     while(cur||!StackNode.empty()){
132         while(cur){
133             StackNode.push(cur);//然后入棧以便訪問他的子樹
134             cur=cur->Left_Child;//循環結束時這個結點的所有子樹的左子樹已經輸出完畢並且儲存到棧中等待下一步調用
135         }
136         if(!StackNode.empty()){
137             cur=StackNode.top();//如果棧非空,取棧頂元素並且將其彈出
138             cout<<cur->Elem<<' ';//訪問元素
139             StackNode.pop();
140             cur=cur->Right_Child;//如果存在右子樹則對右子樹進行上述操作,不存在右子樹則跳過該結點,對下一個結點進行上述操作
141         }
142     }
143 }
144 
145 void PreOrderTraverse_Stack_one(BiTree T){
146     //非遞歸實現二叉樹的先序遍歷
147     BiTree cur=T;
148     stack<BiTree>StackNode;
149     while(cur||!StackNode.empty()){
150         while(cur){
151             cout<<cur->Elem<<' ';//先訪問元素
152             StackNode.push(cur);//然后入棧以便訪問他的子樹
153             cur=cur->Left_Child;//循環結束時這個結點的所有子樹的左子樹已經輸出完畢並且儲存到棧中等待下一步調用
154         }
155         if(!StackNode.empty()){
156             cur=StackNode.top();//如果棧非空,取棧頂元素並且將其彈出
157             StackNode.pop();
158             cur=cur->Right_Child;//如果存在右子樹則對右子樹進行上述操作,不存在右子樹則跳過該結點,對下一個結點進行上述操作
159         }
160     }
161 }
162 
163 void PreOrderTraverse_Stack_two(BiTree T){
164     if(!T)  return;
165     stack<BiTree>StackNode;
166     BiTree cur=T;//初始時讓cur指向二叉樹的根結點
167     StackNode.push(cur);
168     while(!StackNode.empty()){
169         cur=StackNode.top();//每次都讓cur指向棧頂結點並且彈出棧頂元素
170         StackNode.pop();
171         if(cur){//如果棧頂元素存在則輸出其並且將它的右子樹和左子樹依次壓入棧中
172             cout<<cur->Elem<<' ';
173             StackNode.push(cur->Right_Child);
174             StackNode.push(cur->Left_Child);
175         }
176     }
177 }
178 
179 void PostOrderTraverse_Stack(BiTree T){
180     stack<BiTree>StackNode;
181     BiTree  cur=T;//遍歷二叉樹所用的結點
182     while(cur||!StackNode.empty()){//棧空或者樹不存在時結束遍歷
183         while(cur){
184             StackNode.push(cur);
185             cur=cur->Left_Child?cur->Left_Child:cur->Right_Child;//找到后序遍歷的第一個結點,並且將路徑上的結點都放入棧
186         }
187         cur=StackNode.top();//每次讓cur等於棧頂元素
188         StackNode.pop();
189         cout<<cur->Elem<<' ';//訪問棧頂元素並且將其彈出棧
190         if(!StackNode.empty()&&cur==(StackNode.top())->Left_Child)
191             cur=(StackNode.top())->Right_Child;//如果當前元素為棧頂元素的左兒子則說明右兒子還未處理,則將右兒子插入棧頂,否則用NULL表示可以處理當前棧頂元素
192         else
193             cur=NULL;
194     }
195 }
196 
197 int Depth_of_BiTree(BiTree T){
198     if(T==NULL) return 0;
199     int m=Depth_of_BiTree(T->Left_Child);
200     int n=Depth_of_BiTree(T->Right_Child);
201     return m>n?m+1:n+1;
202 }
203 
204 int Count_of_BiNode(BiTree T){
205     if(T==NULL)  return 0;
206     else  return Count_of_BiNode(T->Left_Child)+Count_of_BiNode(T->Right_Child)+1;
207 }
208 
209 int LeafBiNode(BiTree T){
210     if(!T)  return 0;
211     if((!T->Left_Child)&&(!T->Right_Child))
212         return 1;
213     else
214         return LeafBiNode(T->Left_Child)+LeafBiNode(T->Right_Child);
215 }
216 
217 int Node_one_Count(BiTree T){
218     if(!T)  return 0;
219     if(((!T->Left_Child)&&(T->Right_Child))||((!T->Right_Child)&&(T->Left_Child)))
220         return 1;
221     else
222         return Node_one_Count(T->Left_Child)+Node_one_Count(T->Right_Child);
223 }
224 
225 void PrintAllPath(BiTree T, char path[], int pathlen){//二叉樹中從每個葉子結點到根結點的路徑
226     if(T!=NULL){
227     path[pathlen]=T->Elem;//將當前結點放入路徑中
228     if(T->Left_Child==NULL&&T->Right_Child==NULL){//葉子結點
229         for(int i=pathlen;i>=0;i--)
230             cout<<path[i]<<" " ;
231         cout<<endl;
232     }
233     else{
234         PrintAllPath(T->Left_Child,path,pathlen + 1);
235         PrintAllPath(T->Right_Child,path,pathlen + 1);
236         }
237     }
238 }
239 
240 /****************************************
241     Author:CRUEL_KING
242     Time:2018/11/20
243     Program name:鏈式二叉樹基本操作的實現.cpp
244 ****************************************/

 

   

二:線索二叉樹

1. 線索二叉樹的定義:n個結點的二叉鏈表中含有n+1[2n-(n-1)=n+1]個空指針域。利用二叉鏈表中的空指針域,存放指向結點在某種遍歷次序下的前驅和后繼結點的指針(這種附加的指針稱為"線索")。

 

1 typedef bool PointerTag;
2 typedef char TreeElemtype;
3 typedef struct BiThrNode{
4     TreeElemtype Elem;//存放結點數據
5     PointerTag LTag,RTag;//true表示指向前驅或后繼的線索,false表示指向左右孩子的指針
6     struct BiThrNode *Left_Child,*Right_Child;
7 }BiThrNode,*BiThrTree;

 

2.線索二叉樹結構實現:線索化的實質就是將二叉鏈表中的空指針改為指向前驅和后繼的線索,這種過程在遍歷二叉樹時進行。

 1 BiThrTree Pre;//全局變量,始終指向上一個被訪問過的結點
 2 void InThreading(BiThrTree p){
 3     //中序遍歷進行中序線索化
 4     if(p){
 5         InThreading(p->Left_Child);
 6         if(!p->Left_Child){
 7             p->LTag=true;
 8             p->Left_Child=Pre;
 9         }
10         if(!Pre->Right_Child){
11             Pre->RTag=true;
12             Pre->Right_Child=p;
13         }
14         Pre=p;
15         InThreading(p->Right_Child);
16     }
17 }

 

3.線索二叉樹的遍歷:和雙向鏈表結構一樣,在二叉樹線索鏈表上添加一個頭結點,並令其Left_Child域的指針指向二叉樹的根結點,其Right_Child域的指針指向中序遍歷時訪問的最后一個結點。(以中序線索化為例),同時,令二叉樹的中序序列中的第一個結點的Left_Child域和最后一個結點的Right_Child域指針均指向頭結點。這樣定義的好處就是既可以從第一個結點起順后繼進行遍歷,也可以從最后一個結點開始順前驅進行遍歷。

代碼思路:

    ①:申請一個新的結點p讓他指向二叉樹的根結點,T指向二叉樹的頭結點。

    ②:如果該結點等於二叉樹的頭結點(即中序序列最后一個結點的Right_Child域指針指向的結點) 則結束操作。

    ③:如果p->LTag為true則執行⑤。

    ④:p指向其左子樹並且執行③。

    ⑤:輸出p結點的數據域。

    ⑥:如果p->RTag==false或P的Right_Child與T相等時執行⑨。

    ⑦:p指向其右子樹。 

    ⑧:輸出p結點的數據域並且執行⑥。

    ⑨:讓p指向其右子樹並且執行②。

 1 /* T指向頭結點,頭結點左孩子Left_Child指向二叉樹的根結點,
 2    頭結點右孩子Right_Child指向中序遍歷的最后一個結點。
 3 */
 4 bool InOrderTraverse_The(BiThrTree T){
 5     BiThrTree p;
 6     p=T->Left_Child;//p指向二叉樹的根結點
 7     while(p!=T){    //空樹或遍歷結束時p==T
 8         while(!p->LTag)
 9             p=p->Left_Child;//循環結束時p指向中序序列的下一個結點
10         cout<<p->Elem;
11         while(p->RTag&&p->Right_Child!=T){
12             p=p->Right_Child;
13             cout<<p->Elem;
14         }
15         p=p->Right_Child;//讓p指向其右子樹
16     }
17     return true;
18 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM