• 数据结构与算法分析(7)表、栈和队列(二)


        介绍栈的相关知识:

        (2)栈ADT:

        2.1栈模型:

          栈是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈的顶(top)。对栈的基本操作有Push(进栈)和Pop(出栈),前者相当于插入,后者则是删除最后插入的元素。

          栈有时又叫做LIFO(后进先出表)。一般的栈模型是,存在某个元素位于栈顶,而该元素是唯一可见元素。

          

        2.2栈的实现:

          由于栈是一个表,所以任何实现表的方法都可以实现栈,我们将给出两种方法,一个使用指针,一个使用数组。

          1)栈的链表实现:

          栈的链表实现的例程:

    struct Node;
    typedef struct Node *PtrToNode;
    typedef PtrToNode Stack;
    
    int IsEmpty(Stack s);
    Stack CreateStack(void);
    void DisposeStack(Stack s);
    void MakeEmpty(Stack s);
    void Push(ElementType x,Stack s);
    ElementType Top(Stack s);
    void Pop(Stack s);
    
    struct Node{
      ElementType Element;
      PtrToNode Next;
    };

          栈的链表实现的简单实例:

          编写程序,从键盘输入10个数据放入栈中,然后从栈中依次弹出这些数据,并输出。

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<math.h>
     4  
     5 struct Node{
     6   int Element;
     7   struct Node* Next;      
     8 };
     9 //定义一个指向结点类型变量的指针类型 
    10 typedef struct Node *Stack;
    11 Stack creatStack(){
    12     Stack s;
    13     s=malloc(sizeof(struct Node));
    14     if(s==NULL)
    15       printf("Out of space!");
    16     s->Next=NULL;
    17     return s;
    18 }
    19 void Push(int n,Stack s){
    20     struct Node *temp;
    21     temp=malloc(sizeof(struct Node));
    22     if(temp==NULL)
    23       printf("Out of Space!");
    24     else{
    25         temp->Element=n;
    26         temp->Next=s->Next;
    27         s->Next=temp; 
    28     }
    29 }
    30 int IsEmpty (Stack s){
    31     return s->Next==NULL;
    32 }
    33 int Top(Stack s){
    34     struct Node *temp;
    35     if(IsEmpty(s)){
    36       printf("Empty stack");
    37       return 0;        
    38     }
    39     else
    40     return s->Next->Element;
    41 }
    42 void Pop(Stack s){
    43   struct Node *temp;
    44   if(IsEmpty(s))
    45     printf("Empty stack");
    46   else
    47     temp=s->Next;
    48     s->Next=s->Next->Next;
    49     free(temp);    
    50 }
    51 void MakeEmpty(Stack s){
    52     if(s==NULL){
    53         printf("此栈为空,必须先创建一个栈!
    ");
    54     }else{
    55         while(!IsEmpty(s)){
    56             Pop(s);
    57         }
    58     }
    59 } 
    60 int main(){
    61     printf("请输入10个整数,压入栈中:
    ");
    62     Stack s;
    63     s=creatStack();
    64     int temp;
    65     int k;
    66     for(k=0;k<10;k++){
    67         //printf("请输入第%d个数
    ",k+1);
    68         temp=rand()%100;
    69         printf("Num[%d]=%2d  ",k+1,temp);  
    70         Push(temp,s);   
    71     }    
    72     printf("
    下面按照顺序弹出栈中所有元素并输出:
    ");
    73     for(k=0;k<10;k++){
    74       printf("第%2d个出栈的是%4d",k+1,s->Next->Element);
    75       if((k+1)%5==0){
    76           printf("
    ");
    77       } 
    78       Pop(s);        
    79     }
    80     return 0;
    81 }

          2)栈的数组实现:

          栈的数组实现避免了指针并且可能是更加流行的解决方案。这种策略的唯一潜在危害是我们需要提前声明一个数组的大小。

          栈的数组实现中一个很大的问题是对空栈的Pop和对满栈的Push都将超出数组的界限并引起程序的崩溃,因此有时可能需要防止用于构造栈的数组的下标越界。

          栈的数组实现的例程:

    #include<stdlib.h>
    #include<stdio.h>
    
    struct StackRecord;
    typedef struct StackRecord *Stack;
    typedef int ElementType;
    
    int IsEmpty(Stack s);
    int IsFull(Stack s);
    Stack CreateStack(int MaxElements);
    void  DisposeStack(Stack s);
    void MakeEmpty(Stack s);
    void Push(ElementType x,Stack s);
    ElementType Top(Stack s);
    void Pop(Stack s);
    ElementType TopAndPop(Stack s);
    
    #define EmptyTOS (-1)
    #define MinStackSize (5)
    struct StackRecord{
      int Capacity;
      int TopOfStack;
      ElementType *Array; 
    };
    void MakeEmpty(Stack s){
        s->TopOfStack=EmptyTOS;
    }
    int IsFull(Stack s){
        return s->TopOfStack==s->Capacity;
    }
    Stack CreateStack(int MaxElements){
        Stack s;
        if(MaxElements<MinStackSize){
            printf("所建的栈空间太小");
            MaxElements=5;
        }
        s=malloc(sizeof(struct StackRecord));
        s->Array=malloc(sizeof(ElementType)*MaxElements);
        s->Capacity=MaxElements;
        MakeEmpty(s);    
        return s;
    }
    void DisposeStack(Stack s){
        if(s!=NULL){
            free(s->Array);
            free(s);
        }
    }
    int IsEmpty(Stack s){
        return s->TopOfStack==EmptyTOS;
    }
    void Push(ElementType x,Stack s){
        if(!IsFull(s)){
          printf("栈空间已经用完了");
        }
        else
          s->Array[++s->TopOfStack]=x;
    }
    ElementType Top(Stack s){
        if(!IsEmpty(s))
          return s->Array[s->TopOfStack];
        printf("空栈");
        return 0;
    }
    void Pop(Stack s){
        if(IsEmpty(s))
            printf("空栈");
        else
            s->TopOfStack--;    
    }
    ElementType    TopAndPop(Stack s){
        if(!IsEmpty(s))
          return s->Array[s->TopOfStack--];
        printf("空栈");
        return 0;
    }

          栈的数组实现的实例在此就不做介绍了,下面的后缀表达式的实例涉及到了。

        2.4栈的应用: 

          1)平衡符号:

            编译器检查你的程序的语法错误,但是常常由于缺少一个符号(如遗漏一个花括号或者注释起始符)引起编译器列出上百行的诊断,而真正的错误却没有找出。

            在这种情况下一个有用的工具就是检验是否每件事情都能成对出现的一个程序:

            做一个空栈。读入字符指到文件尾,如果字符是一个开放的符号,则将推入栈中。如果是一个封闭符号,则当栈为空时报错,否则将栈元素推出。在文件尾,如果栈非空则报错。

          2)后缀表达式:

           主要包括后缀表达式的计算中缀到后缀的转换:  

       编写程序,从键盘输入一个中缀表达式,利用栈先转换为后缀表达式,然后计算该后缀表达式的结果,要求

      (1)用栈的数组实现完成

      (2)为简单起见,表达式中的操作数为非负整数

      (3)操作符仅包括加(+)、乘(*、左括号(、右括号)

      (4)若后缀表达式正确,输出计算结果;若表达式有误,输出错误提示信息

      1 #include<stdio.h>
      2 #include<stdlib.h>
      3 #include<string.h>
      4  
      5 #define EmptyTOS (-1)
      6 #define MinStackSize 5
      7  
      8 struct StackRecord{
      9   int Capacity;//能存元素最大量
     10   int TopOfStack;//记录新插入的元素所在数组中的位置
     11   char Array[20][5];//字符串数组,每个字符串的大小最多为5 
     12 };
     13 typedef struct StackRecord *Stack;
     14 
     15 void MakeEmpty(Stack s){
     16     s->TopOfStack=EmptyTOS;
     17 }
     18 
     19 Stack CreateStack(int MaxElement){
     20     Stack s;  
     21     if(MaxElement<MinStackSize){
     22         printf("要创建的栈太小,应大于5。
    ");
     23         exit(0); 
     24     }else{        
     25       s=malloc(sizeof(struct StackRecord));
     26       s->Capacity=MaxElement;
     27       MakeEmpty(s);    
     28     }
     29     return s; 
     30 }
     31 
     32 //判断栈是否为空栈 
     33 int isEmpty(Stack S){
     34     return S->TopOfStack==EmptyTOS; 
     35 }
     36 //判断是否满了,当为1是满了,为0是表示未满 
     37 int IsFull(Stack S){
     38     if(S->TopOfStack+1>=S->Capacity){
     39         return 1;
     40     }else
     41     return 0;
     42 }
     43 //压栈 
     44 void Push(char *x,Stack S){
     45  if(IsFull(S)){
     46      printf("栈已经满了!
    ");
     47  }else{
     48      strcpy(S->Array[++S->TopOfStack],x);
     49  }     
     50 }
     51 //只获得头元素 
     52 char *Top(Stack S){
     53     if(isEmpty(S)){
     54         printf("此栈为空,无法取栈头元素!
    ");
     55         exit(0);
     56     }else{
     57         return S->Array[S->TopOfStack]; 
     58     }
     59 }
     60 //只删除头元素 
     61 void Pop(Stack S){
     62     if(isEmpty(S)){
     63         printf("此栈为空,无法去除栈头元素!
    ");
     64     }else{
     65         S->TopOfStack--; 
     66     }
     67 }
     68 //获取头元素并删除 
     69 char *PopAndTop(Stack S){
     70     if(isEmpty(S)){
     71         printf("此栈为空,无法执行获取栈头元素和去除栈头元素!
    ");
     72         exit(0);
     73     }else{
     74         return S->Array[S->TopOfStack--];
     75     }
     76 }
     77 //释放栈空间 
     78 void DisposeStack(Stack s){
     79     if(s!=NULL){
     80         free(s); 
     81     }
     82 } 
     83 void printStack(Stack s){
     84     int i;
     85     for(i=0;i<=s->TopOfStack;i++){
     86         printf("%s ",s->Array[i]);
     87     }    
     88 }
     89 
     90 int isNumber(char p){
     91     if(p>='0'&&p<='9'){
     92       return 1;    
     93     }else
     94     return 0;
     95 }
     96 int isOperator(char *p){     
     97     //当长度为1,且不为从0到9的数时,才是操作符 
     98     if(isNumber(p[0])){
     99         return 0;
    100     }else
    101     return 1; 
    102 } 
    103 int exist(char *p,Stack temp){
    104     int i;
    105     for(i=0;i<=temp->TopOfStack;i++){
    106         if(strcmp(temp->Array[i],p)==0){
    107             return 1;
    108         }
    109     }
    110     return 0;
    111 }
    112 //位置是从栈头开始计算,所以谁小谁离栈比较远 
    113 int LastPosition(char *p,Stack temp){
    114     int i;
    115     if(exist(p,temp)){
    116       for(i=temp->TopOfStack;i>=0;i--){
    117         if(strcmp(temp->Array[i],p)){
    118             return i;
    119         }
    120       }    
    121     }
    122     else{
    123       //这个应该执行不到 
    124       //printf("临时栈没有%s这个操作符
    ",p); 
    125     }
    126 }
    127 void turnInto(Stack A,Stack B){
    128     Stack temp=CreateStack(10);
    129     int i;
    130     for(i=0;i<=A->TopOfStack;i++){
    131         //如果不是操作符,直接输出到后缀表达式 
    132         if(!isOperator(A->Array[i])){
    133             strcpy(B->Array[++B->TopOfStack],A->Array[i]);
    134             //printf("输出中存的有:%s
    ",A->Array[i]);
    135         }else{
    136             char c=A->Array[i][0];
    137         //当是操作符时,有可能是右括号 
    138             if(c==')'){
    139                 //printStack(temp);
    140                 //当两个字符串不相等时 
    141                 while(!strcmp( "(",Top(temp) )==0){
    142                   strcpy(B->Array[++B->TopOfStack],PopAndTop(temp));
    143                   //printf("输出中存的有:%s
    ",B->Array[B->TopOfStack]);    
    144                 }
    145                 Pop(temp);
    146             }else if(c=='('){
    147                 //Push("(",temp);
    148                 //printf("%s",A->Array[i]);
    149                 Push(A->Array[i],temp);
    150             }else if(c=='+'){
    151                 //如果临时栈中有乘号
    152                 if(exist("*",temp)){
    153                     //printStack(temp); 
    154                     //当没有左括号或者有,但左括号在乘号的左边 
    155                     if( !exist("(",temp) || ( exist("(",temp)&& LastPosition("(",temp)>LastPosition("*",temp) ) ){
    156                         //printf("%d<%d",LastPosition("(",temp),LastPosition("*",temp));
    157                         //将乘号及以后的操作符输出到后缀表达式 
    158                         while(Top(temp)[0]!='*'){
    159                           //printf("%s",Top(temp));
    160                           strcpy( B->Array[++B->TopOfStack],PopAndTop(temp) );
    161                           //printf("输出中存的有:%s
    ",A->Array[i]);
    162                         }
    163                         strcpy(B->Array[++B->TopOfStack],PopAndTop(temp));
    164                         //printf("输出中存的有:%s
    ",B->Array[B->TopOfStack]);
    165                         Push(A->Array[i],temp);
    166                         //printStack(temp); 
    167                     }else
    168                     Push(A->Array[i],temp);
    169                 }
    170                 else{
    171                   //如果不存在乘号 
    172                   Push(A->Array[i],temp);    
    173                 }                
    174            }else if(c=='*'){
    175             strcpy(temp->Array[++temp->TopOfStack],A->Array[i]);    
    176            }            
    177         }
    178     }
    179     while(!isEmpty(temp)){
    180         strcpy(B->Array[++B->TopOfStack],PopAndTop(temp)); 
    181     }    
    182 }
    183 
    184 void calculate(Stack A){
    185     //定义一个辅助计算后缀表达式的栈 
    186     Stack as=CreateStack(10);
    187     int result,i;
    188     int a,b,temp;
    189     char str[5];
    190     for(i=0;i<=A->TopOfStack;i++){
    191         //printf("%s
    ",A->Array[i]);
    192         if(isNumber(A->Array[i][0])){
    193             Push(A->Array[i],as);
    194         }else{
    195             int a=atoi(PopAndTop(as));
    196             int b=atoi(PopAndTop(as));
    197             char c=A->Array[i][0];
    198             //putchar(c);
    199             switch(c){
    200                 case '+':temp=a+b;break;
    201                 case '*':temp=a*b;break;
    202                 default:printf("不知名错误!
    ");
    203             }
    204             itoa(temp,str,10);
    205             Push(str,as);
    206          }    
    207     }
    208 
    209     if(as->TopOfStack<=0){
    210         strcpy(str,Top(as));
    211         result=atoi(str);
    212         printf("计算的结果为:%3d
    ",result); 
    213     }
    214 }
    215 
    216 int main(){
    217     printf("请输入一个中缀表达式(最多30个字符的字符串),只包含非负整数,+号,-号和()两个括号!
    ");
    218     Stack s_center=CreateStack(20);
    219     Stack s_later=CreateStack(20); 
    220     char x[30];
    221     gets(x);
    222     int i=0;
    223     //用于提取有多个位数的数字,或操作符 
    224     char temp[5];
    225     int flag; 
    226     while(x[i]!=''){
    227         flag=-1;
    228         while(isNumber(x[i])){
    229           temp[++flag]=x[i];
    230           temp[flag+1]='';
    231           i++;
    232         }
    233         Push(temp,s_center);
    234         //printf("temp=%s
    ",temp); 
    235         while(!isNumber(x[i])){
    236               if(x[i]!=''){
    237               temp[0]=x[i];
    238               temp[1]='';
    239               i++;
    240               Push(temp,s_center);    
    241                 //printf("temp=%s
    ",temp);
    242             }else{
    243                 break;
    244             }    
    245         } 
    246     }
    247     printf("此中缀表达式为:
    "); 
    248     printStack(s_center);
    249     //将中缀表达式转化为后缀表达式 
    250     turnInto(s_center,s_later);
    251     printf("
    这个后缀表达式是:
    ");
    252     printStack(s_later);
    253     printf("
    ");
    254     calculate(s_later);
    255     return 0;
    256 } //这里的所有用来输出的被注释掉的语句都是调试用的。

          这里只是简单的介绍包括+,*,(,)的运算,以后还会将四则运算全部解决,敬请期待。

          3)函数调用

          检测平衡符号的算法提供一种实现函数调用的方法。当主调函数调用被调函数时,系统需要将主调例程中的所有局部变量存起来以防止被被调函数的变量覆盖,而且还要记录主调函数的位置,以便被掉函数运行完时知道向哪里转移。在这里函数调用和函数返回就类似与开括号和闭括号。

          函数调用问题的过程所有的工作都可以由一个栈来完成,所存储的信息或称为活动记录(activation record),或叫做栈帧(stack frame)。

          在这里我们简要了解,如果以后的工作需要用到单片机,汇编语言,计算机组成原理和编译原理时,我会深入探讨这个问题。

          

  • 相关阅读:
    Common Lisp 参数传递的几种形式
    EF架构~基于EF数据层的实现
    标准架构~业务层到底是否应该关注数据持久化的方式
    Redis学习笔记~Redis主从服务器,读写分离
    Redis学习笔记~是时候为Redis实现一个仓储了,RedisRepository来了
    MVVM架构~knockoutjs系列之数组的$index和$data
    【Oracle】-【sqlplus / as sysdba登录报错问题】-新用户使用sqlplus / as sysdba登录报错
    Android---App Widget(一)
    Mit 分布式系统导论,Distributed Systems ,lab1 -lab6 总结,实验一到实验六总结
    GDAL切割重采样遥感图像
  • 原文地址:https://www.cnblogs.com/MenAngel/p/5512930.html
Copyright © 2020-2023  润新知