提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
数据结构——栈
前言
提示:这里可以添加本文要记录的大概内容:
课程链接:【【强烈推荐】深入浅出数据结构 - 顶尖程序员图文讲解 - UP主翻译校对 (已完结)】 https://www.bilibili.com/video/BV1Fv4y1f7T1/?p=17&share_source=copy_web&vd_source=a380547882bee6e5ecb87e70ac629227
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是栈?
示例:
二、实验
1.通过栈反转链表
代码如下(示例):
通过临时变量遍历链表,栈中存放的是节点地址
利用了栈先进后出的特性,先进的地址被压在栈底,temp最后指向栈顶,出栈后栈顶发生变化,next指向新的栈顶,利用栈的反方向输出完成链表反转
#include <iostream>
#include <stack>
using namespace std;
struct Node {
char data;
Node* next;
};
Node* head = NULL;
//反转链表
void reverse() {
if (head == NULL) {
return;
}
stack<Node* > s;
Node* temp = head;
//入栈
while (temp != NULL) {
s.push(temp);
temp = temp->next;
}
temp = s.top();
head = temp;//栈顶是头节点
//出栈
s.pop();
while (!s.empty()) {//栈不空的时候
temp->next = s.top();
s.pop();
temp = temp->next;
}
temp->next = NULL;//最后节点的指针调整一下
}
//尾插法
void insert(char data) {
Node* newNode = new Node();
newNode->data = data;
newNode->next = NULL;
if (head == NULL) {
head = newNode;
return;
}
Node* temp = head;
while (temp->next != NULL) {
temp = temp->next;//最后temp指向尾节点
}
temp->next = newNode;
}
//打印
void print() {
Node* temp = head;
while (temp != NULL) {
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
int main() {
insert('h');
insert('e');
insert('l');
insert('l');
insert('o');
print();
reverse();
print();
}
2.通过栈实现检查括号匹配性
条件:1、数量匹配;2、左括号要有右括号对应;3、出现右括号前,该括号对应的左括号之后出现的所以括号都必须已经匹配。即最后的左括号先遇到匹配的右括号。
思路:从左往右遍历所有括号;如果遇到左括号,则将其压入栈内;如果遇到右括号,则弹出栈顶(即最晚进入的)左括号检查匹配性,匹配则出栈;最终结果所有匹配后,栈内应该是empty的,否则则证明有括号不匹配。
代码如下(示例):
#include <string>
#include <iostream>
#include <stack>
using namespace std;
bool check_for_balanced_parentheses(string A, int n) {
stack<char> s;
for (int i = 0; i < n; i++)
{
if (A[i] == '(' || A[i] == '[' || A[i] == '{')
{
s.push(A[i]);//左括号压入栈内
}
else if(A[i] == ')' || A[i] == ']' || A[i] == '}')
{
if ((s.top()=='('&&A[i]==')')|| (s.top() == '[' && A[i] == ']')|| (s.top() == '{' && A[i] == '}'))
{
s.pop();//匹配则出栈
}
else
{
return false;//不匹配或者空栈压入右括号 都报错
}
}
}
return s.empty() ? true : false;//匹配过程结束后应该是空栈
}
int main() {
string ch;//接收输入的括号字符 char只能接受单个字符或者固定长度
cout << "请输入一串括号:";
cin >> ch;
size_t length = ch.length();//sizeof返回的事字符串ch占用的内存字节数 包括结束字符'\0'以外的字节 length返回的才是字符串实际长度
if (check_for_balanced_parentheses(ch, length))
{
cout << "匹配!!" << endl;
}
else
{
cout<< "不匹配!!" << endl;
}
return 0;
}
3.通过栈实现前缀、后缀表达式求值
3.1 前缀、后缀基本概念
- 前缀:2+3 写成+23 ; a+bc写成+abc
- 后缀:2+3 写成23+ ; a+bc写成abc+
3.2 前缀、后缀表达式求值(栈实现)
- 栈实现后缀表达式求值:操作数入栈,遇到操作符取两个数据出栈。栈的特性:先进后出,因为运算是从左往右算的,那么右操作数一定是后入栈的,所以先出的作为右操作数,紧接着再取出的操作数作为左操作数,运算完的结果还要入栈,因为要连续运算保存结果,每次都是在上一次的运算结果上继续运算
#include <string>
#include <iostream>
#include <stack>
using namespace std;
// 逆波兰表达式求值
int EvaluatePostfix(string expression);
// Function to perform an operation and return output.
int PerformOperation(char operation, int operand1, int operand2);
// Function to verify whether a character is operator symbol or not.
bool IsOperator(char C);
// Function to verify whether a character is numeric digit.
bool IsNumericDigit(char C);
int main() {
string expression;
cout << "请输入后缀表达式(数字之间用空格或者逗号作为分隔符):\n";
//不能用cin>>expression cin读取字符串会在遇到第一个空格时停止
getline(cin, expression);
int result = EvaluatePostfix(expression);
cout << "结果:" << result << endl;
return 0;
}
int EvaluatePostfix(string expression)
{
stack<int> S;
for (int i = 0; i < expression.length(); i++)
{
//后缀表达式 从左至右扫描
//如果是操作数分隔符 继续
if (expression[i] == ' ' || expression[i] == ',')
continue;
else if (IsOperator(expression[i]))
{
int operand2 = S.top();//先入后出 op2后进栈的 先出
S.pop();
int operand1 = S.top();
S.pop();
int result = PerformOperation(expression[i], operand1, operand2);
S.push(result);
}
else if (IsNumericDigit(expression[i]))//从字符串中提取数字
{
int operand = 0;
while (i < expression.length() && IsNumericDigit(expression[i]))
{
/*
* 循环嵌套处理多位数 从左至右扫描
* 表达式 expression[i] - '0' 的作用是将字符形式的数字转换为对应的整数值 0-9对应的ASCII码值是连续的
* i++移动到下一个字符
*/
operand = (operand * 10) + (expression[i] - '0');
i++;
}
/*
i在while循环中被再次增加 最后会停在一个非数字字符或者字符串结尾 所以需要i-- 返回到最开始的for循环中处理这个i对应的非数字字符
*/
i--;
S.push(operand);
}
}
return S.top();//最终栈内只剩一个最终结果
}
bool IsOperator(char C)
{
if (C == '+' || C == '-' || C == '*' || C == '/')
{
return true;
}
return false;
}
bool IsNumericDigit(char C)
{
if (C >= '0' && C <= '9') return true;
return false;
}
int PerformOperation(char operation, int operand1, int operand2)
{
if (operation == '+') return operand1 + operand2;
else if (operation == '-') return operand1 - operand2;
else if (operation == '*') return operand1 * operand2;
else if (operation == '/') return operand1 / operand2;
else cout << "Unexpected Error \n";
return -1;
}
- 栈实现前缀表达式求值:从右至左扫描,加减除注意op1和op2的关系
#include <string>
#include <iostream>
#include <stack>
using namespace std;
// 逆波兰表达式求值
int EvaluatePostfix(string expression);
// Function to perform an operation and return output.
int PerformOperation(char operation, int operand1, int operand2);
// Function to verify whether a character is operator symbol or not.
bool IsOperator(char C);
// Function to verify whether a character is numeric digit.
bool IsNumericDigit(char C);
int main() {
string expression;
cout << "请输入前缀表达式(数字之间用空格或者逗号作为分隔符):\n";
//不能用cin>>expression cin读取字符串会在遇到第一个空格时停止
getline(cin, expression);
int result = EvaluatePostfix(expression);
cout << "结果:" << result << endl;
return 0;
}
int EvaluatePostfix(string expression)
{
stack<int> S;
for (int i = expression.length() - 1; i >= 0; i--)
{
//后缀表达式 从左至右扫描
//如果是操作数分隔符 继续
if (expression[i] == ' ' || expression[i] == ',')
continue;
else if (IsOperator(expression[i]))
{
int operand2 = S.top();//先入后出 op2后进栈的 先出
S.pop();
int operand1 = S.top();
S.pop();
int result = PerformOperation(expression[i], operand1, operand2);
S.push(result);
}
else if (IsNumericDigit(expression[i]))//从字符串中提取数字
{
int operand = 0;
while (i < expression.length() && IsNumericDigit(expression[i]))
{
/*
* 循环嵌套处理多位数 从左至右扫描
* 表达式 expression[i] - '0' 的作用是将字符形式的数字转换为对应的整数值 0-9对应的ASCII码值是连续的
* i++移动到下一个字符
*/
operand = (operand * 10) + (expression[i] - '0');
i--;
}
/*
i在while循环中被再次增加 最后会停在一个非数字字符或者字符串结尾 所以需要i-- 返回到最开始的for循环中处理这个i对应的非数字字符
*/
i++;
S.push(operand);
}
}
return S.top();//最终栈内只剩一个最终结果
}
bool IsOperator(char C)
{
if (C == '+' || C == '-' || C == '*' || C == '/')
{
return true;
}
return false;
}
bool IsNumericDigit(char C)
{
if (C >= '0' && C <= '9') return true;
return false;
}
int PerformOperation(char operation, int operand1, int operand2)
{
if (operation == '+') return operand1 + operand2;
else if (operation == '-') return operand2 - operand1;
else if (operation == '*') return operand1 * operand2;
else if (operation == '/') return operand2 / operand1;
else cout << "Unexpected Error \n";
return -1;
}
3.3 中缀到后缀表达式转换
中缀向后缀表达式转换,从左至右扫描,数字符顺序不会变,运算符顺序可能会发生变化。
思路:从左往右扫描,遇到数字存入后缀表达式字符串中,遇到符号存入栈中。
- 运算右操作数边界:
1、栈顶操作符比infix表达式中遇到的操作符优先级高,则栈内操作符全部弹出(+和-优先级一样,但从左往右,左边优先级高)
2、到达了infix表达式结尾 - 针对表达式有括号:左括号压入栈内,遇到右括号后一直执行弹出栈操作直到对应的左括号出栈。
#include <string>
#include <iostream>
#include <stack>
using namespace std;
// Function to convert Infix expression to postfix
string InfixToPostfix(string expression);
// 验证优先级别的函数
int WhoIsHigher(char operator1, char operator2);
// 操作符判别函数
bool IsOperator(char C);
// 操作数判别函数
bool IsOperand(char C);
//左括号判别
bool leftBracket(char c);
//右括号判别
bool rightBracket(char c);
//优先级别判定
int GetOperatorWeight(char c);
int main() {
string expression;
cout << "请输入表达式(数字之间用空格或者逗号作为分隔符):\n";
getline(cin, expression);
string postfix = InfixToPostfix(expression);
cout << "转换结果:" << postfix << endl;
return 0;
}
string InfixToPostfix(string expression)
{
stack<char> S;//存操作符的栈
string postfix = " ";//初始化 转换输出的后缀表达式
//从左往右循环
for (int i = 0; i < expression.length(); i++)
{
if (expression[i] == ' ' || expression[i] == ',')
continue;
else if (IsOperand(expression[i]))
{
postfix += expression[i];
//操作数直接写入表达式
}
else if (IsOperator(expression[i]))
{
//操作符 且不是左括号 判定优先级
while (!S.empty() && !leftBracket(S.top()) && WhoIsHigher(S.top(), expression[i]))
{
postfix += S.top();//栈内的优先级高 放入后缀表达式
S.pop();
}
//都不满足则操作符入栈
S.push(expression[i]);
}
else if (leftBracket(expression[i]))
{
//左括号入栈
S.push(expression[1]);
}
else if (rightBracket(expression[i]))
{
//右括号则遍历栈内 直到遇到栈内第一个左括号停止
while (!S.empty() && !leftBracket(S.top()))
{
postfix += S.top();
S.pop();
}
S.pop();//弹出栈内多余的左括号
}
}
//for循环遍历中缀表达式结束 栈内还有剩的操作符 则放在后缀表达式的最后
while (!S.empty())
{
postfix += S.top();
S.pop();
}
return postfix;
}
int GetOperatorWeight(char c)
{
int weight = -1;
switch (c)
{
case '+':
case '-':
weight = 1;
break;
case'*':
case'/':
weight = 2;
break;
case '^':
weight = 3;
break;
}
return weight;
}
int WhoIsHigher(char operator1, char operator2)
{
int op1_Weight = GetOperatorWeight(operator1);
int op2_Weight = GetOperatorWeight(operator2);
if (op1_Weight == op2_Weight)
{
return true;//栈内的op1 循环到的中缀表达式中的操作符为op2 从左往右遍历 左边优先级高 所以直接返回true
}
//op1在栈内 判定栈内的是否比表达式中的高
return op1_Weight > op2_Weight ? true : false;
}
bool IsOperator(char C)
{
if (C == '+' || C == '-' || C == '*' || C == '/'|| C == '^')
{
return true;
}
return false;
}
bool IsOperand(char C)
{
if (C >= '0' && C <= '9') return true;
if (C >= 'a' && C <= 'z') return true;
if (C >= 'A' && C <= 'Z') return true;
return false;
}
bool leftBracket(char c)
{
if (c == '(' || c == '{' || c == '[')
{
return true;
}
else
{
return false;
}
}
bool rightBracket(char c)
{
if (c == ')' || c == '}' || c == ']')
{
return true;
}
else
{
return false;
}
}