队列
队列为先进先出的数据结构类型。
我们可以通过数组来实现,也可以用链表来实现,通过一个指针或者数字标记记录头部和尾部的位置,出队列令头部pop,然后后移,入队列让尾部push,然后后移。
循环队列,意味着尾部后移到一定程度要绕回头部,形成一个环。可以节约空间。
经典算法题:BFS轮盘锁
栈
先进后出的数据结构类型。
我们只需要记录栈顶的位置即可,任何操作都是围绕栈顶进行的,当然,一些特定的情形我们可能需要维护一些其他的结构帮助我们操作非栈顶的元素。我们可以通过一个指针或者数字标记记录栈顶位置,出栈令头部pop,入栈令头部push。
经典算法题:有效的括号
注意单调栈的用法:每日温度,最大面积
单调栈是一个单调递增或递减的栈,在每日温度中,我们可以通过单调栈得到一组温度递增的下标栈:例如:temperatures = [73, 74, 75, 71, 69, 72, 76, 73],我们需要维护一个递减的单调栈,在维护这个递减栈时,我们就能得到天数差,
73入栈,74入栈导致73出栈,故73赋值为两者下标差1,75入栈导致74出栈,故74赋值为两者下标差1,71入栈,69入栈,72入栈导致69出栈,69赋值为两者下标差1,以此类推,走一遍即可。
轮盘锁代码
class Solution {
public:
//BFS队列
queue<string> que;
//已经访问数组
bool vist[10010];
//字符串转化为数字
int str2num(string str){
return (str[0]-'0')*1000 + (str[1]-'0')*100 + (str[2]-'0')*10 + (str[3]-'0')*1;
}
//函数复用,将posi位的数字+add
string que_push_num(string target,int add,int posi){
string temp = target;
temp[posi] = (target[posi] - '0' + add + 10)%10 + '0';
return temp;
}
//对应一个数字,将8个方向的密码入队列
void que_push(string target){
string temp;
for(int posi=0;posi<4;posi++){
temp = que_push_num(target,-1,posi);//posi位-1入栈并剔除vist
que.push(temp);
temp = que_push_num(target,1,posi);//posi位+1入栈并剔除vist
que.push(temp);
}
}
int openLock(vector<string>& deadends, string target) {
for(int i=0;i<10010;i++){
vist[i] = false;
}
for(int i=0;i<deadends.size();i++){
vist[str2num(deadends[i])] = true;
}
que.push("0000");
//第一个数就被卡死就return -1
if(vist[0])
return -1;
int step = 0;
while(!que.empty()){
int size = que.size();
for(int i=0;i<size;i++){
if(que.front()==target)
return step;
if(vist[str2num(que.front())]){
que.pop();
continue;
}
que_push(que.front());
vist[str2num(que.front())] = true;
que.pop();
}
step += 1;
}
return -1;
}
};
空间占用过高,可以考虑在入栈前判断是否visit,而不是入栈后再判断。
这样可以节约至少一半的空间,但是懒修改了。
总结
讲点C++的小知识:
c++取余运算结果可正可负,谨防bug
做好字符串与数字的映射,或者采用hash,可以大幅降低查询时间
单调栈维护过程与题目结果的得出是同步进行的,理解这一点对大部分题有帮助