數據結構和算法分析(11)樹的簡介


    對於大量的數據,鏈表的線性訪問時間太慢,不宜使用。我們介紹一種簡單的數據結構,其大部分操作的平均時間為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 }


免責聲明!

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



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