数据结构与算法分析(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("此栈为空,必须先创建一个栈!\n");
54     }else{
55         while(!IsEmpty(s)){
56             Pop(s);
57         }
58     }
59 }
60 int main(){
61     printf("请输入10个整数,压入栈中:\n");
62     Stack s;
63     s=creatStack();
64     int temp;
65     int k;
66     for(k=0;k<10;k++){
67         //printf("请输入第%d个数\n",k+1);
68         temp=rand()%100;
69         printf("Num[%d]=%2d  ",k+1,temp);
70         Push(temp,s);
71     }
72     printf("\n下面按照顺序弹出栈中所有元素并输出:\n");
73     for(k=0;k<10;k++){
74       printf("第%2d个出栈的是%4d",k+1,s->Next->Element);
75       if((k+1)%5==0){
76           printf("\n");
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。\n");
 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("栈已经满了!\n");
 47  }else{
 48      strcpy(S->Array[++S->TopOfStack],x);
 49  }
 50 }
 51 //只获得头元素
 52 char *Top(Stack S){
 53     if(isEmpty(S)){
 54         printf("此栈为空,无法取栈头元素!\n");
 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("此栈为空,无法去除栈头元素!\n");
 64     }else{
 65         S->TopOfStack--;
 66     }
 67 }
 68 //获取头元素并删除
 69 char *PopAndTop(Stack S){
 70     if(isEmpty(S)){
 71         printf("此栈为空,无法执行获取栈头元素和去除栈头元素!\n");
 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这个操作符\n",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\n",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\n",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\n",A->Array[i]);
162                         }
163                         strcpy(B->Array[++B->TopOfStack],PopAndTop(temp));
164                         //printf("输出中存的有:%s\n",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\n",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("不知名错误!\n");
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\n",result);
213     }
214 }
215
216 int main(){
217     printf("请输入一个中缀表达式(最多30个字符的字符串),只包含非负整数,+号,-号和()两个括号!\n");
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]!=‘\0‘){
227         flag=-1;
228         while(isNumber(x[i])){
229           temp[++flag]=x[i];
230           temp[flag+1]=‘\0‘;
231           i++;
232         }
233         Push(temp,s_center);
234         //printf("temp=%s\n",temp);
235         while(!isNumber(x[i])){
236               if(x[i]!=‘\0‘){
237               temp[0]=x[i];
238               temp[1]=‘\0‘;
239               i++;
240               Push(temp,s_center);
241                 //printf("temp=%s\n",temp);
242             }else{
243                 break;
244             }
245         }
246     }
247     printf("此中缀表达式为:\n");
248     printStack(s_center);
249     //将中缀表达式转化为后缀表达式
250     turnInto(s_center,s_later);
251     printf("\n这个后缀表达式是:\n");
252     printStack(s_later);
253     printf("\n");
254     calculate(s_later);
255     return 0;
256 } //这里的所有用来输出的被注释掉的语句都是调试用的。

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

3)函数调用

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

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

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

时间: 2024-10-15 20:05:36

数据结构与算法分析(7)表、栈和队列(二)的相关文章

小猪的数据结构辅助教程——3.1 栈与队列中的顺序栈

小猪的数据结构辅助教程--3.1 栈与队列中的顺序栈 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点 1.栈与队列的介绍,栈顶,栈底,入栈,出栈的概念 2.熟悉顺序栈的特点以及存储结构 3.掌握顺序栈的基本操作的实现逻辑 4.掌握顺序栈的经典例子:进制变换的实现逻辑 1.栈与队列的概念: 嗯,本节要进行讲解的就是栈 + 顺序结构 = 顺序栈! 可能大家对栈的概念还是很模糊,我们找个常见的东西来拟物化~ 不知道大家喜欢吃零食不--"桶装薯片"就可以用来演示栈! 生产的时

浅谈算法和数据结构(1):栈和队列

浅谈算法和数据结构(1):栈和队列 2014/11/03 ·  IT技术                                         · 2 评论                                      ·  数据结构, 栈, 算法, 队列 分享到: 60 SegmentFault D-Day 2015 北京:iOS 站 JDBC之“对岸的女孩走过来” CSS深入理解之relative HTML5+CSS3实现春节贺卡 原文出处: 寒江独钓   欢迎分享原创

小猪的数据结构辅助教程——3.2 栈与队列中的链栈

