zoukankan      html  css  js  c++  java
  • 数据结构和算法分析(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。
    ");
     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("栈已经满了!
    ");
     51  }else{
     52      strcpy(S->Array[++S->TopOfStack],x);
     53  }     
     54 }
     55 //只获得头元素 
     56 char *Top(Stack S){
     57     if(IsEmpty(S)){
     58         printf("此栈为空,无法取栈头元素!
    ");
     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("此栈为空,无法去除栈头元素!
    ");
     68     }else{
     69         S->TopOfStack--; 
     70     }
     71 }
     72 //获取头元素并删除 
     73 char *PopAndTop(Stack S){
     74     if(IsEmpty(S)){
     75         printf("此栈为空,无法执行获取栈头元素和去除栈头元素!
    ");
     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这个操作符
    ",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]!=''){
    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
    ",s);
    138     int i=0;
    139     int flag=-1;//当递归调用这个函数,上一个函数的flag会影响下一个函数的flag值,所以最好每次用时都初始化,而且函数段中每次用flag都最好先赋初值 
    140     char temp[5];
    141     if(s[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]='';
    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]='';
    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]='';
    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("
    ");
    194       //如果不是操作符,直接输出到后缀表达式 
    195       if(!IsOperator(A->Array[i])){
    196         strcpy(B->Array[++B->TopOfStack],A->Array[i]);
    197         //printf("输出中存的有:%s
    ",A->Array[i]);
    198       }else{
    199         char c=A->Array[i][0];
    200         //printf("
    操作的字符是第%d个%c
    ",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
    ",A->Array[i]);
    289                             }
    290                             strcpy(B->Array[++B->TopOfStack],PopAndTop(s_temp));
    291                             //printf("输出中存的有:%s
    ",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("未知错误
    ");    
    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("不用求幂公式!
    ");
    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
    ",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
    ",tempTreeNode->element); 
    394             //AA:printf("%s %d
    ",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
    ",tempTreeNode->element);
    402             //AA:printf("%s %d
    ",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个字符的字符串,可以包含任意整数,+,-,*,/,^和()两个括号!)
    ");
    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("
    这个后缀表达式是:");
    440     printStack(s_later);printf("
    ");
    441     TreeStack treestack=createTreeStack();
    442     //构造表达式树
    443     makeTree(s_later,treestack);
    444     Tree tree=Tree_PopandTop(treestack)->Element; 
    445     //中序遍历和后序遍历树
    446     printf("------------------------------树形结构下的输出------------------------------
    ");
    447     printf("此中缀表达式为:"); Traverl_Center(tree);
    448     printf("
    这个后缀表达式是:");printf("");Taverl_Later(tree);printf("
    ");
    449     return 0;
    450 }

  • 相关阅读:
    Redis-安装
    Redis-介绍
    Redis 教程(转)
    C# Redis 帮助类
    sublime text3---Emmet:HTML/CSS代码快速编写神器
    Sublime Text3 Package Control和Emmet插件安装方法
    vs2010音频文件压缩 调用lame_enc.dll将WAV格式转换成MP3
    vs学习过程中遇见的各种问题
    vs2010中添加dll文件
    解决angular11打包报错Type 'Event' is missing the following properties from type 'any[]': ...Type 'Event' is not assignable to type 'string'
  • 原文地址:https://www.cnblogs.com/MenAngel/p/5837717.html
Copyright © 2011-2022 走看看