0 栈和队列
0.1 栈(stack)
先进后出,后进先出
栈的底层实现可以是vector,deque,list 都是可以的, 主要就是数组和链表的底层实现。
0.2 队列(queue)
先进先出
0.3 优先队列(priority_queue)
小堆序实现从大到小排序,大堆序实现从小到大排序
小堆序堆顶的元素一定是堆里最小的,大堆序堆顶的元素一定是堆里最大的
1用两个栈实现队列
栈(stack):先进后出
队列(cqueue):先进先出
思路:
sin用于进,sout用于出
加入元素:直接添加进sin
删除元素:
- 情况1:sout不为空,则直接返回sout.top(),并pop()
- 情况2:sout为空,若sin不为空,则将sin中的全部元素给sout,若sin为空,则说明此时没有元素,返回-1
class CQueue {
stack<int> sin;
stack<int> sout;
public:
CQueue() {
}
void appendTail(int value) {
sin.emplace(value);
}
int deleteHead() {
if (!sout.empty()) {
int n = sout.top();
sout.pop();
return n;
}
else {
if (sin.empty()) return -1;
else {
while (!sin.empty()) {
sout.emplace(sin.top());
sin.pop();
}
int n = sout.top();
sout.pop();
return n;
}
}
}
};
2 用队列实现栈
2.1 两个队列
用一个队列q1作为主栈,实现所有功能,另一个队q2列作为插入数据时的缓冲队列
加入元素时:
- 先将q1中的所有元素放到q2中,接着在q1中加入元素,此时刚加入的元素就位于队列前端,也就实现了后加先出,再将q2中的元素再导入q1
删除,返回top等功能就只需要在q1进行
class MyStack {
queue<int> q1;
queue<int> q2;
public:
MyStack() {
}
void push(int x) {
while (!q1.empty()) {
q2.emplace(q1.front());
q1.pop();
}
q1.emplace(x);
while (!q2.empty()) {
q1.emplace(q2.front());
q2.pop();
}
}
int pop() {
int n = q1.front();
q1.pop();
return n;
}
int top() {
return q1.front();
}
bool empty() {
return q1.empty();
}
};
2.2 一个队列实现栈
一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。
class MyStack {
public:
queue<int> que;
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
que.push(x);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int size = que.size();
size--;
while (size--) { // 将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部
que.push(que.front());
que.pop();
}
int result = que.front(); // 此时弹出的元素顺序就是栈的顺序了
que.pop();
return result;
}
/** Get the top element. */
int top() {
return que.back();
}
/** Returns whether the stack is empty. */
bool empty() {
return que.empty();
}
};
2有效的括号
思路一(栈):
- 若字符串个数不为偶数个(%2==1),则必是有问题的
- 构造一个栈,遍历字符串,若是
( [ {
则入栈 - 若是
) ] }
,则看此时栈是否为空,为空肯定是false,不为空还需看栈顶是否为对应的括号,并将栈顶移出。 - 最后若栈不为空,说明里面还有多余的
( [ {
,说明也有问题。
思路二(栈+哈希)
哈希表unordered_map
用法:{{key,value}
}
- 定义:
unordered_map<int,int>
、unordered_map<string, double>
… - 插入:例如(“ABC” -> 5.45) 插入,hash[“ABC”]=5.45
- 查询:hash[“ABC”]会返回5.45
- 判断key是否存在:hash.count(“ABC”) != 0 或 hash.find(“ABC”) != hash.end()
eg:unordered_map<char,int> m{{'(',1},{'[',2},{'{',3},{')',4},{']',5},{'}',6}};
- 关联性:std::unorederd_map 是一个关联容器,其中的元素根据键来引用,而不是根据索引来引用。
- 无序性:在内部,std::unordered_map中的元素不会根据其键值或映射值按任何特定顺序排序,而是根据其哈希值组织到桶中,以允许通过键值直接快速访问各个元素(常量的平均时间复杂度)。
- 唯一性:std::unorederd_map中的元素的键是唯一的。
for(char c:s)
:遍历字符串。相当于C++的:
for( int i = 0; i < s.length(); i++)
{
s[i]....
}
for (char c : s)//复制一个s字符串再进行遍历操作
for (char& c : s)//直接引用原字符串进行遍历操作
for(auto &c:s)
for(auto c:s)
auto是c++程序设计语言的关键字。用于两种情况:
-
(1)声明变量时根据初始化表达式自动推断该变量的类型
-
(2)声明函数时函数返回值的占位符
-
举例:对于值x=1;既可以声明: int x=1 或 long x=1,也可以直接声明 auto x=1
3包含min函数的栈
要求:能够得到栈的最小元素的 min 函数在该栈中
思路:构造一个栈A满足正常的push和pop,再构造一个有序栈B将最小值放到栈顶。
- 最小栈在初始化时应该添加一个极大值
INT_MAX
注:/因为在添加第一个值的时候,最小栈里面没有元素,所以用一个极大值保证首个元素能够添加进最小栈。 - 栈A正常添加元素,若添加的元素<=有序栈B的栈顶元素,才将该元素添加进栈B。
-栈A正常删除栈顶元素。若删除的元素==有序栈B的栈顶元素,栈B才删除该元素(保证若最小值出栈时栈A和栈B一起删除它)
class MinStack {
public:
/** initialize your data structure here. */
stack<int>st,stmin;
MinStack() {
stmin.push(INT_MAX);//因为在下方,添加第一个值的时候,stmin.top()没有值,又因为只有一个值,肯定是最小的,所以用一个极大值保证第一个值能够添加进最小栈st_min
}
void push(int x) {
st.push(x);
if( x <= stmin.top() )
{
stmin.push(x);
}
}
void pop() {
if(st.top()==stmin.top())stmin.pop();//若最上面的是最小值,则需把另一个辅助栈也删掉
st.pop();
}
int top() {
return st.top();
}
int min() {
return stmin.top();
}
};