目录
min-stack
evaluate-reverse-polish-notation
decode-string
binary-tree-inorder-traversal
clone-graph
number-of-islands
largest-rectangle-in-histogram
implement-queue-using-stacks
01-matrix
栈
特点是先入后出,利用这个特点可以保存一些临时数据,之后用到再以此弹出,常用于DFS深度搜索。
队列是先用先出,常用于BFS广度搜索,类似层次遍历。
下面根据一些例题来学习下栈的用法。
设计一个支持push、pop、top操作,并且能在常数时间内检索到最小元素的栈。
用两个栈实现,一个栈始终保证最小值在顶部。
class MinStack{
private:
stack<int> normalStack;
stack<int> min_stack;
public:
MinStack(){
min_stack.push(INT_MAX);
}
void push(int x){
normalStack.push(x);
min_stack.push(min_stack.top(), x);
}
void pop(){
normalStack.pop();
min_stack.pop();
}
int top(){
return normalStack.top();
}
int getMin(){
return min_stack.top();
}
};
evaluate-reverse-polish-notation
根据逆波兰表达式求解,逆波兰表达式 > 输入[“2”, “1”, “+”, “3”, “*”] > 输出:9 ,((2+1)*3) = 9
通过栈保存原来的元素,遇到表达式弹出运算,再压入结果,重复这个过程。
int evalRPN(vector<string>& token){
if(tokens.size() == 0) return 0;
stack<int> res;
for(string s : tokens){
if(s != "+" && s != "-" && s != "*" && s != "/"){
res.push(stoi(s));
}else{
int b = res.top();
res.pop();
int a = res.top();
res.pop();
if(s == '+') res.push(a + b);
if(s == '-') res.push(a - b);
if(s == '*') res.push(a * b);
if(s == '/') res.push(a / b);
}
}
return res.top();
}
给定一个经过编码的字符串,返回它解码后的字符串
s = ‘3[a]2[bc]’ ’ -> ‘aaabcbc’
通过栈进行操作
string decodeString(string s){
string res;
int num = 0;
stack<int> stackInt;
stack<string> stackStr;
for(char c : s){
if(c == '['){
stackInt.push(num);
num = 0;
stackStr.push(res);
res = "";
}else if(c == ']'){
int times = stackInt.top(); // 重复次数
stackInt.pop();
for(int i = 0; i < times; i++){
stackStr.top() += res;
}
res = stackStr.top();
stackStr.pop();
}else if(c >= '0' && c <= '9'){
num = num * 10 + c - '0';
}else{
res = res + c;
}
}
}
栈进行DFS递归搜索
栈进行递归搜索的模板
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;
}
二叉树中序遍历
使用栈保存已经访问过的节点,用于原路返回
vector<int> inorderTraversal(TreeNode* root){
vector<int> res;
if(root == NULL) return res;
stack<TreeNode*> s;
s.push(root);
while(!s.empty()){
TreeNode *top = s.top();
s.pop();
if(top != NULL){
// 中序遍历 左根右
if(top->right) s.push(top->right);
s.push(top);
s.push(NULL); // 插入空节点标记访问过
if(top->left) s.push(top->left);
}else{
res.push_back(s.top()->val);
s.pop();
}
}
return res;
}
给一个无向连通图一个节点的引用,返回改图的深拷贝
unordered_map<Node*, Node*> mp; // 标记访问过的节点
Node* cloneGraph(Node* node){
if(node == NULL) return node;
if(mp.count(node)) return mp[node]; //结束递归
const auto newNode = new Node(node->val);
mp[node] = newNode;
for(auto n : node->neighbors){
mp[node]->neighbors.push_back(cloneGraph(n));
}
return mp[node];
}
给定一个由’1’陆地和’0’水组成的二维网络,计算岛屿的数量。一个岛屿被水包围,并且它是通过水平或者垂直方向上与陆地连接而成,你可以假设网络的四个边均被水包围。
通过深度搜索遍历所有的可能性(标记已经访问过的节点)
这是经典的题目,面试中很常见
int numIslands(vector<vector<char>>& grid){
int count = 0;
for(int i = 0; i < grid.size(); i++){
for(int j = 0; j < grid[0].size(); j++){
if(grid[i][j] == '1'){
dfs(grid, i, j);
count++;
}
}
}
return count;
}
void dfs(vector<vector<char>>& grid, int i, int j){
if(i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size()){
return ; //超出边界
}
grid[i][j] = '2'; //标记访问过
dfs(grid, i+1, j);
dfs(grid, i-1, j);
dfs(grid, i, j+1);
dfs(grid, i, j-1);
}
largest-rectangle-in-histogram
给定n个非负整数,用来表示柱状图中各个柱子的高度,每个柱子彼此相邻,且宽度为1,求在该柱状图中,能勾勒出来的矩形的最大面积。
思路:求当前柱子为高度的最大面积,即转化为寻找小于当前值的左右两边值
用到了单调栈
int largestRectangleArea(vector<int>& heights){
//基于各个高度的最大矩形是在出栈的时候计算,所以必须让所有高度都出栈
//利用单调栈的性质,在原始数组后添加一个0
heights.push_back(0);
stack<int> s; //栈
int maxArea = 0;
for(int i = 0; i < heights.size(); i++){
while(!s.empty() && heights[i] < heights[s.top]){
int h = heights[s.top()];
s.pop();
int w = s.empty() ? i : i - s.top() - 1;
maxArea = max(maxArea, h*w);
}
s.push(i);
}
return maxArea;
}
队列
常用于BFS宽度搜索
常见题型:
implement-queue-using-stacks
使用两个栈实现队列
class MyQueue{
public:
stack<int> inStack;
stack<int> outStack;
MyQueue(){
}
void push(int x){
inStack.push(x);
}
int pop(){
cheak(); //将inStack栈内元素移动到outStack
int top = outStack.top();
outStack.pop();
return top;
}
int peek(){
//得到队首元素
cheak();
return outStack.top();
}
bool empty(){
return inStack.empty() && outStack.empty();
}
void cheak(){
if(outStack.empty()){
while(!inStack.empty()){
outStack.push(inStack.top());
inStack.pop();
}
}
}
};
二叉树层次遍历模板
vector<vector<int>> levelOrder(TreeNode* root){
vector<vector<int>> res;
if(root == NULL) return res;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int size = q.size();
res.push_back(vector<int>());
for(int i = 0; i < size; i++){
TreeNode *top = q.front();
res.back().push_back(top->val);
q.pop();
if(top->left) q.push(top->left);
if(top->right) q.push(rop->right);
}
}
return res;
}
给定一个由0和1组成的矩阵,找出每个元素到最近的0的距离,两个相邻元素间的距离是1
01矩阵问题,采用BFS方法求解
从0进入队列,弹出之后计算上下左右的结果,将上下左右重新进队列进行二层操作
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix){
int row = matrix.size(), col = matrix[0].size();
vector<pair<int,int>> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
vector<vector<int>> res(row, vector<int>(col, INT_MAX));
queue<pair<int, int>> q;
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
if(matrix[i][j] == 0){
res[i][j] = 0;
q.push({i, j});
}
}
}
while(!q.empty()){
auto temp = q.front();
q.pop();
for(int i = 0; i < 4; i++){
int x = temp.first + directions[i].first;
int y = temp.second + directions[i].second;
if(x >= 0 && x < row && y >= 0 && y <= col){
// 保证在边界内
if(res[x][y] > res[temp.first][temp.second] + 1){
res[x][y] = res[temp.first][temp.second] + 1;
q.push({x, y});
}
}
}
}
return res;
}
总结
- 知道栈的使用场景
- 先入后出,保存临时值
- 利用栈实现DFS
- 熟悉队列的使用场景
- 利用队列实现BFS