對於大量的數據,鏈表的線性訪問時間太慢,不宜使用。我們介紹一種簡單的數據結構,其大部分操作的平均時間為O(log N)。
(1)學習目標:
我們將要涉及到的數據結構叫做二叉查找樹(binary search tree)。
我們將要了解如下內容:
1.了解樹是如何用於實現幾個流行的操作系統中的文件系統的; 2.看到樹如何能夠用來計算算術表達式的值; 3.如何指出利用樹支持以O(log N)平均時間進行的各種搜索操作,以及如何細化得到最壞情況時間界O(log N)。我們還將討論當數據被存在磁盤上時如何來實現這些操作。
(2)樹的基礎知識:
樹的遞歸定義:
一棵樹由稱作根(root)的結點r以及0個或多個非空的(子)樹T1,T2,T3......組成,這些子樹中每一顆的根都來自根r的一條有向邊(Edge)所連接。

2.1樹的實現:
每一個結點的兒子樹不確定時,可以進行如下定義:
typedef struct TreeNode *PtrToNode; struct TreeNode { ElementType Element; PtrToNode FirstChild; PtrToNode NextSibling; }

2.2樹的遍歷及應用:
樹有很多應用,流行的用法之一是包括UNIX,VAX,VAX/VMS和DOS在內的許多常用操作系統中的目錄結構:
在Unix文件系統每個目錄還有一項指該向目錄本身以及另一項指向該目錄的父目錄。因此嚴格來說,UNIX文件系統不是樹,而是類樹。
設我們想要得到目錄中所有文件的名字。我們的輸出格式是:深度為di的文件的名字將被第di此跳格縮進后打印出來:
static void ListDir(DirectoryOrFile D,int Depth) { if(D is a legitimate entry) { PrintName(D,Name); if(D is a directory) for each child,c,of D ListDir(C,Depth+1); } }//算法的核心例程 void ListDirectory(DirectoryOrFile D) { ListDir(D,0); }//驅動例程

