堆栈的用途:函数调用、递归、回溯算法、表达式求值
例1:计算机如何进行表达式求值?对算术表达式(中缀表达式)(等价于后缀表达式),正确理解:
例2:求后缀表达式?从左往右扫描,先记数,再看其后的运算符,找对应放入数。
由两类对象构成:运算数和运算符号(不同运算符号优先级不一样)
结论:需要有种存储方法,能顺序的存储运算数,并在需要的时候“倒序”输出,此时堆栈就起作用了。
堆栈的抽象数据类型描述
定义:具有一定操作约束的线性表,只在一端(栈顶,Top)做插入(入栈Push)、删除(出栈Pop),后入先出(Last In First Out,LIFO)
数据对象集:一个有0个或多个元素的又穷线性表
操作集:长度为MaxSize的堆栈,堆栈元素
线性表的基本操作(堆栈实现):
- Stack CreateStack(int MaxSize):生成一个最大长度为MaxSize的空堆栈
- int IsFull(Stack S,int MaxSize):判断堆栈S是否已满
- void Push(Stack S,ElementType item):在堆栈中压入item元素
#include <stdio.h> typedef int ElementType; #define MaxSize 100 #define ERROR typedef struct SNode *Stack; struct SNode{ ElementType Data[MaxSize]; int Top; }; void Push(Stack PtrS, ElementType item) { if (PtrS->Top == MaxSize - 1) //判断堆栈是否还能放进去元素 { printf("堆栈满"); return; } else { PtrS->Data[++(PtrS->Top)] = item; //将数据插入到堆栈的上面一个位置 return; } }
- int IsEmpty(Stack S):判断堆栈S是否为空
- ElementType Pop(Stack S):删除并返回栈顶元素
ElementType Pop(Stack PtrS) { if (PtrS->Top == -1) { printf("堆栈空"); //判断堆栈是否为空 return ERROR; //标志错误 } else return (PtrS->Data[(PtrS->Top)--]); //将元素自上而下一个个弹出 }
栈的顺序存储(数组方式)实现:通常由一个一维数组和一个记录栈顶元素位置的变量组成。
例:用一个数组实现两个堆栈,使数组有空间进行入栈操作。(提示:将一个数组分成两个部分,都从左边压入元素,可能会造成一个堆栈满了,另外一个堆栈空着。因此此时将一个数组分成两个部分,分别从两头往中间压入元素,当两个栈头相遇时,表示两个栈满了)
struct DStack{ ElementType Data[MaxSize]; int Top1; //当Top1等于-1时,数组1为空 int Top2; //当Top2等于MaxSize时,数组2为空 }S; void Push(struct DStack *PtrS, ElementType item, int Tag) { if (PtrS->Top2 - PtrS->Top1 == 1){ //判断堆栈是否是满的 printf("堆栈满"); return; } if (Tag == 1) PtrS->Data[++(PtrS->Top1)] = item; //将元素压入第一个数组的后面 else PtrS->Data[--(PtrS->Top2)] = item; //将元素压入第二个数组的前面 } ElementType Pop(struct DStack *PtrS, ElementType item, int Tag) { if (Tag == 1){ if (PtrS->Top1 == -1){ printf("堆栈1为空"); return NULL; } else return PtrS->Data[(PtrS->Top1)--]; //从前往后弹出元素 } else { if (PtrS->Top2 == MaxSize){ printf("堆栈2为空"); return NULL; } else return PtrS->Data[(PtrS->Top2)++]; //从后往前弹出元素 } }
栈的链式存储(链表方式)实现:即单链表,叫链栈。插入和删除操作只能在链栈顶进行。栈顶指针Top应在链表的头部
- 堆栈初始化,建立空栈
#include <stdio.h> #include <stdlib.h> typedef int ElementType; #define MaxSize 100 #define ERROR typedef struct SNode *Stack; struct SNode{ ElementType Data; struct SNode *Next; }; Stack CreateStack() //堆栈初始化,建立空栈 { Stack S; S = (Stack)malloc(sizeof(struct SNode)); S->Next = NULL; return S; } int IsEmpty(Stack S) //判断栈是否为空 { return (S->Next == NULL); }
- 堆栈元素的插入
void Push(ElementType item, Stack S) //插入元素 { struct SNode *TmpCell; TmpCell = (struct SNode *)malloc(sizeof(struct SNode)); TmpCell->Data = item; TmpCell->Next = S->Next; S->Next = TmpCell; }
- 堆栈元素的删除
ElementType Pop(Stack S) { struct SNode *FirstCell; ElementType TopElem; if (IsEmpty(S)){ printf("堆栈空"); return NULL; } else{ FirstCell = S->Next; S->Next = FirstCell->Next; TopElem = FirstCell->Data; free(FirstCell); return TopElem; } }
堆栈的应用:中缀表达式求值。首先将中缀表达式转换为后缀表达式,从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。
转换法则:
1.运算数:直接输入
2.左括号:压入堆栈
3.右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出)
4.运算符:如果优先级大于栈顶运算符时,则把他压栈;
如果优先级小于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,知道该运算符大于栈顶运算符优先级为止
5.如果各对象处理完毕,则把堆栈中存留的运算符一并输出。