数据结构学习笔记5(栈)

来源:互联网 发布:seo公司usseo 编辑:程序博客网 时间:2024/06/05 14:45

一 栈

在学习汇编语言时候曾经简要接触过栈。栈可能是继数组之后在技术安吉科学中最基本的数据结构。基本思想是先进先出,可以看做是一个桶。最先进入的最后出,最后进的最先出。

栈可以用链表或者数组实现。数组实现的缺点在于需要提前预知栈的大小,并定义出来;但是,一般在应用程序中,即使有相当多的栈操作,在任意时刻站元素的实际个数也不会太大。链表可以节省潜在的空间,但是链表实现需要空间定义指针,并且消耗时间来malloc和free操作。两种实现方法原理相同,不过链表实现稍显麻烦。这里给出数组的实现方法。

1数组方法:

栈有一个TopOfStack,对于空栈它是-1,相当于栈的初始化。PUSH X就是讲TopOfStack加1,然后Stack[TopOfStack]=X,Stack代表具体的数组。POP X就是返回Stack[TopOfStack],然后TopOfStack减1。(Stack就是一个数组,只不过人为操作使得Stack有了“栈”这个洋气的名字)。

影响栈的执行效率的问题是 错误检测。如对空栈的POP和对满栈PUSH都会超出数组界限。但是对这些条件的检测在数组实现过程中可能花费实际栈操作的时间。所以除非处理及重要的场合(如OS),一般栈例程中省去错误检测。栈操作几乎花费常数时间,程序的运行时间也不集中于此。在应用程序中,应该随时编写错误检测的代码

#include <stdio.h>#include <STDLIB.H>#include <string.h>#include "stack_h.h"struct StackRecord{ int Capacity; int TopOfStack; ElementType *Array;};#define  EmptyTos -1   //没有; ,语法小错害人不浅啊#define  MinStackSize 5//Creat a new stackStack CreatStack(int MaxElements){ if(MaxElements<MinStackSize) return NULL;//  FatalError("MaxElements is too small!");//这里如果大小不满足,就不需要S,所以放在第一步 Stack S; S=(Stack)malloc(sizeof(struct StackRecord));//!!这一步相当于对指针的内存分配 if (S==NULL);  //FatalError("Out of space!");   S->Array=(ElementType *)malloc(MaxElements*sizeof(ElementType)); if (S->Array==NULL);   //FatalError("Out of space!");//出错提示  S->Capacity=MaxElements; MakeEmpty(S); return S;}int IsEmpty(Stack S){ return S->TopOfStack==EmptyTos;}int IsFull(Stack S){ return S->Capacity==S->TopOfStack;}void DisposeStack(Stack S){ if (!IsEmpty(S)) {  free(S->Array);//这里不需要定义中间变量来free数据,直接把数组内容free掉  free(S);//把栈free掉,栈S就被释放了,只作为变量存在,没有相应的内存空间 }}void MakeEmpty(Stack S){ if(IsEmpty(S))  return; S->TopOfStack=EmptyTos;//栈顶直接等于-1就是空栈,不用free也不用赋值为NULL}void Push(Stack S,ElementType X){ if(IsFull(S))  return;  //FatalError("Out of space!"); else  S->Array[++S->TopOfStack]=X; }ElementType Top(Stack S){ if(IsEmpty(S)) {    /*FatalError("Empty Stack!");*/  return 0; } return S->Array[S->TopOfStack];//只返回,不弹出}void Pop(Stack S){ /*if(IsEmpty(S)); {    FatalError("Empty Stack!");   }*/ S->TopOfStack--;}ElementType PopAndTop(Stack S){ /*if(IsEmpty(S)) {    FatalError("Empty Stack!");  return 0; }*/ return S->Array[S->TopOfStack--];//返回;栈S的坐标改变}


二平衡符号

现在编程VC++6.0和VS都有自动检测功能,但是我们可以自己写一个程序来检查自己的程序文件在小的语法错误比如()[]{}配对上是否正确无误。

基本思想是:设置一个数组栈,大小可以定为10;读入程序文件(这个可以参考K&R的《C程序设计语言》),然后对遇到的所有{  [  (  进行入栈操作,然后碰到  )  ]   } 就对栈里的字符PopAndTop进行比较,如果配对那么对文件接着往下遍历,如果不配对,程序设计就错了。如果碰到  )  ]   } 时候栈空,报错;如果读到文件结尾时,栈非空,报错。

