在 FIFO 数据结构中,将首先处理添加到队列中的第一个元素
。html
如上图所示,队列是典型的 FIFO 数据结构。插入(insert)操做也称做入队(enqueue),新元素始终被添加在队列的末尾
。 删除(delete)操做也被称为出队(dequeue)。 你只能移除第一个元素
。java
示例 - 队列node
Enqueue
以查看如何将新元素 6 添加到队列中。
Dequeue
以查看将删除哪一个元素。
为了实现队列,咱们可使用动态数组和指向队列头部的索引。ios
如上所述,队列应支持两种操做:入队和出队。入队会向队列追加一个新元素,而出队会删除第一个元素。 因此咱们须要一个索引来指出起点。c++
这是一个供你参考的实现:git
#include <iostream> #include <vector> using namespace std; class MyQueue { private: // store elements vector<int> data; // a pointer to indicate the start position int p_start; public: MyQueue() {p_start = 0;} /** Insert an element into the queue. Return true if the operation is successful. */ bool enQueue(int x) { data.push_back(x); return true; } /** Delete an element from the queue. Return true if the operation is successful. */ bool deQueue() { if (isEmpty()) { return false; } p_start++; return true; }; /** Get the front item from the queue. */ int Front() { return data[p_start]; }; /** Checks whether the queue is empty or not. */ bool isEmpty() { return p_start >= data.size(); } }; int main() { MyQueue q; q.enQueue(5); q.enQueue(3); if (!q.isEmpty()) { cout << q.Front() << endl; } q.deQueue(); if (!q.isEmpty()) { cout << q.Front() << endl; } q.deQueue(); if (!q.isEmpty()) { cout << q.Front() << endl; } }
缺点算法
上面的实现很简单,但在某些状况下效率很低。 随着起始指针的移动,浪费了愈来愈多的空间。 当咱们有空间限制时,这将是难以接受的。数组
让咱们考虑一种状况,即咱们只能分配一个最大长度为 5 的数组。当咱们只添加少于 5 个元素时,咱们的解决方案颇有效。 例如,若是咱们只调用入队函数四次后还想要将元素 10 入队,那么咱们能够成功。数据结构
可是咱们不能接受更多的入队请求,这是合理的,由于如今队列已经满了。可是若是咱们将一个元素出队呢?less
实际上,在这种状况下,咱们应该可以再接受一个元素。
此前,咱们提供了一种简单但低效的队列实现。
更有效的方法是使用循环队列。 具体来讲,咱们可使用固定大小的数组
和两个指针
来指示起始位置和结束位置。 目的是重用
咱们以前提到的被浪费的存储
。
让咱们经过一个示例来查看循环队列的工做原理。 你应该注意咱们入队
或出队
元素时使用的策略。
仔细检查动画,找出咱们用来检查队列是空
仍是满
的策略。
下一个练习,咱们将让你本身尝试实现循环队列,以后会提供给你一个解决方案。
设计你的循环队列实现。 循环队列是一种线性数据结构,其操做表现基于 FIFO(先进先出)原则而且队尾被链接在队首以后以造成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是咱们能够利用这个队列以前用过的空间。在一个普通队列里,一旦一个队列满了,咱们就不能插入下一个元素,即便在队列前面仍有空间。可是使用循环队列,咱们能使用这些空间去存储新的值。
你的实现应该支持以下操做:
MyCircularQueue(k)
: 构造器,设置队列长度为 k 。Front
: 从队首获取元素。若是队列为空,返回 -1 。Rear
: 获取队尾元素。若是队列为空,返回 -1 。enQueue(value)
: 向循环队列插入一个元素。若是成功插入则返回真。deQueue()
: 从循环队列中删除一个元素。若是成功删除则返回真。isEmpty()
: 检查循环队列是否为空。isFull()
: 检查循环队列是否已满。示例:
MyCircularQueue circularQueue = new MycircularQueue(3); // 设置长度为 3 circularQueue.enQueue(1); // 返回 true circularQueue.enQueue(2); // 返回 true circularQueue.enQueue(3); // 返回 true circularQueue.enQueue(4); // 返回 false,队列已满 circularQueue.Rear(); // 返回 3 circularQueue.isFull(); // 返回 true circularQueue.deQueue(); // 返回 true circularQueue.enQueue(4); // 返回 true circularQueue.Rear(); // 返回 4
提示:
#include <iostream> #include <vector> using namespace std; /// One more space implementation /// Time Complexity: O(1) /// Space Complexity: O(n) class MyCircularQueue { private: int front, tail; vector<int> data; public: MyCircularQueue(int k) { front = tail = 0; data.clear(); for (int i = 0; i <= k; i++) //这里多分配了一个空间 data.push_back(-1); } bool enQueue(int value) { if (isFull()) return false; data[tail] = value; tail = (tail + 1) % data.size(); return true; } bool deQueue() { if (isEmpty()) return false; front = (front + 1) % data.size(); return true; } int Front(){ if(isEmpty()) return -1; return data[front]; } int Rear() { if (isEmpty()) return -1; int index = tail - 1; if (index < 0) index += data.size(); return data[index]; } bool isEmpty() { return front == tail; } bool isFull() { return (tail + 1) % data.size() == front; } };
在循环队列中,咱们使用一个数组
和两个指针(head
和 tail
)。 head
表示队列的起始位置,tail
表示队列的结束位置。
这里咱们提供了代码供你参考:
class MyCircularQueue { private: vector<int> data; int head; int tail; int size; public: /** Initialize your data structure here. Set the size of the queue to be k. */ MyCircularQueue(int k) { data.resize(k); head = -1; tail = -1; size = k; } /** Insert an element into the circular queue. Return true if the operation is successful. */ bool enQueue(int value) { if (isFull()) { return false; } if (isEmpty()) { head = 0; //第一次入队,下标将从0开始。 } tail = (tail + 1) % size; //以后只需调整队尾指针。 data[tail] = value; return true; } /** Delete an element from the circular queue. Return true if the operation is successful. */ bool deQueue() { if (isEmpty()) { return false; } if (head == tail) { //这里与下面的判断方式不一样 head = -1; tail = -1; return true; } head = (head + 1) % size; return true; } /** Get the front item from the queue. */ int Front() { if (isEmpty()) { return -1; } return data[head]; } /** Get the last item from the queue. */ int Rear() { if (isEmpty()) { return -1; } return data[tail]; } /** Checks whether the circular queue is empty or not. */ bool isEmpty() { return head == -1; //这不是用head == tail判断 } /** Checks whether the circular queue is full or not. */ bool isFull() { return ((tail + 1) % size) == head; } }; /** * Your MyCircularQueue object will be instantiated and called as such: * MyCircularQueue obj = new MyCircularQueue(k); * bool param_1 = obj.enQueue(value); * bool param_2 = obj.deQueue(); * int param_3 = obj.Front(); * int param_4 = obj.Rear(); * bool param_5 = obj.isEmpty(); * bool param_6 = obj.isFull(); */
大多数流行语言都提供内置的队列库,所以您无需从新发明轮子。
如前所述,队列有两个重要的操做,入队 enqueue
和出队 dequeue
。 此外,咱们应该可以得到队列中的第一个元素
,由于应该首先处理它。
下面是使用内置队列库及其常见操做的一些示例:
#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; }
咱们在本文以后提供了练习,以帮助你熟悉这些操做。请记住,当你想要按顺序处理元素
时,使用队列多是一个很好的选择。
*到了这一题,发现要开会员才能看到题目-_-b,因而我就去百度找题目了。*
Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window. For example, MovingAverage m = new MovingAverage(3); m.next(1) = 1 m.next(10) = (1 + 10) / 2 m.next(3) = (1 + 10 + 3) / 3 m.next(5) = (10 + 3 + 5) / 3 给一个整数流和一个窗口,计算在给定大小的窗口里的数字的平均值。 解法:队列queue,用一个queue记录进入窗口的整数。当流进窗口的整数不足时,计算全部窗口内的数字和返回,当进入窗口的整数多于窗口大小时,移除最早进入窗口的整数,新的整数进入queue,而后计算窗口内的整数和。
#include <iostream> #include <queue> using namespace std; /// Using Queue /// Time Complexity: O(1) /// Space Complexity: O(size) class MovingAverage { private: queue<int> q; int sz, sum; public: MovingAverage(int size) { sz = size; sum = 0; } double next(int val) { if (q.size() == sz) { sum -= q.front(); q.pop(); } sum += val; q.push(val); return (double)sum / q.size(); } }
广度优先搜索(BFS)的一个常见应用是找出从根结点到目标结点的最短路径。在本文中,咱们提供了一个示例来解释在 BFS 算法中是如何逐步应用队列的。
示例
这里咱们提供一个示例来讲明如何使用 BFS 来找出根结点 A
和目标结点 G
之间的最短路径。
洞悉
观看上面的动画后,让咱们回答如下问题:
1. 结点的处理顺序是什么?
在第一轮中,咱们处理根结点。在第二轮中,咱们处理根结点旁边的结点;在第三轮中,咱们处理距根结点两步的结点;等等等等。
与树的层序遍历相似,越是接近根结点的结点将越早地遍历
。
若是在第 k 轮中将结点 X
添加到队列中,则根结点与 X
之间的最短路径的长度刚好是 k
。也就是说,第一次找到目标结点时,你已经处于最短路径中。
2. 队列的入队和出队顺序是什么?
如上面的动画所示,咱们首先将根结点排入队列。而后在每一轮中,咱们逐个处理已经在队列中的结点,并将全部邻居添加到队列中。值得注意的是,新添加的节点不会
当即遍历,而是在下一轮中处理。
结点的处理顺序与它们添加
到队列的顺序是彻底相同的顺序
,即先进先出(FIFO)。这就是咱们在 BFS 中使用队列的缘由。
以前,咱们已经介绍了使用 BFS 的两个主要方案:遍历
或找出最短路径
。一般,这发生在树或图中。正如咱们在章节描述中提到的,BFS 也能够用于更抽象的场景中。
在本文中,咱们将为你提供一个模板。而后,咱们在本文后提供一些习题供你练习。
在特定问题中执行 BFS 以前肯定结点和边缘很是重要。一般,结点将是实际结点或是状态,而边缘将是实际边缘或可能的转换。
模板I
在这里,咱们为你提供伪代码做为模板:
/** * 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 (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 }
等待处理的结点
。while
循环以后,咱们距离根结点更远一步
。变量 step
指示从根结点到咱们正在访问的当前结点的距离。模板 II
有时,确保咱们永远不会访问一个结点两次
很重要。不然,咱们可能陷入无限循环。若是是这样,咱们能够在上面的代码中添加一个哈希集来解决这个问题。这是修改后的伪代码:
/** * 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 Set<Node> used; // store all the used nodes int step = 0; // number of steps neeeded from root to current node // initialize add root to queue; add root to used; // BFS while (queue is not empty) { step = step + 1; // iterate the nodes which are already in the queue int size = queue.size(); 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) { if (next is not in used) { add next to queue; add next to used; } } remove the first node from queue; } } return -1; // there is no path from root to target }
有两种状况你不须要使用哈希集:
- 你彻底肯定没有循环,例如,在树遍历中;
- 你确实但愿屡次将结点添加到队列中。
You are given a m x n 2D grid initialized with these three possible values.
-1
- A wall or an obstacle.0
- A gate.INF
- Infinity means an empty room. We use the value 231 - 1 = 2147483647
to represent INF
as you may assume that the distance to a gate is less than 2147483647
.Fill each empty room with the distance to its nearest gate. If it is impossible to reach a gate, it should be filled with INF
.
For example, given the 2D grid:
INF -1 0 INF INF INF INF -1 INF -1 INF -1 0 -1 INF INF
After running your function, the 2D grid should be:
3 -1 0 1 2 2 1 -1 1 -1 2 -1 0 -1 3 4
这道题相似一种迷宫问题,规定了-1表示墙,0表示门,让求每一个点到门的最近的曼哈顿距离,这其实相似于求距离场Distance Map的问题,那么咱们先考虑用DFS来解,思路是,咱们搜索0的位置,每找到一个0,以其周围四个相邻点为起点,开始DFS遍历,并带入深度值1,若是遇到的值大于当前深度值,咱们将位置值赋为当前深度值,并对当前点的四个相邻点开始DFS遍历,注意此时深度值须要加1,这样遍历完成后,全部的位置就被正确地更新了,参见代码以下:
解法一:
class Solution { public: void wallsAndGates(vector<vector<int>>& rooms) { for (int i = 0; i < rooms.size(); ++i) { for (int j = 0; j < rooms[i].size(); ++j) { if (rooms[i][j] == 0) dfs(rooms, i, j, 0); } } } void dfs(vector<vector<int>>& rooms, int i, int j, int val) { if (i < 0 || i >= rooms.size() || j < 0 || j >= rooms[i].size() || rooms[i][j] < val) return; rooms[i][j] = val; dfs(rooms, i + 1, j, val + 1); dfs(rooms, i - 1, j, val + 1); dfs(rooms, i, j + 1, val + 1); dfs(rooms, i, j - 1, val + 1); } };
那么下面咱们再来看BFS的解法,须要借助queue,咱们首先把门的位置都排入queue中,而后开始循环,对于门位置的四个相邻点,咱们判断其是否在矩阵范围内,而且位置值是否大于上一位置的值加1,若是知足这些条件,咱们将当前位置赋为上一位置加1,并将次位置排入queue中,这样等queue中的元素遍历完了,全部位置的值就被正确地更新了,参见代码以下:
解法二:
class Solution { public: void wallsAndGates(vector<vector<int>>& rooms) { queue<pair<int, int>> q; vector<vector<int>> dirs{{0, -1}, {-1, 0}, {0, 1}, {1, 0}}; for (int i = 0; i < rooms.size(); ++i) { for (int j = 0; j < rooms[i].size(); ++j) { if (rooms[i][j] == 0) q.push({i, j}); } } while (!q.empty()) { int i = q.front().first, j = q.front().second; q.pop(); for (int k = 0; k < dirs.size(); ++k) { int x = i + dirs[k][0], y = j + dirs[k][1]; if (x < 0 || x >= rooms.size() || y < 0 || y >= rooms[0].size() || rooms[x][y] < rooms[i][j] + 1) continue; rooms[x][y] = rooms[i][j] + 1; q.push({x, y}); } } } };
给定一个由 '1'
(陆地)和 '0'
(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,而且它是经过水平方向或垂直方向上相邻的陆地链接而成的。你能够假设网格的四个边均被水包围。
示例 1:
输入: 11110 11010 11000 00000 输出: 1
示例 2:
输入: 11000 11000 00100 00011 输出: 3
思路:只需判断陆地有没有跟已发现的岛屿相邻,若是没有相邻,则是新的岛屿。
/// Source : https://leetcode.com/problems/number-of-islands/description/ /// Author : liuyubobobo /// Time : 2018-08-25 #include <iostream> #include <vector> #include <cassert> #include <queue> using namespace std; /// Floodfill - BFS /// Time Complexity: O(n*m) /// Space Complexity: O(n*m) class Solution { private: int d[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; //分别表示上、右、下、左 int m, n; public: int numIslands(vector<vector<char>>& grid) { m = grid.size(); if(m == 0) return 0; n = grid[0].size(); if(n == 0) return 0; vector<vector<bool>> visited(m, vector<bool>(n, false)); int res = 0; for(int i = 0 ; i < m ; i ++) for(int j = 0 ; j < n ; j ++) if(grid[i][j] == '1' && !visited[i][j]){ bfs(grid, i, j, visited); res ++; } return res; } private: void bfs(vector<vector<char>>& grid, int x, int y, vector<vector<bool>>& visited){ queue<pair<int, int>> q; q.push(make_pair(x, y)); visited[x][y] = true; while(!q.empty()){ int curx = q.front().first; int cury = q.front().second; q.pop(); for(int i = 0; i < 4; i ++){ int newX = curx + d[i][0]; int newY = cury + d[i][1]; if(inArea(newX, newY) && !visited[newX][newY] && grid[newX][newY] == '1'){ q.push(make_pair(newX, newY)); visited[newX][newY] = true; } } } return; } bool inArea(int x, int y){ return x >= 0 && x < m && y >= 0 && y < n; } }; int main() { vector<vector<char>> grid1 = { {'1','1','1','1','0'}, {'1','1','0','1','0'}, {'1','1','0','0','0'}, {'0','0','0','0','0'} }; cout << Solution().numIslands(grid1) << endl; // 1 // --- vector<vector<char>> grid2 = { {'1','1','0','0','0'}, {'1','1','0','0','0'}, {'0','0','1','0','0'}, {'0','0','0','1','1'} }; cout << Solution().numIslands(grid2) << endl; // 3 return 0; }
你有一个带有四个圆形拨轮的转盘锁。每一个拨轮都有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" 时这个锁就会被锁定。
示例 2:
输入: deadends = ["8888"], target = "0009" 输出:1 解释: 把最后一位反向旋转一次便可 "0000" -> "0009"。
示例 3:
输入: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888" 输出:-1 解释: 没法旋转到目标数字且不被锁定。
示例 4:
输入: deadends = ["0000"], target = "8888" 输出:-1
提示:
deadends
的长度范围为 [1, 500]
。target
不会在 deadends
之中。deadends
和 target
中的字符串的数字会在 10,000 个可能的状况 '0000'
到 '9999'
中产生。思路:等价于八领域的迷宫问题,并绕过死锁区域。字符和数字的转换经过加 '0' 实现。
/// Source : https://leetcode.com/problems/open-the-lock/description/ /// Author : liuyubobobo /// Time : 2017-12-23 #include <iostream> #include <vector> #include <set> #include <queue> #include <cassert> using namespace std; /// BFS /// Time Complexity: O(charset^N) /// Space Complexity: O(charset^N) class Solution { public: int openLock(vector<string>& deadends, string target) { set<string> dead; for(string s: deadends) dead.insert(s); if(dead.find(target) != dead.end() || dead.find("0000") != dead.end()) return -1; set<string> visited; queue<pair<string, int>> q; q.push(make_pair("0000", 0)); visited.insert("0000"); while(!q.empty()){ string cur = q.front().first; int step = q.front().second; q.pop(); vector<string> next = getNext(cur, dead); for(string next_s: next) if(visited.find(next_s) == visited.end()){ if(next_s == target) return step + 1; visited.insert(next_s); q.push(make_pair(next_s, step + 1)); } } return -1; } private: vector<string> getNext(const string& s, const set<string>& dead){ vector<string> res; assert(s.size() == 4); for(int i = 0 ; i < 4 ; i ++){ int num = s[i] - '0'; int d = num + 1; if(d > 9) d = 0; string t = s; t[i] = ('0' + d); if(dead.find(t) == dead.end()) res.push_back(t); d = num - 1; if(d < 0) d = 9; t = s; t[i] = ('0' + d); if(dead.find(t) == dead.end()) res.push_back(t); } return res; } }; int main() { vector<string> dead1 = {"0201","0101","0102","1212","2002"}; string target1 = "0202"; cout << Solution().openLock(dead1, target1) << endl; vector<string> dead2 = {"8888"}; string target2 = "0009"; cout << Solution().openLock(dead2, target2) << endl; vector<string> dead3 = {"8887","8889","8878","8898","8788","8988","7888","9888"}; string target3 = "8888"; cout << Solution().openLock(dead3, target3) << endl; vector<string> dead4 = {"1002","1220","0122","0112","0121"}; string target4 = "1200"; cout << Solution().openLock(dead4, target4) << endl; return 0; }
给定正整数 n,找到若干个彻底平方数(好比 1, 4, 9, 16, ...
)使得它们的和等于 n。你须要让组成和的彻底平方数的个数最少。
示例 1:
输入: n = 12 输出: 3 解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13 输出: 2 解释: 13 = 4 + 9.
思路:该问题可转化为求图的无权最短路径,即正整数n到0间是否存在一条最短路径,路径上节点数目最少。所以采用BFS,率先抵达0的路径即为所求。此外,重复出现的节点,可不加入图中。以下图中第一个7若能到达0,则以后再出现的7也能抵达0,而且第一次出现的7所历经的层数必定比以后的7要少,无需重复计算。
/// Source : https://leetcode.com/problems/perfect-squares/description/ /// Author : liuyubobobo /// Time : 2017-11-17 #include <iostream> #include <vector> #include <queue> #include <stdexcept> using namespace std; /// BFS /// Time Complexity: O(n) /// Space Complexity: O(n) class Solution { public: int numSquares(int n) { if(n == 0) return 0; queue<pair<int, int>> q; q.push(make_pair(n, 0)); vector<bool> visited(n + 1, false); visited[n] = true; while(!q.empty()){ int num = q.front().first; int step = q.front().second; q.pop(); for(int i = 1; num - i * i >= 0; i ++){ int a = num - i * i; if(!visited[a]){ if(a == 0) return step + 1; q.push(make_pair(a, step + 1)); visited[a] = true; } } } throw invalid_argument("No Solution."); } }; int main() { cout << Solution().numSquares(12) << endl; cout << Solution().numSquares(13) << endl; return 0; }
在 LIFO 数据结构中,将首先处理添加到队列
中的最新元素
。
与队列不一样,栈是一个 LIFO 数据结构。一般,插入操做在栈中被称做入栈 push
。与队列相似,老是在堆栈的末尾添加一个新元素
。可是,删除操做,退栈 pop
,将始终删除
队列中相对于它的最后一个元素
。
示例 - 栈
入栈:你能够单击下面的 Push
按钮查看如何将新元素 6 添加到栈中。
退栈:你能够单击下面的 Pop
按钮查看当你从栈中弹出一个元素时将移除哪一个元素。
实现 - 栈
#include <iostream> class MyStack { private: vector<int> data; // store elements public: /** Insert an element into the stack. */ void push(int x) { data.push_back(x); } /** Checks whether the queue is empty or not. */ bool isEmpty() { return data.empty(); } /** Get the top item from the queue. */ int top() { return data.back(); } /** Delete an element from the queue. Return true if the operation is successful. */ bool pop() { if (isEmpty()) { return false; } data.pop_back(); return true; } }; int main() { MyStack s; s.push(1); s.push(2); s.push(3); for (int i = 0; i < 4; ++i) { if (!s.isEmpty()) { cout << s.top() << endl; } cout << (s.pop() ? "true" : "false") << endl; } }
大多数流行的语言都提供了内置的栈库,所以你没必要从新发明轮子。除了初始化
,咱们还须要知道如何使用两个最重要的操做:入栈
和退栈
。除此以外,你应该可以从栈中得到顶部元素
。下面是一些供你参考的代码示例:
#include <iostream> int main() { // 1. Initialize a stack. stack<int> s; // 2. Push new element. s.push(5); s.push(13); s.push(8); s.push(6); // 3. Check if stack is empty. if (s.empty()) { cout << "Stack is empty!" << endl; return 0; } // 4. Pop an element. s.pop(); // 5. Get the top element. cout << "The top element is: " << s.top() << endl; // 6. Get the size of the stack. cout << "The size is: " << s.size() << endl; }
从如今开始,咱们可使用内置的栈库来更方便地解决问题。 让咱们从一个有趣的问题(最小栈)开始,帮助你复习有用的操做。 而后咱们将看一些经典的栈问题。 当你想首先处理最后一个元素时,栈将是最合适的数据结构。
设计一个支持 push,pop,top 操做,并能在常数时间内检索到最小元素的栈。
示例:
MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin(); --> 返回 -2.
思路:有两个栈:最小栈和普通栈。最小栈的元素个数和普通栈同样,最小栈中的元素为普通栈对应位置前全部元素的最小值。
class MinStack { private: stack<int> normalStack; stack<int> minStack; public: MinStack() { while (!normalStack.empty()) normalStack.pop(); while (!minStack.empty()) minStack.pop(); } void push(int x) { normalStack.push(x); if (minStack.empty()) minStack.push(x); else minStack.push(min(minStack.top(), x)); } int pop() { assert(normalStack.size() > 0); int v = normalStack.top(); normalStack.pop(); minStack.pop(); return v; } int top() { assert(normalStack.size() > 0); return normalStack.top(); } int getMin() { assert(normalStack.size() > 0); return minStack.top(); } };
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串,判断字符串是否有效。
有效字符串需知足:
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()" 输出: true
示例 2:
输入: "()[]{}" 输出: true
示例 3:
输入: "(]" 输出: false
示例 4:
输入: "([)]" 输出: false
示例 5:
输入: "{[]}" 输出: true
#include <iostream> #include <stack> #include <cassert> using namespace std; // Using Stack // Time Complexity: O(n) // Space Complexity: O(n) class Solution { public: bool isValid(string s) { stack<char> stack; for (int i = 0; i < s.size(); i++) if (s[i] == '(' || s[i] == '{' || s[i] == '[') stack.push(s[i]); else { if (stack.size() == 0) return false; char c = stack.top(); stack.pop(); char match; if (s[i] == ')') match = '('; else if (s[i] == ']') match = '['; else { assert(s[i] == '}'); match = '{'; } if (c != match) return false; } if (stack.size() != 0) return false; return true; } }; void printBool(bool res){ cout << (res? "True" : "False") << endl; } int main() { printBool(Solution().isValid("()")); printBool(Solution().isValid("()[]{}")); printBool(Solution().isValid("(]")); printBool(Solution().isValid("([)]")); return 0; }
根据每日 气温
列表,请从新生成一个列表,对应位置的输入是你须要再等待多久温度才会升高的天数。若是以后都不会升高,请输入 0
来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
,你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]
。
提示:气温
列表长度的范围是 [1, 30000]
。每一个气温的值的都是 [30, 100]
范围内的整数。
思路:从后往前遍历每一个节点,用一个栈stack来存放值比自身大 且 离本身最近的节点下标
/// Source : https://leetcode.com/problems/daily-temperatures/description/ /// Author : liuyubobobo /// Time : 2018-08-28 #include <iostream> #include <vector> #include <stack> using namespace std; /// Using Stack /// Time Complexity: O(n) /// Space Complexity: O(n) class Solution { public: vector<int> dailyTemperatures(vector<int>& temperatures) { vector<int> res(temperatures.size(), 0); stack<int> stack; for(int i = temperatures.size() - 1; i >= 0 ; i --){ while(!stack.empty() && temperatures[stack.top()] <= temperatures[i]) stack.pop(); if(!stack.empty()) res[i] = stack.top() - i; stack.push(i); } return res; } }; void printVec(const vector<int>& vec){ for(int e: vec) cout << e << " "; cout << endl; } int main() { vector<int> vec = {73, 74, 75, 71, 69, 72, 76, 73}; printVec(Solution().dailyTemperatures(vec)); // 1 1 4 2 1 1 0 0 return 0; }
根据逆波兰表示法,求表达式的值。
有效的运算符包括 +
, -
, *
, /
。每一个运算对象能够是整数,也能够是另外一个逆波兰表达式。
说明:
示例 1:
输入: ["2", "1", "+", "3", "*"] 输出: 9 解释: ((2 + 1) * 3) = 9
示例 2:
输入: ["4", "13", "5", "/", "+"] 输出: 6 解释: (4 + (13 / 5)) = 6
示例 3:
输入: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"] 输出: 22 解释: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22
#include <iostream> #include <stack> #include <vector> using namespace std; /// Two stacks /// Time Complexity: O(n) /// Space Complexity: O(n) class Solution { public: int evalRPN(vector<string> &tokens) { stack<int> nums; stack<char> ops; for(const string& s: tokens){ if(s == "+" || s=="-" || s=="*" || s=="/"){ int a = nums.top(); nums.pop(); int b = nums.top(); nums.pop(); if(s == "+") nums.push(b + a); else if(s == "-") nums.push(b- a); else if(s=="*") nums.push(b*a); else if(s=="/") nums.push(b/a); } else nums.push(atoi(s.c_str())); } return nums.top(); } }; int main(){ return 0; }
与 BFS 相似,深度优先搜索
(DFS)也可用于查找从根结点到目标结点的路径。在本文中,咱们提供了示例来解释 DFS 是如何工做的以及栈是如何逐步帮助 DFS 工做的。
示例
咱们来看一个例子吧。咱们但愿经过 DFS 找出从根结点 A
到目标结点 G
的路径。
洞悉
观看上面的动画后,让咱们回答如下问题:
1. 结点的处理顺序是什么?
在上面的例子中,咱们从根结点 A
开始。首先,咱们选择结点 B
的路径,并进行回溯,直到咱们到达结点 E
,咱们没法更进一步深刻。而后咱们回溯到 A
并选择第二条路径到结点 C
。从 C
开始,咱们尝试第一条路径到 E
可是 E
已被访问过。因此咱们回到 C
并尝试从另外一条路径到 F
。最后,咱们找到了 G
。
总的来讲,在咱们到达最深的
结点以后,咱们只
会回溯并尝试另外一条路径。
所以,你在 DFS 中找到的第一条路径并不老是最短的路径。例如,在上面的例子中,咱们成功找出了路径
A-> C-> F-> G
并中止了 DFS。但这不是从A
到G
的最短路径。
2. 栈的入栈和退栈顺序是什么?
如上面的动画所示,咱们首先将根结点推入到栈中;而后咱们尝试第一个邻居 B
并将结点 B
推入到栈中;等等等等。当咱们到达最深的结点 E
时,咱们须要回溯。当咱们回溯时,咱们将从栈中弹出最深的结点
,这其实是推入到栈中的最后一个结点
。
结点的处理顺序是彻底相反的顺序
,就像它们被添加
到栈中同样,它是后进先出(LIFO)。这就是咱们在 DFS 中使用栈的缘由。
正如咱们在本章的描述中提到的,在大多数状况下,咱们在能使用 BFS 时也可使用 DFS。可是有一个重要的区别:遍历顺序
。
与 BFS 不一样,更早访问的结点可能不是更靠近根结点的结点
。所以,你在 DFS 中找到的第一条路径可能不是最短路径
。
在本文中,咱们将为你提供一个 DFS 的递归模板,并向你展现栈是如何帮助这个过程的。在这篇文章以后,咱们会提供一些练习给你们练习。
模板-递归
有两种实现 DFS 的方法。第一种方法是进行递归,这一点你可能已经很熟悉了。这里咱们提供了一个模板做为参考:
/* * Return true if there is a path from cur to target. */ boolean DFS(Node cur, Node target, Set<Node> visited) { return true if cur is target; for (next : each neighbor of cur) { if (next is not in visited) { add next to visted; return true if DFS(next, target, visited) == true; } } return false; }
示例
让咱们看一个例子。咱们但愿在下图中找到结点 0 和结点 3 之间的路径。咱们还会在每次调用期间显示栈的状态。
在每一个堆栈元素中,都有一个整数 cur
,一个整数 target
,一个对访问过的
数组的引用和一个对数组边界
的引用,这些正是咱们在 DFS 函数中的参数。咱们只在上面的栈中显示 cur
。
每一个元素都须要固定的空间。栈的大小正好是 DFS 的深度。所以,在最坏的状况下,维护系统栈须要 O(h),其中 h 是 DFS 的最大深度。在计算空间复杂度时,永远不要忘记考虑系统栈。
在上面的模板中,咱们在找到
第一条
路径时中止。若是你想找到
最短
路径呢?提示:再添加一个参数来指示你已经找到的最短路径。
给定一个由 '1'
(陆地)和 '0'
(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,而且它是经过水平方向或垂直方向上相邻的陆地链接而成的。你能够假设网格的四个边均被水包围。
示例 1:
输入: 11110 11010 11000 00000 输出: 1
示例 2:
输入: 11000 11000 00100 00011 输出: 3
#include <iostream> #include <vector> #include <cassert> using namespace std; /// Floodfill - DFS /// Recursion implementation /// /// Time Complexity: O(n*m) /// Space Complexity: O(n*m) class Solution { private: int d[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; int m, n; vector<vector<bool>> visited; bool inArea(int x, int y) { return x >= 0 && x < m && y >= 0 && y < n; } void dfs(vector<vector<char>> &grid, int x, int y) { visited[x][y] = true; for (int i = 0; i < 4; i++) { int newx = x + d[i][0]; int newy = y + d[i][1]; if (inArea(newx, newy) && !visited[newx][newy] && grid[newx][newy] == '1') dfs(grid, newx, newy); } return ; } public: int numIslands(vector<vector<char>>& grid){ m = grid.size(); if(m == 0) return 0; n = grid[0].size(); if(n == 0) return 0; for(int i=0; i<m; i++) visited.push_back(vector<bool>(n, false)); int res = 0; for(int i=0; i<m; i++) for(int j=0; j<n; j++) if(grid[i][j]=='1' && !visited[i][j]){ dfs(grid, i,j); res++; } return res; } }; int main() { char g1[4][5] = { {'1','1','1','1','0'}, {'1','1','0','1','0'}, {'1','1','0','0','0'}, {'0','0','0','0','0'} }; vector<vector<char>> grid1; for(int i = 0 ; i < 4 ; i ++) grid1.push_back( vector<char>(g1[i], g1[i] + sizeof( g1[i])/sizeof(char))); cout << Solution().numIslands(grid1) << endl; // 1 // --- char g2[4][5] = { {'1','1','0','0','0'}, {'1','1','0','0','0'}, {'0','0','1','0','0'}, {'0','0','0','1','1'} }; vector<vector<char>> grid2; for(int i = 0 ; i < 4 ; i ++) grid2.push_back(vector<char>(g2[i], g2[i] + sizeof( g2[i])/sizeof(char))); cout << Solution().numIslands(grid2) << endl; // 2 return 0; }
给定无向连通图中一个节点的引用,返回该图的深拷贝(克隆)。图中的每一个节点都包含它的值 val
(Int
) 和其邻居的列表(list[Node]
)。
示例:
输入: {"$id":"1","neighbors":[{"$id":"2","neighbors":[{"$ref":"1"},{"$id":"3","neighbors":[{"$ref":"2"},{"$id":"4","neighbors":[{"$ref":"3"},{"$ref":"1"}],"val":4}],"val":3}],"val":2},{"$ref":"4"}],"val":1} 解释: 节点 1 的值是 1,它有两个邻居:节点 2 和 4 。 节点 2 的值是 2,它有两个邻居:节点 1 和 3 。 节点 3 的值是 3,它有两个邻居:节点 2 和 4 。 节点 4 的值是 4,它有两个邻居:节点 1 和 3 。
提示:
#include <iostream> #include <vector> #include <unordered_map> using namespace std; struct UndirectedGraphNode { int label; vector<UndirectedGraphNode *> neighbors; UndirectedGraphNode(int x) : label(x){}; }; /// DFS /// Time Complexity: O(V+E) /// Space Complexity: O(V) class Solution { public: UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) { if (node == NULL) return NULL; unordered_map<UndirectedGraphNode *, UndirectedGraphNode *> nodeMap; return dfs(node, nodeMap); } private: UndirectedGraphNode *dfs(UndirectedGraphNode *node, unordered_map<UndirectedGraphNode *, UndirectedGraphNode *> &nodeMap) { if (nodeMap.count(node)) return nodeMap[node]; nodeMap[node] = new UndirectedGraphNode(node->label); for (UndirectedGraphNode *next : node->neighbors) nodeMap[node]->neighbors.push_back(dfs(next, nodeMap)); return nodeMap[node]; } };
给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。如今你有两个符号 +
和 -
。对于数组中的任意一个整数,你均可以从 +
或 -
中选择一个符号添加在前面。
返回可使最终数组和为目标数 S 的全部添加符号的方法数。
示例 1:
输入: nums: [1, 1, 1, 1, 1], S: 3 输出: 5 解释: -1+1+1+1+1 = 3 +1-1+1+1+1 = 3 +1+1-1+1+1 = 3 +1+1+1-1+1 = 3 +1+1+1+1-1 = 3 一共有5种方法让最终目标和为3。
注意:
#include <iostream> #include <vector> #include <stack> using namespace std; /// Backtracking /// Time Complexity: O(2^n) /// Space Complexity: O(n) class SolutionA { public: int findTargetSumWays(vector<int> &nums, int S) { return dfs(nums, 0, 0, S); } private: int dfs(const vector<int>& nums, int index, int res, int S){ if(index == nums.size()) return res==S; int ret = 0; ret += dfs(nums, index+1, res-nums[index], S); ret += dfs(nums, index+1, res+nums[index],S); return ret; } }; /// Backtracking /// Using Stack - Non-recursion solution /// /// Time Complexity: O(2^n) /// Space Complexity: O(2^n) class SolutionB { public: int findTargetSumWays(vector<int> &nums, int S) { stack<int> indexStack, sumStack; indexStack.push(0); sumStack.push(0); int res = 0, index, sum; while (!indexStack.empty()) { index = indexStack.top(); sum = sumStack.top(); indexStack.pop(); sumStack.pop(); if (index + 1 == nums.size()) res += (sum + nums[index] == S) + (sum - nums[index] == S); else { indexStack.push(index + 1); sumStack.push(sum + nums[index]); indexStack.push(index + 1); sumStack.push(sum - nums[index]); } } return res; } }; int main() { vector<int> nums = {18, 1}; cout << SolutionA().findTargetSumWays(nums, 3) << endl; return 0; }
递归解决方案的优势是它更容易实现。 可是,存在一个很大的缺点:若是递归的深度过高,你将遭受堆栈溢出。 在这种状况下,您可能会但愿使用 BFS,或使用显式栈实现 DFS。
这里咱们提供了一个使用显式栈的模板:
/* * Return true if there is a path from cur to target. */ boolean DFS(int root, int target) { Set<Node> visited; Stack<Node> s; add root to s; while (s is not empty) { Node cur = the top element in s; return true if cur is target; for (Node next : the neighbors of cur) { if (next is not in visited) { add next to s; add next to visited; } } remove cur from s; } return false; }
该逻辑与递归解决方案彻底相同。 但咱们使用 while
循环和栈
来模拟递归期间的系统调用栈
。 手动运行几个示例确定会帮助你更好地理解它。
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2]
进阶: 递归算法很简单,你能够经过迭代算法完成吗?
#include <iostream> #include <vector> #include <stack> using namespace std; struct TreeNode{ int val; TreeNode* left; TreeNode* right; TreeNode(int x):val(x), left(NULL), right(NULL){} }; // Recursive // Time Complexity: O(n), n is the node number in the tree // Space Complexity: O(h), h is the height of the tree class SolutionA{ public: vector<int> inorderTraversal(TreeNode* root){ vector<int> res; __inorderTraversal(root, res); return res; } private: void __inorderTraversal(TreeNode* node, vector<int> & res){ if(node){ __inorderTraversal(node->left, res); res.push_back(node->val); __inorderTraversal(node->right, res); } } }; // Classic Non-Recursive algorithm for inorder traversal // Time Complexity: O(n), n is the node number in the tree // Space Complexity: O(h), h is the height of the tree class SolutionB{ public: vector<int> inorderTraversal(TreeNode* root){ vector<int> res; if(root == NULL) return res; stack<TreeNode*> stack; TreeNode* cur = root; while(cur != NULL || !stack.empty()){ while(cur != NULL){ stack.push(cur); cur = cur->left; } cur = stack.top(); stack.pop(); res.push_back(cur->val); cur = cur->right; } return res; } }; int main(){ return 0; }
使用栈实现队列的下列操做:
示例:
MyQueue queue = new MyQueue(); queue.push(1); queue.push(2); queue.peek(); // 返回 1 queue.pop(); // 返回 1 queue.empty(); // 返回 false
说明:
push to top
, peek/pop from top
, size
, 和 is empty
操做是合法的。使用队列实现栈的下列操做:
注意:
push to back
, peek/pop from front
, size
, 和 is empty
这些操做是合法的。#include <iostream> #include <queue> #include <cassert> using namespace std; /// Naive Implementation /// Time Complexity: init: O(1) /// push: O(1) /// pop: O(n) /// top: O(n) /// empty: O(1) /// Space Complexity: O(n) class MyStack{ private: queue<int> q; public: MyStack(){} void push(int x){ q.push(x); } int pop(){ assert(!empty()); queue<int> q2; while(q.size() > 1){ q2.push(q.front()); q.pop(); } int ret = q.front(); q.pop(); while(!q2.empty()){ q.push(q2.front()); q2.pop(); } return ret; } int top(){ assert(!empty()); queue<int> q2; while(q.size() > 1){ q2.push(q.front()); q.pop(); } int ret = q.front(); q2.push(ret); q.pop(); while(!q2.empty()){ q.push(q2.front()); q2.pop(); } return ret; } bool empty(){ return q.empty(); } };
给定一个通过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string]
,表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你能够认为输入字符串老是有效的;输入字符串中没有额外的空格,且输入的方括号老是符合格式要求的。
此外,你能够认为原始数据不包含数字,全部的数字只表示重复的次数 k ,例如不会出现像 3a
或 2[4]
的输入。
示例:
s = "3[a]2[bc]", 返回 "aaabcbc". s = "3[a2[c]]", 返回 "accaccacc". s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".
#include <iostream> #include <cassert> #include <vector> using namespace std; /// Using Stack - non recursion algorithm /// Time Complexity: O(n) /// Space Complexity: O(n) class Solution { public: string decodeString(string s) { vector<string> stack; vector<int> nums; stack.push_back(""); for (int i = 0; i < s.size();) if (isalpha(s[i])) stack.back() += s[i++]; else if (isdigit(s[i])) { int j; for (j = i + 1; j < s.size() && isdigit(s[j]); j++) ; nums.push_back(atoi(s.substr(i, j - i).c_str())); stack.push_back(""); assert(s[j] == '['); i = j + 1; } else { assert(s[i] == ']'); string tres = stack.back(); stack.pop_back(); int num = nums.back(); nums.pop_back(); while (num--) stack.back() += tres; i++; } return stack.back(); } }; int main() { string s1 = "3[a]2[bc]"; cout << Solution().decodeString(s1) << endl; // "aaabcbc" string s2 = "3[a2[c]]"; cout << Solution().decodeString(s2) << endl; // "accaccacc" string s3 = "2[abc]3[cd]ef"; cout << Solution().decodeString(s3) << endl; // "abcabccdcdcdef" return 0; }
有一幅以二维整数数组表示的图画,每个整数表示该图画的像素值大小,数值在 0 到 65535 之间。
给你一个坐标 (sr, sc)
表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newColor
,让你从新上色这幅图像。
为了完成上色工做,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将全部有记录的像素点的颜色值改成新的颜色值。
最后返回通过上色渲染后的图像。
示例 1:
输入: image = [[1,1,1],[1,1,0],[1,0,1]] sr = 1, sc = 1, newColor = 2 输出: [[2,2,2],[2,2,0],[2,0,1]] 解析: 在图像的正中间,(坐标(sr,sc)=(1,1)), 在路径上全部符合条件的像素点的颜色都被更改为2。 注意,右下角的像素没有更改成2, 由于它不是在上下左右四个方向上与初始点相连的像素点。
注意:
image
和 image[0]
的长度在范围 [1, 50]
内。0 <= sr < image.length
和 0 <= sc < image[0].length
。image[i][j]
和 newColor
表示的颜色值在范围 [0, 65535]
内。#include <iostream> #include <vector> #include <stack> using namespace std; /// DFS - Using Stack /// /// Time Complexity: O(n*m) /// Space Complexity: O(n*m) class Solution { private: int d[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; int n, m; public: vector<vector<int>> floodFill(vector<vector<int>> &image, int sr, int sc, int newColor) { n = image.size(); m = image[0].size(); int oldColor = image[sr][sc]; vector<vector<bool>> visited(n, vector<bool>(m, false)); stack<pair<int, int>> stack; stack.push(make_pair(sr, sc)); visited[sr][sc] = true; while (!stack.empty()) { int x = stack.top().first, y = stack.top().second; stack.pop(); image[x][y] = newColor; for (int i = 0; i < 4; i++) { int newX = x + d[i][0], newY = y + d[i][1]; if (inArea(newX, newY) && !visited[newX][newY] && image[newX][newY] == oldColor) { visited[newX][newY] = true; stack.push(make_pair(newX, newY)); } } } return image; } private: bool inArea(int x, int y) { return x >= 0 && x < n && y >= 0 && y < m; } }; void printImage(const vector<vector<int>> & image){ for(vector<int> row:image){ for(int pixel: row) cout << pixel<<"\t"; cout << endl; } } int main(){ vector<vector<int>> image = {{1,1,1}, {1,1,0}, {1,0,1}}; printImage(Solution().floodFill(image, 1, 1, 2)); return 0; }
给定一个由 0 和 1 组成的矩阵,找出每一个元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
示例 1:
输入:
0 0 0 0 1 0 0 0 0
输出:
0 0 0 0 1 0 0 0 0
示例 2:
输入:
0 0 0 0 1 0 1 1 1
输出:
0 0 0 0 1 0 1 2 1
注意:
#include <iostream> #include <vector> #include <queue> #include <cassert> using namespace std; /// BFS /// Put all zero position in queue and only make one pass BFS :-) /// /// Time Complexity: O(m*n) /// Space Complexity: O(m*n) class Solution { private: const int d[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; int m, n; public: vector<vector<int>> updateMatrix(vector<vector<int>> &matrix) { if (matrix.size() == 0 || matrix[0].size() == 0) return matrix; m = matrix.size(); n = matrix[0].size(); queue<int> q; vector<vector<int>> res(m, vector<int>(n, INT_MAX)); for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) if (matrix[i][j] == 0) { res[i][j] = 0; q.push(i * n + j); } bfs(matrix, q, res); return res; } void bfs(const vector<vector<int>> &matrix, queue<int> &q, vector<vector<int>> &res) { while (!q.empty()) { int x = q.front() / n; int y = q.front() % n; q.pop(); for (int k = 0; k < 4; k++) { int newX = x + d[k][0], newY = y + d[k][1]; if (inArea(newX, newY) && res[x][y] + 1 < res[newX][newY]) { res[newX][newY] = res[x][y] + 1; q.push(newX * n + newY); } } } } bool inArea(int x, int y) { return x >= 0 && x < m && y >= 0 && y < n; } };
有 N
个房间,开始时你位于 0
号房间。每一个房间有不一样的号码:0,1,2,...,N-1
,而且房间里可能有一些钥匙能使你进入下一个房间。
在形式上,对于每一个房间 i
都有一个钥匙列表 rooms[i]
,每一个钥匙 rooms[i][j]
由 [0,1,...,N-1]
中的一个整数表示,其中 N = rooms.length
。 钥匙 rooms[i][j] = v
能够打开编号为 v
的房间。
最初,除 0
号房间外的其他全部房间都被锁住。
你能够自由地在房间之间来回走动。
若是能进入每一个房间返回 true
,不然返回 false
。
示例 1:
输入: [[1],[2],[3],[]] 输出: true 解释: 咱们从 0 号房间开始,拿到钥匙 1。 以后咱们去 1 号房间,拿到钥匙 2。 而后咱们去 2 号房间,拿到钥匙 3。 最后咱们去了 3 号房间。 因为咱们可以进入每一个房间,咱们返回 true。
示例 2:
输入:[[1,3],[3,0,1],[2],[0]] 输出:false 解释:咱们不能进入 2 号房间。
提示:
1 <= rooms.length <= 1000
0 <= rooms[i].length <= 1000
3000
。class Solution{ public: bool canVisitAllRooms(vector<vector<int>>& rooms){ int V = rooms.size(); vector<bool> visited(V, false); return dfs(rooms, 0, visited) == V; } private: int dfs(const vector<vector<int>>& rooms, int v, vector<bool>& visited){ visited[v] = true; int res = 1; for(int next: rooms[v]) if(!visited[next]) res += dfs(rooms, next, visited); return res; } };