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 }
