栈和队列
Last updated
Last updated
栈的特点是后入先出
根据这个特点可以临时保存一些数据,之后用到依次再弹出来,常用于 DFS 深度搜索
队列一般常用于 BFS 广度搜索,类似一层一层的搜索
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
思路:用两个栈实现,一个最小栈始终保证最小值在顶部
class MinStack {
List<int> stack = [];
List<int> minstack = [];
void push(int val) {
stack.add(val);
if (minstack.isEmpty) {
minstack.add(val);
} else {
minstack.add(min(minstack.last, val));
}
}
void pop() {
stack.removeLast();
minstack.removeLast();
}
int top() {
return stack.last;
}
int getMin() {
return minstack.last;
}
}
evaluate-reverse-polish-notation
波兰表达式计算 > 输入:
["2", "1", "+", "3", "*"]
> 输出: 9解释:
((2 + 1) * 3) = 9
思路:通过栈保存原来的元素,遇到表达式弹出运算,再推入结果,重复这个过程
int evalRPN(List<String> tokens) {
List<String> stack = [];
for(int i = 0; i < tokens.length;i++){
if(tokens[i] == '+' || tokens[i] == '-' || tokens[i] == '*' || tokens[i] == '/'){
int num1 = int.parse(stack.removeLast());
int num2 = int.parse(stack.removeLast());
if(tokens[i] == '+' ){
stack.add((num1 + num2).toString());
}
if(tokens[i] == '-' ){
stack.add((num2 - num1).toString());
}
if(tokens[i] == '*' ){
stack.add((num2 * num1).toString());
}
if(tokens[i] == '/' ){
stack.add((num2 ~/ num1).toString());
}
} else {
stack.add(tokens[i]);
}
}
return int.parse(stack.last);
}
给定一个经过编码的字符串,返回它解码后的字符串。 s = "3[a]2[bc]", 返回 "aaabcbc". s = "3[a2[c]]", 返回 "accaccacc". s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".
思路:通过栈辅助进行操作
String decodeString(String str) {
int length = str.length;
List<String> stackStr = [];
for(int i = 0 ; i < length;i++){
String temp = str[i];
if(temp == "]"){
//1.弹出[]里面所有字母 保存在s中
String strCode = '';
while(stackStr.isNotEmpty && stackStr.last != "[") {
strCode = stackStr.removeLast() + strCode;
}
//2.弹出'['
if(stackStr.isNotEmpty && stackStr.last == "[")stackStr.removeLast();
//3.弹出所有的数字
String strNum = '';
while(stackStr.isNotEmpty) {
String temp = stackStr.last;
if (int.tryParse(temp) != null){
strNum = temp + strNum ;
stackStr.removeLast();
} else {
break;
}
}
//4.将数字 * 字符串 展开,然后压入栈中
String strAll = '';
int result = int.parse(strNum);
for(int i = 0;i < result;i++){
strAll = strAll + strCode;
}
stackStr.add(strAll);
} else {
stackStr.add(temp);
}
}
return stackStr.join();
}
给定一个二叉树,返回它的中序遍历。
//递归的方式
List<int> res = [];
List<int> inorderTraversal(TreeNode? root) {
dfs(root);
return res;
}
void dfs(TreeNode? root) {
if (root == null) return;
dfs(root.left);
res.add(root.val!);
dfs(root.right);
}
//非递归的方式
//思考一下递归的方式,首先会先调用root.left 不停的访问下去。
//直到节点为null,这时候开始访问右边节点.访问右边节点的时候,
//又会递归,首先访问root.left,不停的访问下去。
List<int> inorderTraversal(TreeNode? root) {
List<int> res = [];
List<TreeNode> stackNode = [];
while(stackNode.isNotEmpty || root != null){
while(root != null){
stackNode.add(root);
root= root.left;
}
TreeNode node = stackNode.removeLast();
res.add(node.val!);
root = node.right;
}
return res;
}
给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 思路:通过深度搜索遍历可能性(注意标记已访问元素)
class Solution {
int numIslands(List<List<String>> grid) {
int m = grid.length; // 网格的行数
int n = grid[0].length; // 网格的列数
int count = 0; // 岛屿数量的计数器
// 四个方向的偏移量,用于遍历网格的相邻单元格
List<List<int>> directions = [ [1, 0], // 向下
[-1, 0], // 向上
[0, 1], // 向右
[0, -1], // 向左
];
// 将指定的单元格标记为已访问,并将其添加到队列中
void markVisited(int row, int col, List<List<int>> queue) {
grid[row][col] = '0'; // 将单元格标记为已访问
queue.add([row, col]); // 将单元格添加到队列中
}
// 遍历指定单元格的相邻单元格,并将未访问的单元格添加到队列中
void traverseAdjacentCells(int row, int col, List<List<int>> queue) {
for (List<int> direction in directions) {
int r = row + direction[0]; // 计算相邻单元格的行坐标
int c = col + direction[1]; // 计算相邻单元格的列坐标
// 如果相邻单元格在网格范围内且未被访问,则标记为已访问并添加到队列中
if (r >= 0 && r < m && c >= 0 && c < n && grid[r][c] == '1') {
markVisited(r, c, queue);
}
}
}
// 遍历整个网格,对每个未被访问的岛屿进行 BFS 遍历,并增加岛屿数量计数器
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') { // 如果单元格是陆地而未被访问
count++; // 岛屿数量加1
List<List<int>> queue = []; // 创建一个队列用于 BFS 遍历
markVisited(i, j, queue); // 标记当前单元格为已访问,并添加到队列中
while (queue.isNotEmpty) { // 当队列不为空时进行 BFS 遍历
List<int> curr = queue.removeAt(0); // 从队列中取出一个单元格
int row = curr[0]; // 取出该单元格的行坐标
int col = curr[1]; // 取出该单元格的列坐标
traverseAdjacentCells(row, col, queue); // 遍历
}
}
}
}
return count; // 返回岛屿数量
}
}
largest-rectangle-in-histogram
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。
思路:求以当前柱子为高度的面积,即转化为寻找小于当前值的左右两边值
用栈保存小于当前值的左的元素
class Solution {
int largestRectangleArea(List<int> heights) {
// 使用单调栈来维护每个柱子的左右边界
List<int> stack = [];
int maxArea = 0;
int i = 0;
while (i < heights.length) {
// 如果栈为空或者当前柱子高度大于栈顶柱子的高度,则将当前柱子的下标压入栈中
if (stack.isEmpty || heights[i] >= heights[stack.last]) {
stack.add(i);
i++;
} else {
// 如果当前柱子高度小于栈顶柱子的高度,则弹出栈顶柱子
int topIndex = stack.removeLast();
// 计算弹出的栈顶柱子对应的最大面积,并更新maxArea
int area = heights[topIndex] *
(stack.isEmpty ? i : i - stack.last - 1);
maxArea = max(maxArea, area);
}
}
// 处理栈中剩余的柱子
while (!stack.isEmpty) {
int topIndex = stack.removeLast();
int area = heights[topIndex] *
(stack.isEmpty ? i : i - stack.last - 1);
maxArea = max(maxArea, area);
}
return maxArea;
}
}
常用于 BFS 宽度优先搜索
使用栈实现队列
class MyQueue {
List<int> inStack = [];
List<int> outStack = [];
void push(int x) {
inStack.add(x);
}
int pop() {
if (outStack.isNotEmpty) {
return outStack.removeLast();
} else {
while (inStack.isNotEmpty) {
outStack.add(inStack.removeLast());
}
return outStack.removeLast();
}
}
int peek() {
if (outStack.isNotEmpty) {
return outStack.last;
} else {
while (inStack.isNotEmpty) {
outStack.add(inStack.removeLast());
}
return outStack.last;
}
}
bool empty() {
return inStack.isEmpty && outStack.isEmpty;
}
}
二叉树层次遍历
class TreeNode {
int? val;
TreeNode? left;
TreeNode? right;
}
List<List<int>> levelOrder(TreeNode? root) {
List<List<int>> result = [[]];
if (root == null) {
return result;
}
List<TreeNode> queue = [];
queue.add(root);
while (queue.isNotEmpty) {
int count = queue.length;
List<int> temp = [];
//一层一层的移除元素
for (int i = 0; i < count; i++) {
TreeNode node = queue.removeAt(0);
temp.add(node.val!);
if (node.left != null) {
queue.add(node.left!);
}
if (node.right != null) {
queue.add(node.right!);
}
}
result.add(temp);
}
return result;
}
给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。 两个相邻元素间的距离为 1
//多源BFS问题:
//对于二叉树来说只有一个起点,所以二叉树在进行BFS的时候,只需要把根节点加入到queue里
//对于图来说 有多个点可以做为起点,所以要把所有起点加入queue,并且要记录已经访问的节点
//防止重复访问
List<List<int>> updateMatrix(List<List<int>> mat) {
int row = mat.length;
int col = mat[0].length;
List<List<int>> queue = [];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (mat[i][j] == 0) {
queue.add([i, j]);
} else {
mat[i][j] = -1;
}
}
}
List<int> xD = [1, 0, -1, 0];
List<int> yD = [0, 1, 0, -1];
while (queue.isNotEmpty) {
List<int> pos = queue.removeAt(0);
for (int i = 0; i < 4; i++) {
int x = pos[0] + xD[i];
int y = pos[1] + yD[i];
if (x >= 0 && x < row && y >= 0 && y < col && mat[x][y] == -1) {
mat[x][y] = mat[pos[0]][pos[1]] + 1;
queue.add([x, y]);
}
}
}
return mat;
}
熟悉栈的使用场景
后入先出,保存临时值
利用栈 DFS 深度搜索
熟悉队列的使用场景
利用队列 BFS 广度搜索