一、栈(Stack):是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。
1.操作:入栈,出栈,判断栈是否为空,获取栈顶元素
2.特点:后存储的数据先拿出来,后进先出。
这里用数组模拟,参考如下代码(注代码不完善,大致框架):
#include<iostream>
using namespace std;
const int N=100010;
int stk[N],tt=0; //tt栈头,初始化
//插入
stk[tt++];
//弹出
tt--;
//判断栈是否为空
if(tt>0) not empty;
else empty;
//栈顶
stk[tt];
具体操作见以下两道题
见代码:
#include<iostream>
using namespace std;
const int N=100005;
int stk[N],tt=0;//初始化栈头为空
int main()
{
int m,x;
string s;
cin>>m;
while(m)
{
cin>>s;
if(s=="push")
{
cin>>x;
stk[++tt]=x; //插入
}
if(s=="pop")
{
--tt; //弹出,先进后出
}
if(s=="query")
{
cout<<stk[tt]<<endl; //查询栈顶
}
if(s=="empty")
{
if(tt>0) cout<<"NO"<<endl; //判断是否为空
else cout<<"YES"<<endl;
}
m--;
}
return 0;
}
难点在于这样判断运算符的优先级 ,没想出来,参考了一位大佬的思路,详情见AcWing 3302. 表达式求值:多图讲解运算符优先级+详细代码注释 - AcWing
代码如下:(注释为个人理解)
#include<iostream>
#include<stack>
#include<cctype>
#include<string>
#include<unordered_map>
using namespace std;
stack<int> num;
stack<char> op;
unordered_map<char,int> h{{'+',1},{'-',1},{'*',2},{'/',2}};
void cou()//求值 栈顶不断的更新 弹出
{
int a = num.top();//第二个操作数
num.pop();
int b = num.top();//第一个操作数
num.pop();
char p = op.top();//运算符
op.pop();
int r = 0;//结果
//计算结果
if (p == '+') r = b + a;
if (p == '-') r = b - a;
if (p == '*') r = b * a;
if (p == '/') r = b / a;
num.push(r);//结果入栈
}
int main()
{
string s;
cin>>s;
for(int i=0;i<s.size();i++)
{
/*if(s[i]>=0&&s[i]<=9) //这样是错的,if(s[i] >= ‘0’ && s[i] <= ‘9’)
{ //数字的ASCII码值是48-57
num.push(s[i]); //直接插进去不对,忽略了连续的数字,十位、百位···
}
*/
if(s[i]>='0'&&s[i]<='9')
{
int x = 0, j = i;//x最终的整数值,
//j用于记录数字字符的结束位置
while (j < s.size() && isdigit(s[j])) //直接用isdigit函数判断是否是数字字符
{
x = x * 10 + s[j] - '0'; //转为整数
j++;
}
num.push(x);//入栈
i = j - 1; //更新i
}
else if(s[i]=='(')
op.push(s[i]);
else if(s[i]==')')
{
while(op.top()!='(') //括号得读到新的“( ”才行
cou();
op.pop(); //出栈,弹出
}
else
{
while(op.size()&&h[op.top()]>=h[s[i]]) //当栈顶的运算符大于要插入的
//运算符,则先计算
cou();
op.push(s[i]); //否则入栈(插入的优先级大于栈顶的)
}
}
while (op.size()) cou(); //处理剩余的运算符和操作数(+、-)
cout << num.top() << endl;
return 0;
}
补充知识点:
isdigit( ) 函数
① C++中的可以用 isdigit( ) 函数可以用来判断字符是否为数字
头文件:使用函数 isdigit( ) 需要包含头文件 #include<cctype>
功能:当判断的字符是数字时,函数返回1~9的非零值,当判断的字符不是数字时,函数返回 0
例
#include <iostream>
#include<cctype>
#include<string>
using namespace std;
int main()
{
string s;
cin>>s;
for (int i = 0; i < s.size(); i++)
{
if (isdigit(s[i])) //用isdigit()函数进行判断
{
cout << s[i] << "是数字" << endl;
}
else
{
cout << s[i] << "不是数字" << endl;
}
}
return 0;
}
运行结果
unordered_map与map不同之处
② unordered_map与map有什么不同
·unordered_map和map是C++标准库中的两个容器类,用于存储键值对。
·unordered_map头文件#include<unordered_map>;map的头文件#include<map>
·unordered_map适用于需要快速查找和插入元素且无需保持顺序的情况;而map适用于需要有序存储并支持范围查找的情况
·unordered_map的用法和map相同
它们之间有以下几点不同:
1. 内部实现方式:unordered_map使用哈希表(hash table)实现,而map使用红黑树(red-black tree)实现。哈希表通过计算键的哈希值来快速查找,而红黑树则保持元素有序。
2. 元素顺序:unordered_map中的元素没有特定的顺序,无法保证插入顺序或者键的排序顺序。而map按照键的排序顺序存储元素,默认以升序排列。
3. 效率:在大多数情况下,unordered_map的查找、插入和删除操作的平均时间复杂度为O(1),而map的这些操作的平均时间复杂度为O(log n),其中n为元素数量。然而,在某些特殊情况下,哈希函数可能导致unordered_map的性能下降,而红黑树保持了较稳定的性能。
4. 支持的操作:unordered_map和map都提供了类似的成员函数和操作符,例如插入insert、删除erase、查找find等。但unordered_map还提供了bucket functions(桶函数),可以获取每个元素所属的桶索引。
二、队列(queue):是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。即遵循“先进先出”原则.
1.允许插入的一端称为队尾(tail),允许删除的一端称为队头(front)
2.操作:入队、出队、判断队是否为空、获取队头元素
3.注意:只有队头和队尾能被外界访问,因此不能遍历查找某元素
这里用数组模拟大致流程,参考如下代码(注代码不完善,大致框架):
#include<iostream>
using namespace std;
const int N=100010;
int q[N],hh,tt=-1;
//插入
q[++tt]=x;
//弹出
hh++;
//判断队列是否为空
if(hh<=tt) not empty;
else empty;
//取出队头元素
q[hh];
q[tt]; //取出队尾元素
具体操作见下题
https://www.acwing.com/problem/content/831/
#include <iostream>
using namespace std;
const int N = 100010;
int q[N];
int hh = 0; //队头位置
int tt = -1; //队尾
int main()
{
int m,x;
string s;
cin >> m;
while(m--)
{
cin >> s;
if(s == "push"){
cin >> x;
q[++tt] = x; //插入
}
if(s == "pop"){
hh++; //弹出,[hh,tt]这个区间,hh后移即队头弹出
}
if(s == "empty"){
if(tt >= hh) cout << "NO" << endl; //判断队是否为空
else cout << "YES" << endl;
}
if(s == "query"){
cout << q[hh] << endl; //队头元素
}
}
return 0;
}
关于栈和队列的知识就到这里了,想详细了解可参考http://t.csdn.cn/2nwZ2