简介
先进先出
#include <iostream>
int main() {
// 1. Initialize a queue.
queue<int> q;
// 2. Push new element.
q.push(5);
q.push(13);
q.push(8);
q.push(6);
// 3. Check if queue is empty.
if (q.empty()) {
cout << "Queue is empty!" << endl;
return 0;
}
// 4. Pop an element.
q.pop();
// 5. Get the first element.
cout << "The first element is: " << q.front() << endl;
// 6. Get the last element.
cout << "The last element is: " << q.back() << endl;
// 7. Get the size of the queue.
cout << "The size is: " << q.size() << endl;
}
含有两个元素的队列
定义 queue<pair<int,int>> neighborhoods
入队 neighborhoods.push({i,j})
出队 neighborhoods.pop()
取队首 auto rc=neighborhoods.front();
int r=rc.first,c=rc.second;
是否为空 neighborhoods.empty()
队列与广度优先算法
和树的层次遍历类似,每次pop出一个节点进行处理,判断是否为目标值。不是的话,遍历其子节点,插入到队列,然后再从队列pop出一个值进行处理,以此循环。
模板
/**
* Return the length of the shortest path between root and target node.
*/
int BFS(Node root, Node target) {
Queue<Node> queue; // store all nodes which are waiting to be processed
int step = 0; // number of steps neeeded from root to current node
// initialize
add root to queue;
// BFS
while (queue is not empty) {
step = step + 1;
// iterate the nodes which are already in the queue
int size = queue.size(); #这个一定要先算出来,不可以在for循环那一行求,因为for循环里面有pop操作,造成size会变化。
#这个循环可以加,也可以不加,加上的话,类似树的层次遍历,可以统计一共遍历了多少层,在计算一些最小步问题是,需要记录step
#不加的话,每次pop出单个元素,也可正常运行。
for (int i = 0; i < size; ++i) {
Node cur = the first node in queue;
return step if cur is target;
for (Node next : the neighbors of cur) {
add next to queue;
}
remove the first node from queue;
}
}
return -1; // there is no path from root to target
}
例题
1.岛屿数量
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1
示例 2:
输入:grid = [
[“1”,“1”,“0”,“0”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“1”,“0”,“0”],
[“0”,“0”,“0”,“1”,“1”]
]
输出:3
//思路:首先遍历查找到“1”的位置,岛屿数目加1(因为只要有"1"在,肯定会有岛屿)然后利用广度优先算法,查找到与其相邻的“1”,并将其置为“0’,直到再也没有"1",结束。
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int num=0;
int row=grid.size();
int column=grid[0].size();
queue<pair<int,int>> neighborhoods;
for(int i=0;i<row;i++){
for(int j=0;j<column;j++){
if(grid[i][j]=='1'){
num++;
// 广度优先算法
neighborhoods.push({i,j});
while(!neighborhoods.empty()){
for(int k=0;k<neighborhoods.size();k++){
//处理当前节点
auto rc=neighborhoods.front();
int r=rc.first,c=rc.second;
//如果为"1',入队,并置为”0“
if(r-1>=0 && grid[r-1][c]=='1'){
neighborhoods.push({r-1,c});
grid[r-1][c]='0';
}
if(r+1<row && grid[r+1][c]=='1'){
neighborhoods.push({r+1,c});
grid[r+1][c]='0';
}
if(c-1>=0 && grid[r][c-1]=='1'){
neighborhoods.push({r,c-1});
grid[r][c-1]='0';
}
if(c+1<column && grid[r][c+1]=='1'){
neighborhoods.push({r,c+1});
grid[r][c+1]='0';
}
//当前节点出队,并置为0
grid[r][c]='0';
neighborhoods.pop();
}
}
}
}
}
return num;
}
};
2.完全平方数
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9
//思路:对于每一步,我们都可以减去若干个平方数,剩余的若干和入队,然后再以此出队,知道剩余和为0为止。
//有个小tip,加个记忆序列,可大幅减少计算量,减少重复运算,即如果元素入队前,先判断其是否已经如果队,如果已经入队过,不让其入队,防止重复运算
class Solution {
public:
int numSquares(int n) {
queue<int> q;
//记忆序列,用于记录是否已经入队过
vector<bool> visited(n+1, false);
q.push(n);
int step=0;
while(!q.empty()){
int size=q.size();
for(int i=0;i<size;i++){
int cur=q.front();
if(cur==0) return step;
// vector<int> sum=get_data(cur);
for(int k=1;k<=sqrt(cur);k++){
if(visited[cur-k*k]==false){
q.push(cur-k*k);
visited[cur-k*k]=true;
}
}
q.pop();
}
step++;
}
return step;
}
};
3.打开转盘锁
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
示例 1:
输入:deadends = [“0201”,“0101”,“0102”,“1212”,“2002”], target = “0202”
输出:6
解释:
可能的移动序列为 “0000” -> “1000” -> “1100” -> “1200” -> “1201” -> “1202” -> “0202”。
注意 “0000” -> “0001” -> “0002” -> “0102” -> “0202” 这样的序列是不能解锁的,
因为当拨动到 “0102” 时这个锁就会被锁定。
//思路:对于每个状态,接下来的可能状态有8个,所以,处理当前元素时,先判断是否为目标值,不是的话,将其出队,然后将其下一个状态可能值入队,并用step记录是第几次。
//tip:加个记忆序列,防止重复运算
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
int step=0;
queue<string> q;
//记忆序列,记录是否出现过
unordered_set<string> seen = {"0000"};
unordered_set<string> dead(deadends.begin(), deadends.end());
q.push("0000");
if(dead.count("0000")) return -1;
while(!q.empty()){
int size=q.size();
for(int k=0;k<size;k++){
string cur=q.front();
// status.push_back(cur);
if(cur.compare(target)==0) return step;
q.pop();
vector<string> next=get_str(cur);
for(int i=0;i<next.size();i++){
if(!dead.count(next[i])){
if(!seen.count(next[i])){
q.push(next[i]);
seen.insert(move(next[i]));
}
// q.push(next[i]);
}
}
}
step++;
// if(step>3){
// return step;
// }
}
return -1;
}
//查找接下来的8个可能状态
vector <string> get_str(string cur){
vector<string> result;
int step=0;
while(step<4){
string s1(cur);
string s2(cur);
if(s1[step]=='9'){
s1[step]='0';
}else{
s1[step]=char(s1[step]+1);
};
if(s2[step]=='0'){
s2[step]='9';
}else{
s2[step]=char(s2[step]-1);
};
result.push_back(s1);
result.push_back(s2);
step=step+1;
}
return result;
}
};