三.1中缀表达式后缀表达式 作业3.20

貌似这个比较厉害?

前缀、后缀表达式是中缀表达式的一种变形,其中如果中缀表达式是a+b*c+(d*e+f)/g那么后缀表达式就是abc*+de*f+g/+。易知,中缀表达式是常用的表达式,后缀表达式则显得比较“怪异”,但是可以看到后缀表达式中没有()这种运算符。但是,计算机时按照后缀表达式来进行计算的(前缀表达式是从右向左扫描,不常用)。

中缀到后缀转换几条守则:1读到一个操作数时,输出;读到 + - * / ( 操作符时放入栈中;2读到 ) 时弹出栈中的元素并输出直到弹出(,两个符号都不输出;3遇到除 ( 以外的运算符则比较栈顶运算符的优先级 * /> + -,如果栈中运算符的等级高于或等于该运算符,则弹出栈中的运算符并将,如遇到+则弹出/ * - +并将 + 入栈。除非遇到 ) ,否则不从栈中移走 ( 。4在读到输入的末尾,将占元素弹出知道该栈变空。

a+b*c+(d*e+f)/g表达式,

a输出  +入栈  b输出  *入栈  c输出(此时栈中是+*,输出是ab),遇到+优先级最低,输出 * +,即abc*+,此时栈中还有一个刚刚入栈的+;

然后 ( 入栈  d输出   *入栈  e输出  ,遇到+,*输出+入栈,此时输出是abc*+de*,栈中是+(+;

然后 f 输出 ,遇到 ) ,+ 输出,()消去,则输出是abc*+de*f+,栈中是+;

然后/入栈,g输出,到结尾,/+依次输出,即为abc*+de*f+g/+

后缀表达式转中缀表达式,见到一个数把它入栈,遇到一个运算法则弹出两个数进行计算并将计算结果入栈。如6523+8*+3+*就是6523入栈,遇到+计算2+3得到5入栈655,8入栈遇到*则8*5得40入栈6 5 40遇到+得6 45,3入栈遇+得6 48遇*得6*48=288。可以手工写出计算式为((2+3)*8+5+3)*6 

int main(void){ Stack S; //S=(Stack)malloc(sizeof(struct StackRecord)); char Expre[]="a+b*c-(d*e+f)/g";  char char_Exp; int i,n=sizeof(Expre)/sizeof(char); S=CreatStack(10); for (i=0;i<n;i++) {  if (Expre[i]<='z'&&Expre[i]>='a'||(Expre[i]<='9'&&Expre[i]>='0'))  {   putchar(Expre[i]);  }   else  {   switch (Expre[i])   {case '+':case '-': while(Top(S)!='('&&S->TopOfStack!=-1&&Top(S)!='(')//验证(,这里需要验证(,脑袋中应该牢记各种条件  putchar(PopAndTop(S)); Push(S,Expre[i]); break; case '*':case '/': while((Top(S)=='*'||Top(S)=='/')&&S->TopOfStack!=-1&&Top(S)!='(')  putchar(PopAndTop(S)); Push(S,Expre[i]); break; case '(':Push(S,Expre[i]);break;//!!!!!!!!!!!!!!!case ')': while(Top(S)!='('&&S->TopOfStack!=-1)  putchar(PopAndTop(S)); Pop(S); break; default:break;   }     } } while(S->TopOfStack!=-1)  putchar(PopAndTop(S)); return 0;}

三.2后缀表达式中缀表达式

 这里的问题在于如何识别后缀表达式中的数字是个位数还是一个大于10的数。后缀表达式如果用字符串表示,那么就不存在大于10的数。也可以在字符串中加入特殊的符号来说明此处是一个三位数etc.,但是鉴于目的不在此,就利用书上例子6523+8*+3+*来写出实现计算的代码。

...

四 函数调用、递归与栈

这一部分主要意思是讲在函数调用中,原来的局部变量和调用函数的地址等活动记录需要存起来。存哪里呢?栈(stack)。但是如果栈太大,可能与程序覆盖,可能存在冲毁栈的风险,程序奔溃。
失控递归(通常是忘记基准情形)会跃出栈空间。另外,当使用嵌套很多的递归时,会存在这个风险。因为每一个递归调用的活动记录都要存起来。
如果是尾递归(在程序最后一行使用递归),系统将自动转换为goto语句并在其前加上对函数每个参数的赋值语句。

0 0