輸出文件通常用樹的先序遍歷(preorider traversal),而樹的后續遍歷通常用來計算該樹所有文件占用的磁盤塊的總數。
計算一個目錄大小的例程:
static void SizeDirectory(DirectoryFile D) { int TotalSize; TotalSize=0; if(D is a legitimate Entry) { TotalSize=FileSize(D);//當當前是文件,不是目錄時 if(D is a directory) for(each child,C, of D) TotalSize+=SizeDirectory(C); } return TotalSize; }
(3)二叉樹:
定義:
二叉樹(binary tree)是一棵樹,每一個結點都不能有多於兩個的兒子。

特點:
平均二叉樹的深度要比N小得多,這個性質有時很重要。分析表明,這個平均深度為O(√N),而對於特殊的二叉樹,即二叉查找樹(binary search tree),其深度的平均值是O(log N)。
二叉樹的結點聲明:
typedef struct TreeNode *PtrToNode; typedef struct PtrToNode Tree; struct TreeNode { ElementType Element; Tree left; Tree right; };
(4)二叉樹的應用:(表達式樹)
二叉樹有許多與搜索無關的重要應用。它的主要用途之一是在編譯器的設計領域。
1 /***************構造表達式樹***************/ 2 /* 3 1.輸入中綴表達式先轉化為后綴表達式; 4 2.根據后綴表達式構造樹 5 3.分別以中序遍歷法和后序遍歷法遍歷此樹 6 */ 7 #include<stdio.h> 8 #include<stdlib.h> 9 #include<string.h> 10 11 #define EmptyTOS (-1) 12 #define MinStackSize 5 13 14 struct StackRecord{ 15 int Capacity;//能存元素最大量 16 int TopOfStack;//記錄新插入的元素所在數組中的位置 17 char Array[30][5];//字符串數組,每個字符串的大小最多為5 18 }; 19 typedef struct StackRecord *Stack; 20 21 void MakeEmpty(Stack s){ 22 s->TopOfStack=EmptyTOS; 23 } 24 Stack CreateStack(int MaxElement){ 25 Stack s; 26 if(MaxElement<MinStackSize){ 27 printf("要創建的棧太小,應大於5。\n"); 28 exit(0); 29 }else{ 30 s=malloc(sizeof(struct StackRecord)); 31 s->Capacity=MaxElement; 32 MakeEmpty(s); 33 } 34 return s; 35 } 36 //判斷棧是否為空棧 37 int IsEmpty(Stack S){ 38 return S->TopOfStack==EmptyTOS; 39 } 40 //判斷是否滿了,當為1是滿了,為0是表示未滿 41 int IsFull(Stack S){ 42 if(S->TopOfStack+1>=S->Capacity){ 43 return 1; 44 }else 45 return 0; 46 } 47 //壓棧 48 void Push(char *x,Stack S){ 49 if(IsFull(S)){ 50 printf("棧已經滿了!\n"); 51 }else{ 52 strcpy(S->Array[++S->TopOfStack],x); 53 } 54 } 55 //只獲得頭元素 56 char *Top(Stack S){ 57 if(IsEmpty(S)){ 58 printf("此棧為空,無法取棧頭元素!\n"); 59 exit(0); 60 }else{ 61 return S->Array[S->TopOfStack]; 62 } 63 } 64 //只刪除頭元素 65 void Pop(Stack S){ 66 if(IsEmpty(S)){ 67 printf("此棧為空,無法去除棧頭元素!\n"); 68 }else{ 69 S->TopOfStack--; 70 } 71 } 72 //獲取頭元素並刪除 73 char *PopAndTop(Stack S){ 74 if(IsEmpty(S)){ 75 printf("此棧為空,無法執行獲取棧頭元素和去除棧頭元素!\n"); 76 exit(0); 77 }else{ 78 return S->Array[S->TopOfStack--]; 79 } 80 } 81 //釋放棧空間 82 void DisposeStack(Stack s){ 83 if(s!=NULL){ 84 free(s); 85 } 86 } 87 void printStack(Stack s){ 88 int i; 89 for(i=0;i<=s->TopOfStack;i++){ 90 printf("%s ",s->Array[i]); 91 } 92 } 93 //位置是從棧頭開始計算,所以誰小誰離棧頂比較遠 94 int LastPosition(char *p,Stack temp){ 95 int i; 96 if(exist(p,temp)){ 97 for(i=temp->TopOfStack;i>=0;i--){ 98 if(strcmp(temp->Array[i],p)){ 99 return i; 100 } 101 } 102 } 103 else{ 104 printf("臨時棧沒有%s這個操作符\n",p); 105 return -1; 106 } 107 } 108 int IsNumber(char p){ 109 if(p>='0'&&p<='9'){ 110 return 1; 111 }else 112 return 0; 113 } 114 //由於這里允許負數的存在,所以與之前的函數不同 115 int IsOperator(char *p){//這個函數是給turnInto函數使用的 116 //當長度不為1.肯定是數字,不是操作符 117 if(p[1]!='\0'){ 118 return 0; 119 } 120 //當長度為1,且不為從0到9的數時,才是操作符 121 if(IsNumber(p[0])){ 122 return 0; 123 }else 124 return 1; 125 } 126 int exist(char *p,Stack temp){ 127 int i; 128 for(i=0;i<=temp->TopOfStack;i++){ 129 if(strcmp(temp->Array[i],p)==0){ 130 return 1; 131 } 132 } 133 return 0; 134 } 135 //用這個遞歸提取函數明顯比以前用的順序處理要好得多 136 void handleString(char *s,Stack s_center){ 137 //printf("即將操作的字符串%s\n",s); 138 int i=0; 139 int flag=-1;//當遞歸調用這個函數,上一個函數的flag會影響下一個函數的flag值,所以最好每次用時都初始化,而且函數段中每次用flag都最好先賦初值 140 char temp[5]; 141 if(s[0]=='\0'){ 142 return; 143 } 144 if(s[i]=='('&&s[i+1]=='-'){//處理類似1.(-34*76+21)-12或者2.(-43)+76這種形式的表達式字符串 145 flag=0; 146 temp[flag]='-'; 147 i=i+2;//i指到-號后的第一個數字 148 while(IsNumber(s[i])){ 149 temp[++flag]=s[i]; 150 temp[flag+1]='\0'; 151 i++; 152 }//如果數字過后的符號不是右括號,類似1 153 if(s[i]!=')'){ 154 Push("(",s_center); 155 Push(temp,s_center); 156 s=s+i; 157 handleString(s,s_center); 158 }else{//等於右括號,類似2 159 Push(temp,s_center); 160 i++; 161 s=s+i; 162 handleString(s,s_center); 163 } 164 } 165 //如果字符串的開頭是是數字,就將這一串數字壓棧,並將數字后的第一個操作符壓棧 166 else//else不能少,這里是為了在一次調用此函數時只能執行一次操作,他們是互斥關系 167 if(IsNumber(s[i])){ 168 flag=-1; 169 while(IsNumber(s[i])){ 170 temp[++flag]=s[i]; 171 temp[flag+1]='\0'; 172 i++; 173 } 174 Push(temp,s_center); 175 s=s+i; 176 handleString(s,s_center); 177 } 178 else 179 if(!IsNumber(s[0])) { 180 temp[0]=s[0]; 181 temp[1]='\0'; 182 Push(temp,s_center); 183 handleString(s+1,s_center); 184 } 185 } 186 int Max(int a,int b){ 187 return a>b?a:b; 188 } 189 void turnInto(Stack A,Stack B){ 190 Stack s_temp=CreateStack(15); 191 int i;int max;int leftbracketPosition; 192 for(i=0;i<=A->TopOfStack;i++){ 193 //printStack(s_temp);printf("\n"); 194 //如果不是操作符,直接輸出到后綴表達式 195 if(!IsOperator(A->Array[i])){ 196 strcpy(B->Array[++B->TopOfStack],A->Array[i]); 197 //printf("輸出中存的有:%s\n",A->Array[i]); 198 }else{ 199 char c=A->Array[i][0]; 200 //printf("\n操作的字符是第%d個%c\n",i+1,A->Array[i][0]); 201 switch(c){ 202 case '(':{ 203 Push(A->Array[i],s_temp); 204 break; 205 } 206 case ')':{ 207 while(!strcmp( "(",Top(s_temp) )==0){ 208 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 209 } 210 Pop(s_temp); 211 break; 212 } 213 case '+': 214 case '-':{ 215 if(exist("(",s_temp)){//如果存在左括號,將左括號右側全部運算符彈出 216 while(Top(s_temp)[0]!='('){ 217 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 218 } 219 Push(A->Array[i],s_temp); 220 }else{//如果不存在左括號,將棧中所有元素彈出 221 while(!IsEmpty(s_temp)){ 222 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 223 } 224 Push(A->Array[i],s_temp); 225 } 226 break; 227 } 228 case '*': 229 case '/':{ 230 if(IsEmpty(s_temp)){ 231 Push(A->Array[i],s_temp); 232 } 233 else{ 234 if(exist("(",s_temp)){ 235 leftbracketPosition=LastPosition("(",s_temp); 236 if(exist("/",s_temp)||exist("*",s_temp)){ 237 max=Max(LastPosition("/",s_temp),LastPosition("*",s_temp)); 238 if(max>leftbracketPosition){//表明符號在左括號右側 239 while(Top(s_temp)[0]!='*' || Top(s_temp)[0]!='/'){ 240 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 241 } 242 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 243 Push(A->Array[i],s_temp); 244 }else{//符號在左括號左側 245 Push(A->Array[i],s_temp); 246 } 247 }else{//存在左括號,但既不存在乘號也不存在除號,判斷此時有沒有乘方號 248 //如果有乘方號,且乘方號在左括號右側 249 if(exist("^",s_temp)&&(LastPosition("^",s_temp)>leftbracketPosition)){ 250 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 251 Push(A->Array[i],s_temp); 252 }else{//不存在乘方號或者存在乘方號,但乘方號在左括號的左側 253 Push(A->Array[i],s_temp); 254 } 255 } 256 }else{//不存在左括號時,只要臨時棧中有乘號或除號就不可能再有乘方號 257 if(exist("*",s_temp)||exist("/",s_temp)){ 258 while(Top(s_temp)[0]!='*'&&Top(s_temp)[0]!='/'){ 259 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 260 } 261 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 262 Push(A->Array[i],s_temp); 263 }else{//表示既沒有左括號也沒有乘號或除號,此時考慮棧中有沒有乘方號 264 if(exist("^",s_temp)){//如果有乘方號,肯定在棧頂 265 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 266 Push(A->Array[i],s_temp); 267 }else{ 268 Push(A->Array[i],s_temp); 269 } 270 }//不存在*或/的結束 271 }//不存在左括號的結束 272 } 273 break; 274 } 275 case '^':{ 276 if(IsEmpty(s_temp)){ 277 Push(A->Array[i],s_temp); 278 }else{ 279 //最高優先級,但臨時棧中有可能還有乘方號,要把臨時棧中與當前操作符有相同優先級的並在左括號的右邊的符號彈出 280 if(!exist("^",s_temp)){ 281 strcpy(s_temp->Array[++s_temp->TopOfStack],A->Array[i]); 282 break; 283 }else{ 284 if(exist("(",s_temp)&& ( LastPosition("(",s_temp)<LastPosition("^",s_temp) ) ){ 285 while(Top(s_temp)[0]!='^'){ 286 //printf("%s",Top(temp)); 287 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 288 //printf("輸出中存的有:%s\n",A->Array[i]); 289 } 290 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 291 //printf("輸出中存的有:%s\n",B->Array[B->TopOfStack]); 292 Push(A->Array[i],s_temp); 293 //printStack(temp); 294 }else{//包括不存在左括號和存在左括號且左括號在^右邊的情況 295 if(!exist("(",s_temp)){ 296 strcpy( B->Array[++B->TopOfStack],PopAndTop(s_temp) ); 297 Push(A->Array[i],s_temp); 298 }else 299 strcpy(s_temp->Array[++s_temp->TopOfStack],A->Array[i]); 300 } 301 } 302 } 303 break; 304 } 305 default:printf("未知錯誤\n"); 306 }//switch語句的結束 307 }//如果是操作符的結束 308 }//for循環結束 309 while(!IsEmpty(s_temp)){ 310 strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp)); 311 } 312 } 313 //利用斐波那契數快速求冪 314 unsigned long FB_QiuMi(int x,int n){ 315 if(n<=2){ 316 //printf("不用求冪公式!\n"); 317 switch(n){ 318 case 1:return x; 319 case 2:return x*x; 320 } 321 } 322 //記錄斐波那契數列的下標 323 int N=0; 324 int f=1; 325 int temp; 326 int flag=f+1; 327 while(flag<=n){ 328 temp=flag; 329 flag=flag+f; 330 f=temp; 331 N++; 332 } 333 int less=n-temp; 334 int i; 335 unsigned long result=x; 336 flag=x; 337 for(i=0;i<N;i++){ 338 temp=result; 339 result=result*flag; 340 flag=temp; 341 } 342 while(less>0){ 343 result=result*x; 344 less--; 345 } 346 return result; 347 } 348 /******后添加的模塊*****/ 349 struct treeNode{ 350 char element[5]; 351 struct treeNode *left; 352 struct treeNode *right; 353 }; 354 typedef struct treeNode *TreePtr; 355 typedef TreePtr Tree;//樹的一個結點 356 struct TreeStackNode{ 357 TreePtr Element;//此棧中的元素用來存指向樹結點的指針 358 struct TreeStackNode *Next; 359 }; 360 typedef struct TreeStackNode *TreeStackPtr; 361 typedef TreeStackPtr TreeStack; 362 TreeStack createTreeStack(){ 363 TreeStack treestack=malloc(sizeof(struct TreeStackNode)); 364 treestack->Next=NULL; 365 return treestack; 366 } 367 //將樹結點的指針壓入棧中 368 void Tree_Push(TreeStack tree_stack,TreePtr treePtr){ 369 TreeStackPtr temp=malloc(sizeof(struct TreeStackNode)); 370 temp->Element=treePtr; 371 //AA:printf("%s",treePtr->element);printf("左子樹是否為空:%d\n",treePtr->left==NULL); 372 temp->Next=tree_stack->Next; 373 tree_stack->Next=temp; 374 } 375 TreeStackPtr Tree_PopandTop(TreeStack tree_stack){ 376 if(tree_stack->Next==NULL){ 377 printf("棧為空"); 378 return NULL; 379 } 380 TreeStackPtr temp=tree_stack->Next; 381 tree_stack->Next=tree_stack->Next->Next; 382 return temp; 383 } 384 void makeTree(Stack s_later,TreeStack treestack){ 385 int i; 386 TreePtr tempTreeNode=NULL; 387 for(i=0;i<=s_later->TopOfStack;i++){ 388 //如果后綴表達式的元素不為操作符,就新建樹結點,並將樹結點的指針壓入指針棧中 389 if(!IsOperator(s_later->Array[i])){ 390 tempTreeNode=malloc(sizeof(struct treeNode)); 391 tempTreeNode->left=NULL;tempTreeNode->right=NULL; 392 strcpy(tempTreeNode->element,s_later->Array[i]); 393 //BB:printf("進棧1個元素"); printf("%s\n",tempTreeNode->element); 394 //AA:printf("%s %d\n",tempTreeNode->element,tempTreeNode->left==NULL); 395 Tree_Push(treestack,tempTreeNode); 396 }else{ 397 tempTreeNode=malloc(sizeof(struct treeNode)); 398 strcpy(tempTreeNode->element,s_later->Array[i]); 399 tempTreeNode->right=Tree_PopandTop(treestack)->Element; 400 tempTreeNode->left=Tree_PopandTop(treestack)->Element; 401 //BB:printf("進棧1個元素"); printf("%s\n",tempTreeNode->element); 402 //AA:printf("%s %d\n",tempTreeNode->element,tempTreeNode->left==NULL); 403 Tree_Push(treestack,tempTreeNode); 404 //BB:printf("出棧2個元素"); printf("進棧1個元素"); 405 } 406 } 407 } 408 void Traverl_Center(Tree tree){ 409 if(tree->left!=NULL){ 410 Traverl_Center(tree->left); 411 } 412 printf(" %s",tree->element); 413 if(tree->right!=NULL){ 414 Traverl_Center(tree->right); 415 } 416 } 417 void Taverl_Later(Tree tree){ 418 if(tree->left!=NULL){ 419 Taverl_Later(tree->left); 420 } 421 if(tree->right!=NULL){ 422 Taverl_Later(tree->right); 423 } 424 printf(" %s",tree->element); 425 } 426 int main(){ 427 printf("請輸入一個中綴表達式:"); 428 printf("(最多40個字符的字符串,可以包含任意整數,+,-,*,/,^和()兩個括號!)\n"); 429 Stack s_center=CreateStack(35); 430 Stack s_later=CreateStack(35); 431 char x[40]; 432 gets(x); 433 char *p=x; 434 handleString(p,s_center); 435 printf("此中綴表達式為:"); 436 printStack(s_center); 437 //將中綴表達式轉化為后綴表達式 438 turnInto(s_center,s_later); 439 printf("\n這個后綴表達式是:"); 440 printStack(s_later);printf("\n"); 441 TreeStack treestack=createTreeStack(); 442 //構造表達式樹 443 makeTree(s_later,treestack); 444 Tree tree=Tree_PopandTop(treestack)->Element; 445 //中序遍歷和后序遍歷樹 446 printf("------------------------------樹形結構下的輸出------------------------------\n"); 447 printf("此中綴表達式為:"); Traverl_Center(tree); 448 printf("\n這個后綴表達式是:");printf("\b");Taverl_Later(tree);printf("\n"); 449 return 0; 450 }

