当前位置 博文首页 > Shockang的博客:LeetCode 刷题汇总之栈
本文隶属于专栏《LeetCode 刷题汇总》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构请见LeetCode 刷题汇总
看到括号,第一反应一定是栈
Java 和 Scala 版本的栈推荐都使用 ArrayDeque,其中 Scala 也可以使用 ListBuffer,不过 ArrayDeque 的 API 更友好一点
class Solution {
public boolean isValid(String s) {
ArrayDeque<Character> stack = new ArrayDeque<>();
for (char c : s.toCharArray()) {
if (c == '(')
stack.push(')');
else if (c == '{')
stack.push('}');
else if (c == '[')
stack.push(']');
else if (stack.isEmpty() || stack.pop() != c)
return false;
}
return stack.isEmpty();
}
}
import java.util.ArrayDeque
object Solution {
def isValid(s: String): Boolean = {
val stack = new ArrayDeque[Char]()
for (c <- s) {
c match {
case '(' => stack.push(')')
case '[' => stack.push(']')
case '{' => stack.push('}')
case _ => if (stack.isEmpty || stack.pop() != c) return false
}
}
stack.isEmpty
}
}
接雨水这道题的思路:暴力解法( O(n^2) )-> 动态规划 3 次遍历( O(n) )-> 单调栈 1 次遍历( O(n) )-> 双指针 1 次遍历 ( O(n) )
选择单调栈是因为遍历过程中满足单调性(栈底到栈顶递减)
接雨水中的凹槽口也很容易想到数学函数中的极小值,进而想到单调性 -> 单调栈
class Solution {
public int trap(int[] height) {
int n = height.length, res = 0;
ArrayDeque<Integer> stack = new ArrayDeque<>();
for(int i = 0; i < n; i++){
while(!stack.isEmpty() && height[i] > height[stack.peek()]){
// 出栈操作发生在凹槽口的底点,即由递减到递增的转折点,当然这里只是方便理解,后续可能需要计算出不同高度的雨水量
int top = stack.pop();
if(stack.isEmpty()){
break;
}
int left = stack.peek();
// 这里推荐使用特值法来推断
int width = i - left - 1;
// 对于凹槽口的底点来说,左右的较小值减去当前点的高度,即为雨水的高度
int h = Math.min(height[i], height[left]) - height[top];
res += width * h;
}
stack.push(i);
}
return res;
}
}
import java.util.ArrayDeque
import scala.util.control.Breaks._
object Solution {
def trap(height: Array[Int]): Int = {
if (height == null || height.isEmpty) return 0
val stack = new ArrayDeque[Int]()
var top, distance, boundedHeight, ans = 0
for (current <- height.indices) {
breakable(
while (!stack.isEmpty && height(current) > height(stack.peek)) {
top = stack.pop()
if (stack.isEmpty) break
distance = current - stack.peek() - 1
boundedHeight = math.min(height(current), height(stack.peek())) - height(top)
ans += distance * boundedHeight
}
)
stack.push(current)
}
ans
}
}
借助栈结构,然后在遍历的过程中对单点和双点特殊处理下
import java.util.StringJoiner;
class Solution {
public String simplifyPath(String path) {
String[] paths = path.split("/");
ArrayDeque<String> stack = new ArrayDeque<>();
for(String p: paths){
if(!p.isEmpty() && !p.equals(".")){
if(p.equals("..") && !stack.isEmpty()){
stack.pop();
}else if(!p.equals("..")){
stack.push(p);
}
}
}
StringJoiner sj = new StringJoiner("/");
// 这里我们借助 ArrayList 来逆序栈
List<String> res = new ArrayList<>(stack);
Collections.reverse(res);
for(String s: res){
sj.add(s);
}
return "/" + sj;
}
}
import scala.collection.mutable.ListBuffer
object Solution {
def simplifyPath(path: String): String = {
val paths = path.split("/")
val res = ListBuffer[String]()
for (p <- paths if p.nonEmpty && !p.equals(".")) {
if (p.equals("..") && res.nonEmpty) res.remove(res.size - 1)
else if (!p.equals("..")) res += p
}
"/" + res.mkString("/")
}
}
这里使用 ListBuffer 代码更简洁
class Solution {
public int largestRectangleArea(int[] heights) {
int len = heights.length;
ArrayDeque<Integer> s = new ArrayDeque<>();
int maxArea = 0;
for (int i = 0; i <= len; i++){
int h = (i == len ? 0 : heights[i]);
if (