数据结构学习之栈

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

课程链接:【【强烈推荐】深入浅出数据结构 - 顶尖程序员图文讲解 - 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;
	}
}

总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值