数据结构 -- 栈ADT
栈模型
栈(又叫LIFO(后进先出)表)是限制插入和删除只能在一个位置上进行的表, 该位置是表的末端,叫做顶(top)。对栈的基本操作有Push(进栈)和Pop(出栈), 前者相当于插入,后者是删除最后插入的元素。对空栈进行Pop一般视为栈ADT的错误,而当运行Push是空间用尽是一个实现错误,但不是ADT错误,所以普通的请空栈操作与判断栈是否为空的测试都是栈的基本操作,但是,栈基本做的也就是Push和Pop操作了。学习栈的结构最好结合图形来学,画画图,结合基本的数组,指针与链表操作,栈是挺容易的一个数据结构,最重要是学会使用。
栈的实现
栈的实现分链表实现和数组实现。链表实现主要是使用指针,数组实现避免的指针的使用,可能是更流行的策略。
一:栈的链表实现
栈的链表实现是使用单链表,通过在表顶端插入来实现Push,通过删除表顶端元素来实现Pop。Top操作只是考查表的顶端元素并返回它的值,有时候Top与Pop操作合二为一。下面我们来实现这些操作的例程。
1. 类型声明
我们先给出栈ADT链表的类型声明:
struct Node;
typedef struct Node *PtyToNode;
typedef PtyToNode Stack;
int IsEmpty(Stack S); /*判断是否为空*/
Stack CreateStack(); /*创建栈*/
void MakeEmpty(Stack S); /*让栈为空*/
void Push(ElementType X, Stack S); /*入栈*/
ElementType Top(Stack S); /*Top操作*/
void Pop(Stack S); /*出栈*/
struct Node
{
ElementType Element;
PtyToNode Next;
};
2.判断栈是否为空
int IsEmpty(Stack S)
{
return S->next == NULL;
}
3.创建一个空栈,建立一个头结点; MakeEmpty设置栈为空。
Stack CreateStack()
{
Stack S;
S = molloc(sizeof(struct Node));
if(S == NULL)
exit(1);
S->next = NULL;
MakeEmpty(S);
return S;
}
void MakeEmpty(Stack S)
{
if(S == NULL)
exit(1);
else
while(!IsEmpty(S))
Pop(S);
}
4. Push进栈
Push是作为向链表前端进行插入而实现的,记得要判断内存空间是否不足。
void Push(ElementType X, Stack S)
{
PtyToNode TmpCell;
TmpCell = malloc(sizeof(struct Node));
if(TmpCell == NULL)
{
printf("Out of space!!!");
exit(1);
}
else
{
TmpCell->Element = X;
TmpCell->next = S->next;
S->next = TmpCell;
}
}
ElementType Top(Stack S)
{
if(!IsEmpty(S))
return S->next->Element;
else
{
printf("Empty stack");
return 0;
}
}
6.Pop弹出元素
void Pop(Stack S)
{
PtyToNode FirstCell;
if(IsEmpty(S)
{
printf("Empty stack");
exit(1);
}
else
{
FirstCell = S->Next;
S->Next = S->Next->Next;
free(FirstCell);
}
}
二。栈的数组实现
用一个数组实现栈是很简单的,但是需要提前声明一个数组的大小,存在着危害性,但声明一个数组足够大而不至于浪费太多的空间通常没什么困难,真的要节省空间就用链表实现。每一个栈有一个TopOfStack,空栈它是-1,进栈加1,然后置Stack[TopOfStack] = X; 出栈置返回值为Stack[TopOfStack],然后TopOfStack减1。下面来用数组实现:
1. 顺序栈的声明
struct StackRecord;
typedef struct StackRecord *Stack;
int IsEmpty(Stack S);
int IsFull(Stack S);
Stack CreateStack(int MaxElements);
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;
};
Stack Create(int MaxElements)
{
Stack S;
if(MaxElements < MinStackSize)
{
printf("Stack size is too small");
exit(1);
}
S = malloc(sizeof(struct StackRecord));
if(S == NULL)
{
printf("Out of space!!!");
exit(1);
}
S->Array = malloc(sizeof(ElementType) * MaxElements);
if(S->Array == null)
{
printf("Out of space!!!");
exit(1);
}
S->Capacity = MaxElements;
MakeEmpty(S);
return S;
}
3.判断顺序栈是否为空
int IsEmpty(Stack S)
{
return S->TopOfStack == EmptyTOS;
}
void MakeEmpty(Stack S)
{
S->TopOfStack = EmptyTOS;
}
void Push(ElementType X, Stack S)
{
if(IsFull(S))
{
printf("Full Stack");
exit(1);
}
else
S->Array[++S->TopOfStack] = X;
}
6.返回栈顶元素
ElementType Top(Stack S)
{
if(!IsEmpty(S))
return S->Array[S->TopOfStack];
else
{
printf("Empty stack");
return 0;
}
}
7.出栈
void Pop(Stack S)
{
if(IsEmpty(S))
{
printf("Empty stack");
exit(1);
}
else
S->TopOfStack--;
}
ElementType TopAndPop(Stack S)
{
if(!IsEmpty(S))
return S->Array[S->TopOfStack--];
else
{
printf("Empty stack");
return 0;
}
}
栈的应用
1。平衡符号
编译器会因为你的程序缺少一个符号(如遗漏了一个花括号)而引起编译器列出上百行的测断,而真正的错误却没有找到。于是我们要有一个工具,让每一个括号自动配对,这些实现就要用到栈。做一个空栈,读入字符直到文件尾,如果是开放符号,将其进栈;如果是封闭符号,则当栈空时报错,否则将栈元素弹出。如果弹出的符号是对应的开放符号,则报错。在文件尾,如果栈非空则报错。
2.中缀表达式到后缀表达式的转换
栈不仅可以用来计算后缀表达式的值,而且可以用栈来将中缀表达式到后缀表达式的转换
如: 中缀表达式:a + b * c + (d *e+ f) * g 转为:
后缀表达式:a b c * + d e * f + g * +
算法:1.设置一个空栈
2.从做左到右读取中缀表达式,当读入一个操作数时,立即将它放到输出中
3.当读入运算符,则将该运算符与当前的栈顶元素进行比较,当该运算符优先级大于栈顶运算符的优先级,则将该运算符放入栈中;否则,将栈的元素弹出直到栈顶元素优先级低于该运算符
4.如果是左括号,直接进栈,如果是右括号,将栈的元素弹出直到遇到响应的左括号为止。
5.重复上述步骤,直到处理完所有字符。
3.十进制转换为d进制
将用十进制值除以d,保留其余数,即让余数进栈,重复操作指导该十进制数值为0为止。最后将所有的余数反向输出就得到对应的d进制。
N = (N div d) * d + N mod d (其中div为整除运算,mod为取余运算)
4. 迷宫问题求解
从迷宫入口开始,沿某一方向进行探索,若有通路则继续前进,没有通路则原路返回,从另外方向在进行探索,知道所有可能的道路都被探索完为止。利用栈来保存探索的路径,从而回溯出迷宫的道路。
5. 函数调用
一个函数调用时,需要存储所用的重要信息,以抽象方式来存在“一张纸”上,并置于堆的顶部,当进行其他的函数调用,遵循同样的过程。当该函数要返回时,它查看堆顶部的那张“纸”并复原所有的寄存器,然后进行返回转移。这些工作均可由一个栈完成。