迷茫,是青春最真实的状态;但奋斗,才是青春的主基调;努力是打败焦虑的绝好方法!
堆栈定义以及基本运算
堆栈: 堆栈,重点在栈,是限制在表的一端进行插入和删除的线性表。允许插入、删除的这一端称为栈顶,另一个固定端称为栈底。
特点:后入先出,先入后出。适用于正序输入,逆序输出。
栈又称为后进先出的线性表,简称LIFO表。
栈的基本运算
- 构造栈:Init_Stack
- 判空栈:Empty_Stack
- 压入栈:Push_Stack
- 压出栈:Pop_Stack
- 读栈顶元素:Top_Stack
基本运算的具体实现
顺序栈
- 置空栈
Stack *Init_Stack()
{ Stack *s;
s = (Stack*)malloc(sizeof(Stack)); //申请栈的空间
s -> top = -1; //初始化栈顶指针
return s;
}
- 判栈空
int Empty_Stack(Stack *s)
{if(s->top==-1) return 1;
else return 0;
}
- 入栈
int Push_Stack(Stack *s,datatype x)
{if(s->top==MAXSIZE-1) return 0; //栈满不能入栈
else{ s->top++;
s->data[s->top] = x;
return 1;
}
}
- 出栈
int Pop_Stack(Stack *s,datatype *x)
{if(Empty_Stack(s)) return 0; //栈空不能出栈
else{*x=s->data[s->top]; //保存栈顶元素
s->top--; //更新栈顶指针
return 1;
}
}
- 取栈顶元素
int Top_Stack(Stack *s,datatype *x)
{if(Empty_Stack(s)) return 0; //栈空无元素
else {*x = s->data[s->top]; //保存栈顶元素
return 1;
}
}
链栈
定义栈顶指针变量top:LinkStack top,指向栈顶。
- 置空栈
栈顶指针指向空即可,top = null;
- 判栈空
int Empty_LinkStack(LinkStack top)
{if(top==NULL) return 1;
else return 0;
}
- 入栈
LinkStack Push_LinkStack(LinkStack top,datatype x)
{StackNode *p;
p = (StackNode *)malloc(sizeof(StackNode)); //申请节点空间
p -> data = x; //将要加入的数值加入到新开辟的节点的数值域
p -> next = top; //让该节点指向栈顶,即加入到栈顶下方
top = p; //栈顶指针指向该节点,即该节点为栈顶
return top; //返回栈顶,即返回新加入的节点
}
- 出栈
LinkStack Pop_LinkStack(LinkStack top,datatype *x)
{StackNode *p;
if(top==NULL) return NULL; //栈空,无法出栈
else {*x = top->data; //保存栈顶元素
p = top; //p为栈顶
top = top->next; //栈顶下移
free(p); //释放p的空间
return top;
}
}
堆栈应用
表达式求值
注意:左括号在栈外优先级最高,栈内优先级最低。
中缀表达式求值
例子
给一个中缀表达式,输出计算得到的值。
7*2*2-5+1-5+3-4
求解结果为18
解决步骤
- 设定两个栈s1和s2,s1用于存储运算对象,s2用于存储运算符。
- 从左到右扫描整个表达式,如果是运算对象,压入s1。
- 如果是运算符,则要分情况讨论:
- s2为空栈,直接压入栈中;
- 当前运算符的优先级高于栈顶的优先级,压入栈
- 当前运算符的优先级低于栈顶的优先级,s1出栈两个数据,s2出栈栈顶运算符,进行计算,结果压入s1。
- 当前运算符还未入栈,继续循环第3步骤(整个第3步骤,不是里面的第3步骤)。
- 如果运算符为右括号,则持续出栈,直到s2中遇到左括号为止,然后去掉这一对括号。
- 扫描完整个中缀表达式后,如果s2不为空,则依次从s1中出栈两个数据进行计算。结果压入栈中;循环往复,直到s2为空。最后s1中的数据就是所求的结果。
表格分析
读字符 | 对象栈s1 | 算符栈s2 | 说明 |
---|---|---|---|
7 | 7 | ( | 7入栈s1 |
* | 7 | (* | *入栈s2 |
2 | 7,2 | (* | 2入栈s1 |
* | 14 | ( | 做7*2=14,结果入栈s1 |
14 | (* | *入栈s2 | |
2 | 14,2 | (* | 2入栈s1 |
- | 28 | ( | 做14*2=28,结果入栈 s1 |
28 | (- | -入栈s2 | |
5 | 28,5 | (- | 5入栈s1 |
+ | 23 | ( | 做28-5=23,结果入栈s1 |
23 | (+ | +入栈s2 | |
1 | 23,1 | (+ | 1入栈s1 |
- | 24 | ( | 做23+1=24,结果入栈s1 |
24 | (- | -入栈s2 | |
5 | 24,5 | (- | 5入栈s1 |
+ | 19 | ( | 做24-5=19,结果入栈s1 |
19 | (+ | +入栈s2 | |
3 | 19,3 | (+ | 3入栈s1 |
- | 22 | ( | 做19+3=22,结果入栈s1 |
22 | (- | -入栈s2 | |
4 | 22,4 | (- | 4入栈s1 |
扫描至表达式末尾,算符栈中还有算符,依次出栈,进行计算,所以22-4=18。
后缀表达式求值
例子
给一个后缀表达式,输出计算得到的值。
32422*+13*-^*5-
求解结果为91
解决步骤
- 使用一个对象栈s1,来存储运算对象。
- 从左到右扫描表达式。
- 遇到运算对象,就入栈s1,遇到运算符,就从s1中取出两个运算对象,进行计算,结果压入s1。
表格分析
读字符 | 对象栈s1 | 说明 |
---|---|---|
3 | 3 | 3入栈s1 |
2 | 3,2 | 2入栈s1 |
4 | 3,2,4 | 4入栈s1 |
2 | 3,2,4,2 | 2入栈s1 |
2 | 3,2,4,2,2 | 2入栈s1 |
* | 3,2,4,4 | 计算2*2,将结果4入栈s1 |
+ | 3,2,8 | 计算4+4,将结果8入栈s1 |
1 | 3,2,8,1 | 1入栈s1 |
3 | 3,2,8,1,3 | 3入栈s1 |
* | 3,2,8,3 | 计算1*3,将结果3入栈s1 |
- | 3,2,5 | 计算8-3,将结果5入栈s1 |
^ | 3,32 | 计算2^5,将结果32入栈s1 |
* | 96 | 计算3*32,将结果96入栈s1 |
5 | 96,5 | 5入栈s1 |
- | 91 | 计算96-5,将结果91入栈s1 |
中缀表达式转后缀表达式
例子
输入一个中缀表达式,转换成后缀表达式。
中缀表达式:3*2^(4+2*2-1*3)-5
转换为后缀表达式:32422*+13*-^*5-
解决步骤
初始化两个栈,对象栈s1,算符栈s2。
从左至右扫描中缀表达式。
遇到运算对象时,入栈s1。
遇到运算符时,将其与s2的栈顶作比较:
1. 若s2为空栈,或者s2栈顶运算符为“(”,则入栈s2。 2. 若运算符的优先级高于栈顶运算符,入栈s2。 3. 否则,将s2的栈顶运算符弹出,入栈s1,再次循环进行与当前栈顶运算符比较。
遇到括号时:
1. 若是“(”,入栈s2。 2. 若是“)”,则依次弹出s2中的栈顶运算符并压入s1中,直到遇到左括号为止,此时将这一对括号丢弃。
重复上述除初始化栈之外的所有操作,直到表达式扫描至最右边。
若扫描结束后,算符栈s2中还有剩余的字符,则全部出栈,压入s1中。
将s1中的元素依次弹出,取逆序,得到后缀表达式。
表格分析
读字符 | 对象栈s1 | 算符栈s2 | 说明 |
---|---|---|---|
3 | 3 | ( | 3入栈s1 |
* | 3 | (* | *入栈s2 |
2 | 3,2 | (* | 2入栈s1 |
^ | 3,2 | (*^ | ^入栈s2 |
( | 3,2 | (*^( | (入栈s2,(在栈外优先级最高 |
4 | 3,2,4 | (*^( | 4入栈s1 |
+ | 3,2,4 | (*^(+ | +入栈s2,(在栈内优先级最低 |
2 | 3,2,4,2 | (*^(+ | 2入栈s1 |
* | 3,2,4,2 | (*^(+ * | *入栈s2 |
2 | 3,2,4,2,2 | (*^(+ * | 2入栈s1 |
- | 3,2,4,2,2,*,+ | (*^(- | *+出栈s2,压入s1,-入栈s2 |
1 | 3,2,4,2,2,*,+,1 | (*^(- | 1入栈s1 |
* | 3,2,4,2,2,*,+,1 | (*^(- * | *入栈s2 |
3 | 3,2,4,2,2,*,+,1,3 | (*^(- * | 3入栈s1 |
) | 3,2,4,2,2,*,+,1,3, *,- | (*^ | 遇到右括号,算符栈栈顶持续出栈,直到遇到左括号为止,丢弃这一对括号。 |
- | 3,2,4,2,2,*,+,1,3, *,-,^, * | (- | 优先级低于栈顶,栈顶出栈;栈为空,-入栈s1 |
5 | 3,2,4,2,2,*,+,1,3, *,-,^, *,5 | (- | 5入栈s1 |
结束符 | 3,2,4,2,2,*,+,1,3, *,-,^, *,5,- | ( | 扫描到末尾,s2中还有字符,出栈s2,压入s1 |
后缀表达式即需要出栈s1中所有元素,取逆序即可,所以后缀表达式为32422 * +13 * -^ * 5-