小猪的数据结构辅助教程--3.2 栈与队列中的链栈 标签(空格分隔): 数据结构 1.本节引言: 嗯,本节没有学习路线图哈,因为栈我们一般都用的是顺序栈,链栈还是顺带提一提吧, 栈因为只是栈顶来做插入和删除操作,所以较好的方法是将栈顶放在单链表的头部,栈顶 指针与单链表的头指针合二为一~所以本节只是讲下链栈的存储结构和基本操作! 2.链栈的存储结构与示意图 存储结构: typedef struct StackNode { SElemType data; //存放的数据 struct StackN

数据结构实验之栈与队列二:一般算术表达式转换成后缀式

数据结构实验之栈与队列二:一般算术表达式转换成后缀式 Description 对于一个基于二元运算符的算术表达式,转换为对应的后缀式,并输出之. Input 输入一个算术表达式,以‘#’字符作为结束标志. Output 输出该表达式转换所得到的后缀式. Sample Input a*b+(c-d/e)*f# Output ab*cde/-f*+ #include <stdio.h> #include <stdlib.h> char s[100005]; //分配栈的大小 int m

数据结构和算法分析(9)表栈和队列的实际应用(一)

    在接下来的几篇博文中,将介绍表.栈.队列在编程实践中的应用.     (1)表达式求值:     输入一个中缀表达式,操作符包括(+ - * / ^).转化为后缀表达式之后并计算表达式的值: 要求: 1.输入的中缀表达式必须是一个完整的字符串: 2.不限制数字的位数和正负,负数用()括起来: 代码如下: 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 #define EmptyT

数据结构和算法分析(10)表栈和队列的实际应用(二)

    本节继续介绍表.栈.队列在编程实践中的应用.     (1)行编辑程序:(允许用户输入出差错,并在发现错误时可以及时更正.)     功能:接受用户从终端输入的字符型的数据,并存入用户的数据区.由于不能保证不出差错,因此“每接受一个字符即存入用户数据区”的做法不是最恰当的:较好的做法是,设立一个输入的缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区. 算法原理:当用户发现刚刚键入的一个字符是错的时,可补进一个退格符“#”,以表示前一个字符无效:如果发现当前键入的行内差错较多或者

《数据结构》第3章-栈与队列的学习总结

我之前有接触过栈和队列,当时就觉得很好奇,那是以怎样的存储结构存储数据的呢?拨开重重迷雾,终于学到基础知识了. 学习<栈和队列>有两个星期了,有了前面两个章节的思维基础,我觉得栈和队列学习起来还是很好理解的,通过一些实际应用例子,让我有了更进一步的理解.现在我梳理一下知识,下面总结这一章我所学习到的东西. 一.栈(后进先出:LIFO) 1.顺序栈 这是顺序栈的存储结构: typedef struct { int *base;//栈底指针 int *top; //栈顶指针 int size; /

数据结构 数组,链表,栈,队列理解

数据结构 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合.再简单描述一下:数据结构就是描述对象间逻辑关系的学科. 数据存储结构 常用的数据存储方式有两种:顺序存储,非顺序存储.顺序存储就是把数据存储在一块联系的存储介质(硬盘或内存等)中.反之则是非顺序存储. Java中的数组就是典型的顺序存储,链表就是非顺序存储.数组存储数据时会开辟出一块联系内存,按顺序存储.链表先不会开辟出一块内存来,而是只需要知道下一个节点存储的位置,就能把所以的数据连起来了.所以单向链表的最后一个节点是指向N

纯数据结构Java实现(2/11)(栈与队列)

栈和队列的应用非常多,但其起实现嘛,其实很少人关心. 虽然苹果一直宣传什么最小年龄的编程者,它试图把编程大众化,弱智化,但真正的复杂问题,需要抽丝剥茧的时候,还是要 PRO 人士出场,所以知根知底,实在是必要之举(而非无奈之举). 大门敞开,越往里走越窄,竞争会越激烈. 栈 基本特性 就一条,FILO.但是用在其他复杂数据结构,比如树,或者用在其他应用场景的时候,比如记录调用过程中的变量及其状态等,超有用. 应用举例 比如 撤销操作: 用户每次的录入都会入栈,被系统记录,然后写入文件:但是用户撤

2 限定性线性表——栈与队列

1 栈与队列     1.1 包含min函数的栈 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数 在该栈中,调用min.push和pop方法 要求时间复杂度均为O(1) 算法思想: 要求时间复杂度均为 O(1),增加辅助空间实现,即增加一个辅助栈存储min值 例如:data 中依次入栈 5, 4, 3, 8, 10, 11, 12, 1, 则 min 中依次入栈 5, 4, 3,no,no, no, no, 1. no 代表此次不如栈,如果入栈的元素小于等于 min 中的栈