开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
2.2.3 堆栈的链式存储实现
栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能再链栈的栈顶进行。
栈顶指针Top应该再链表的哪一头?若用单向链表实现一个堆栈,链表的头和尾都可以作为top?
只有链表的头才行。链表的尾空余插入,但是删除就有问题,找不到前面的结点了,因为这是单项链表找不到前面的结点
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)
{
//判断堆栈s是否为空,若为空函数返回整数1,否则返回0
return (S->Next == NULL);
}
void Push(ElementType item,Stack S)
{
//将元素item压入堆栈S
struct SNode *TmpCell;
TmpCell = (struct SNode *)malloc(sizeof(struct SNode));
TmpCell -> Element = item;
TmpCell -> Next = S -> Next;
S -> Next = TmpCell;
}
使用链表来进行Push操作的时候不用判别堆栈满不满的问题,因为链表通过不断申请结点空间往里面插
数组实现堆栈的话,数组大小是固定的,存在着满不满的问题
ElementType Pop(Stack S)
{
//删除并返回堆栈s的栈顶元素
struct SNode *FirstCell;
ElementType TopElem;
if( IsEmpty (S) ){
printf("堆栈空");return NULL;
}else{
FirstCell = S ->Next;
TopElem = FirstCell -> Element;
free(FirstCell);
return TopElem;
}
}
2.2.4 堆栈应用:表达式求值
回忆:应用堆栈实现后缀表达式求值的基本过程:
- 从左到右读入后缀表达式的各项(运算符或运算数);
1.运算数:入栈;
2.运算符:从堆栈中弹出适当数量的运算数,计算并结果入栈;
3.最后,堆栈顶上的元素就是表达式的结果值
中缀表达式求值
基本策略:将中缀表达式转化为后缀表达式,然后求值
如何将中缀表达式转化为后缀?
观察一个简单例子:2+9/3-5 -> 2 9 3 / + 5 -
1.运算数相对顺序不变
2.运算符号顺序发生改变
1.需要存储"等待中"的运算符号
2.要将当前运算符号与"等待中"的最后一个运算符号比较(如果前面的一个运算符号的优先级比我来得高,就说明可以拿来计算,如果优先度比我低,那么当前的运算符号还不能说就直接拿来运算,因为后面可能还有优先级比我高的,所以需要保留起来,这个时候我们就需要一种结构来实现我们运算符号的存储,那结构就是堆栈)
输出:2 9 3
记下:+ /
碰到运算数 我们就把它输出
碰到运算符号 我们等着
有括号怎么办?
【例】a*(b+c)/d = ? a b c + * d /
当括号被丢进堆栈里面的时候,它的优先级降到最低,优先算括号里的内容
算数规则:当遇到同一个优先级的时候,它的顺序是从左到右
T(N) = O(N)
请试试应用堆栈将中缀表达式2*(6/3+4)-5转换为后缀表达式。在这个转换过程中,堆栈元素最多时元素个数是多少?3个
中缀表达式如何转换为后缀表达式
从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。
1.运算数:直接输出
2.左括号:压入堆栈
3.右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
4.运算符:
1.若优先级大于栈顶运算符时,则把它压栈;
2.若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;
5.若各对象处理完毕,则把堆栈中存留的运算符一并输出
堆栈的其他应用:
函数的调用及递归实现
深度优先搜索(图)
回溯算法(老鼠走迷宫案例)等等