栈的定义
- 仅在表尾进行插入删除操作的线性表
- 栈顶:表尾端
- 栈底:表头端
- 空栈:不含元素的空表
特点:后进先出(如铁路调度站,撤销(后退键)等)
程序设计中:需要按照保存数据时相反的顺序来使用数据时,可利用栈来实现
顺序栈
顺序栈基本操作:1.初始化 2.入栈 3.出栈 4.测试
#include <stdio.h>
#include <malloc.h>
/*顺序栈的存储结构*/
#define STACK_MAX_SIZE 10
typedef struct CharStack{
int top;//栈顶元素的位置
int data[STACK_MAX_SIZE];
}*CharStackPtr;
int i;char ch;
/*输出栈元素(测试用)*/
void outputStack(CharStackPtr paraStack){
for( i=0 ; i <= paraStack->top ; i++ ){
printf("%c",paraStack->data[i]);
} //Of for i
printf("\r\n");
}//Of outputStack
/*初始化栈*/
CharStackPtr initStack(){
CharStackPtr resultPtr=(CharStackPtr)malloc(sizeof(struct CharStack));
resultPtr->top = -1;
return resultPtr;
} //Of initStack
/*入栈(在栈顶插入新元素)*/
void push(CharStackPtr paraStack,char paraChar){
//判断栈是否已满(空间检查)
if(paraStack->top >= STACK_MAX_SIZE-1){
printf("Cannot push element: The stack is full.\r\n");
return;
} //Of if
//更新栈顶
paraStack->top++;
//添加元素到栈顶
paraStack->data[paraStack->top]=paraChar;
}//Of push
/*出栈(删除栈顶元素)*/
char pop(CharStackPtr paraStack){
//检查栈是否为空
if(paraStack->top<0){
printf("Cannot pop the element: The stack is empty.\r\n");
return '\0';
} //Of if
//更新栈顶
paraStack->top--;
//返回出栈元素
return paraStack->data[paraStack->top+1];
}//Of pop
/*测试样例*/
void pushPopTest(){
printf("---- pushPopTest begins. ----\r\n");
//初始化init
CharStackPtr tempStackPtr=initStack();
printf("After initialization, the stack is: \r\n");
outputStack(tempStackPtr);
//入栈push
for(ch='a';ch<'m';ch++){
printf("Pushing %c.\r\n",ch);
push(tempStackPtr,ch);
outputStack(tempStackPtr);
}
//出栈pop
for(i=0;i<3;i++){
ch=pop(tempStackPtr);
printf("Poping %c.\r\n",ch);
outputStack(tempStackPtr);
}
printf("---- pushPopTest ends. ----\r\n");
}//Of pushPopTest
int main() {
pushPopTest();
return 0;
}// Of main
运行结果
注意:
- 初始化时栈顶"指针"top为-1
- 入栈出栈都要进行空间检查
- 顺序栈的插入删除都只在栈顶进行
- 栈元素具有线性关系,即具有前驱后继关系(栈为特殊线性表)
- 栈对线性表的插入和删除的位置进行了限制,并没有对元素进出的时间进行限制(在不是所有元素都进栈的情况下,事先进去的元素也可以出栈,只要保证是栈顶元素出栈就行)
- 扩展:两栈共享空间
括号匹配
#include <stdio.h>
#include <malloc.h>
#include<stdbool.h>
/*顺序栈的存储结构*/
#define STACK_MAX_SIZE 10
typedef struct CharStack{
int top;//栈顶元素的位置
int data[STACK_MAX_SIZE];
}*CharStackPtr;
int i;char ch;
/*输出栈元素(测试用)*/
void outputStack(CharStackPtr paraStack){
for( i=0 ; i <= paraStack->top ; i++ ){
printf("%c",paraStack->data[i]);
} //Of for i
printf("\r\n");
}//Of outputStack
/*初始化栈*/
CharStackPtr initStack(){
CharStackPtr resultPtr=(CharStackPtr)malloc(sizeof(struct CharStack));
resultPtr->top = -1;
return resultPtr;
} //Of initStack
/*入栈(在栈顶插入新元素)*/
void push(CharStackPtr paraStack,char paraChar){
//判断栈是否已满(空间检查)
if(paraStack->top >= STACK_MAX_SIZE-1){
printf("Cannot push element: The stack is full.\r\n");
return;
} //Of if
//更新栈顶
paraStack->top++;
//添加元素到栈顶
paraStack->data[paraStack->top]=paraChar;
}//Of push
/*出栈(删除栈顶元素)*/
char pop(CharStackPtr paraStack){
//检查栈是否为空
if(paraStack->top<0){
printf("Cannot pop the element: The stack is empty.\r\n");
return '\0';
} //Of if
//更新栈顶
paraStack->top--;
//返回出栈元素
return paraStack->data[paraStack->top+1];
}//Of pop
/*PushPop测试样例*/
void pushPopTest(){
printf("---- pushPopTest begins. ----\r\n");
//初始化init
CharStackPtr tempStackPtr=initStack();
printf("After initialization, the stack is: \r\n");
outputStack(tempStackPtr);
//入栈push
for(ch='a';ch<'m';ch++){
printf("Pushing %c.\r\n",ch);
push(tempStackPtr,ch);
outputStack(tempStackPtr);
}
//出栈pop
for(i=0;i<3;i++){
ch=pop(tempStackPtr);
printf("Poping %c.\r\n",ch);
outputStack(tempStackPtr);
}
printf("---- pushPopTest ends. ----\r\n");
}//Of pushPopTest
/*括号匹配*/
bool bracketMatching(char* paraString,int paraLength){
//初始化(统一操作:将'#'压入栈底)
CharStackPtr tempStack=initStack();
push(tempStack,'#');
char tempchar,tempPopedchar;
//处理字符串
for(i=0;i<paraLength;i++){
tempchar=paraString[i];
switch(tempchar){
case '(':
case '[':
case '{':
push(tempStack,tempchar);
break;
case ')':
tempPopedchar=pop(tempStack);
if(tempPopedchar!='('){
return false;
}//Of if
break;
case ']':
tempPopedchar=pop(tempStack);
if(tempPopedchar!='['){
return false;
}//Of if
break;
case '}':
tempPopedchar=pop(tempStack);
if(tempPopedchar!='{'){
return false;
}//Of if
break;
default:
break;
}//Of switch
}//Of for
//检查最后栈中是否还有未被匹配的括号
tempPopedchar=pop(tempStack);
if(tempPopedchar!='#'){
return false;
}//Of if
return true;
} //Of bracketMatching
/*bracketMatching测试样例*/
void bracketMatchingTest(){
char* tempExpression;
tempExpression="[2 + (1 - 3) * 4]";
bool tempMatch=bracketMatching(tempExpression,17);
printf("Is the expression '%s' bracket matching? %d\r\n",tempExpression,tempMatch);
tempExpression = "( ) )";
tempMatch = bracketMatching(tempExpression, 6);
printf("Is the expression '%s' bracket matching? %d \r\n", tempExpression, tempMatch);
tempExpression = "()()(())";
tempMatch = bracketMatching(tempExpression, 8);
printf("Is the expression '%s' bracket matching? %d \r\n", tempExpression, tempMatch);
tempExpression = "({}[])";
tempMatch = bracketMatching(tempExpression, 6);
printf("Is the expression '%s' bracket matching? %d \r\n", tempExpression, tempMatch);
tempExpression = ")(";
tempMatch = bracketMatching(tempExpression, 2);
printf("Is the expression '%s' bracket matching? %d \r\n", tempExpression, tempMatch);
}
int main() {
//pushPopTest();
bracketMatchingTest();
return 0;
}// Of main
运行结果
注意:for循环结束后要再次检查栈中是否还有未匹配的括号(此处也是为什么初始化时要将'#'压入栈底,这里是一种“统一操作”)
括号匹配的处理过程与栈的特点相吻合:每读入一个括号,若为右括号,则使置于栈顶的最急迫的期待得以消解,否则是不合法情况;若为左括号,则作为一个新的更急迫的期待压入栈中,自然使原有的在栈中所有未消解的期待的紧迫性下降了一级。
表达式求值
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>//直接定义栈,需要stack头文件
#include <unordered_map>
//在C++中使用cin和cout,需要包含头文件iostream以及std标准命名空间。
using namespace std;
/*stack<int> num:创建堆栈对象num
*C++ Stack(堆栈) 是一个容器类的改编,为程序员提供了堆栈的全部功能
*也就是说实现了一个先进后出(FILO)的数据结构。
*/
stack<int> num;
stack<char> op;
//计算
void eval()
{
auto b = num.top();//auto:根据后边的值来推测前面的值是什么
num.pop();
auto a = num.top();
num.pop();
auto c = op.top();
op.pop();
int x;
if (c == '+') x = a + b;
else if (c == '-') x = a - b;
else if (c == '*') x = a * b;
else x = a / b;
num.push(x);//计算结果入栈;
}
int main()
{
unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};//运算符优先级
string str;
cin >> str;//cin标准输入和cout标准输出
for (int i = 0; i < str.size(); i ++ )
{
auto c = str[i];
if (isdigit(c))// c为数字
{
int x = 0, j = i;
while (j < str.size() && isdigit(str[j]))//j不超过字符串的最大长度
x = x * 10 + str[j ++ ] - '0';//str[j ++ ] - '0'是将字符转化为数字的操作
i = j - 1;
num.push(x);
}
else if (c == '(') op.push(c);//c为左括号,压入运算符栈op
else if (c == ')')//c为右括号
{
while (op.top() != '(') eval();//op栈顶元素不匹配,就计算
op.pop();//计算完毕,将op栈的栈顶元素弹出
}
else
{ //op栈不为空,栈顶元素不为左括号 ,栈顶元素优先级比当前元素c高 :计算
while (op.size() && op.top() != '(' && pr[op.top()] >= pr[c]) eval();
op.push(c);//计算完毕,将op栈的栈顶元素弹出
}
}
while (op.size()) eval();//op栈不为空,继续算
cout << num.top() << endl;//输出计算结果
return 0;
}
运行结果:
图解表达式求值:以3+(5-2)*4为例
1.入栈(遇到右括号之前)
2.遇到右括号之后,相继弹出进行计算
3.让计算结果入栈
4.表达式遍历完毕,跳出for循环,进入while循环
5.while循环两次,一次计算3*4=12,将12入num栈;第二次再计算3+12=15,将15入num栈
计算表达式核心思想:用两个栈,一个储存数字,一个储存操作符