栈的设计与算法例题
qingmei2 opened this issue · comments
栈的设计与算法例题
栈的实现
栈的实现比队列容易。动态数组 足以实现堆栈结构。这里LeetCode
官方提供了一个简单的实现供参考:
// "static void main" must be defined in a public class.
class MyStack {
private List<Integer> data; // store elements
public MyStack() {
data = new ArrayList<>();
}
/** Insert an element into the stack. */
public void push(int x) {
data.add(x);
}
/** Checks whether the queue is empty or not. */
public boolean isEmpty() {
return data.isEmpty();
}
/** Get the top item from the queue. */
public int top() {
return data.get(data.size() - 1);
}
/** Delete an element from the queue. Return true if the operation is successful. */
public boolean pop() {
if (isEmpty()) {
return false;
}
data.remove(data.size() - 1);
return true;
}
};
例题
155. 最小栈
- 难度:
Easy
题目描述
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x) -- 将元素 x 推入栈中。
- pop() -- 删除栈顶的元素。
- top() -- 获取栈顶元素。
- getMin() -- 检索栈中的最小元素。
解题思路及实现
官方 栈和队列 标签中,对栈的实现方式描述为:
栈的实现比队列容易。动态数组足以实现堆栈结构。
// 官方实现的描述,直接使用了ArrayList
class MyStack {
private List<Integer> data; // store elements
public MyStack() {
data = new ArrayList<>();
}
}
因此做本题的时候,下意识觉得使用 ArrayList
就足够了,结果发现不通过,原因是题目中额外要求 在常数时间内检索到最小元素,而我则是 暴力循环遍历所有元素找到最小值 , 时间复杂度为O(N)
, 这样当然不行。
原来 getMin()
函数有时间限制,那么用一个成员记录最小值的状态应该就可以了吧...
试了一下竟然真的可以,这也算偷奸耍滑通过了......(我只是把循环的操作从getMin()
转移到了pop()
)...
class MinStack {
private List<Integer> data;
// 1.用一个成员记录栈内最小值
private int min = Integer.MAX_VALUE;
public MinStack() {
data = new ArrayList<>();
}
// 2.写入时,看情况更新最小值
public void push(int x) {
if (min > x) {
min = x;
}
data.add(x);
}
// 3.出栈时,更新最小值,这里时间复杂度为 O(N)
public void pop() {
verifyNotEmpty();
data.remove(data.size() - 1);
min = Integer.MAX_VALUE;
for (Integer num : data) {
if (min > num) {
min = num;
}
}
}
public int top() {
verifyNotEmpty();
return data.get(data.size() - 1);
}
// 4.这样就能保证,获取最小值的函数,执行时间为常数时间了 =w=
public int getMin() {
verifyNotEmpty();
return min;
}
private void verifyNotEmpty() {
if (data.isEmpty()) {
throw new IllegalArgumentException("Stack is empty");
}
}
}
20. 有效的括号
- 难度:
Easy
题目描述
给定一个只包括 '(',')','{','}','[',']'
的字符串,判断字符串是否有效。
有效字符串需满足:
- 1.左括号必须用相同类型的右括号闭合。
- 2.左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
解题思路及实现
说实话栈相关的题目还是不太好第一时间联想到的,这里解决方案参考官方题解:
https://leetcode-cn.com/problems/valid-parentheses/solution/you-xiao-de-gua-hao-by-leetcode/
实现代码:
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
Map<Character, Character> mapping = new HashMap<>();
mapping.put('}', '{');
mapping.put(')', '(');
mapping.put(']', '[');
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (mapping.containsKey(c)) {
// 闭括号
char top = stack.isEmpty() ? '#' : stack.pop();
if (top != mapping.get(c)) {
return false;
}
} else {
// 新的开符号,直接压入栈中
stack.push(c);
}
}
return stack.isEmpty();
}
}
739. 每日温度
- 难度:
Medium
题目描述
根据每日气温列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0
来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
,你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]
。
提示:气温 列表长度的范围是 [1, 30000]
。每个气温的值的均为华氏度,都是在 [30, 100]
范围内的整数。
解题思路及实现
这道题难点在于题目的理解,题目看懂了,其实解决起来比较简单。
1.暴力破解法
暴力破解法是最直接想到的方式,时间复杂度 O(N^2)
:
class Solution {
public int[] dailyTemperatures(int[] T) {
int[] ans = new int[T.length];
for (int i = 0; i < T.length; i++) {
// 最后一个元素,对应一定是0
if (i == T.length - 1) {
ans[T.length - 1] = 0;
continue;
}
int times = 0;
for (int j = i + 1; j < T.length; j++) {
if (T[j] > T[i]) {
ans[i] = ++times;
break;
}
if (j == T.length - 1) {
ans[i] = 0;
}
times++;
}
}
return ans;
}
}
2.使用栈
说实话这个解决方案真的没想到,并且是题目标签提示了的,参考官方题解:
https://leetcode-cn.com/problems/daily-temperatures/solution/mei-ri-wen-du-by-leetcode/
class Solution {
public int[] dailyTemperatures(int[] T) {
int[] ans = new int[T.length];
Stack<Integer> stack = new Stack();
for (int i = T.length - 1; i >= 0; --i) {
while (!stack.isEmpty() && T[i] >= T[stack.peek()]) stack.pop();
ans[i] = stack.isEmpty() ? 0 : stack.peek() - i;
stack.push(i);
}
return ans;
}
}
通过使用和动态的更新栈,将数据在数组中的索引作为栈的元素进行处理,使得代码极大的被简化,并且思路更高级,因为每个索引最多做一次压栈和出栈的操作。因此时间复杂度为 O(N)
。
150. 逆波兰表达式求值
- 难度:
Medium
题目描述
根据逆波兰表示法,求表达式的值。
有效的运算符包括 +, -, *, /
。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
- 整数除法只保留整数部分。
- 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为
0
的情况。
解题思路及实现
同样是使用栈解决,将数字元素压入栈,遇到符号时,从栈顶取出两个元素进行求值,并将结果再次存入栈顶,最终将栈顶的结果弹出进行返回即可:
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
Integer i1, i2;
for (String s : tokens) {
switch (s) {
case "+":
i1 = stack.pop();
i2 = stack.pop();
stack.push(i2 + i1);
break;
case "-":
i1 = stack.pop();
i2 = stack.pop();
stack.push(i2 - i1);
break;
case "*":
i1 = stack.pop();
i2 = stack.pop();
stack.push(i2 * i1);
break;
case "/":
i1 = stack.pop();
i2 = stack.pop();
stack.push(i2 / i1);
break;
default:
stack.push(Integer.parseInt(s));
break;
}
}
return stack.pop();
}
}
参考 & 感谢
文章绝大部分内容节选自LeetCode
,概述:
例题:
- https://leetcode-cn.com/problems/min-stack/
- https://leetcode-cn.com/problems/valid-parentheses/
- https://leetcode-cn.com/problems/daily-temperatures/
- https://leetcode-cn.com/problems/evaluate-reverse-polish-notation
关于我
Hello,我是 却把清梅嗅 ,如果您觉得文章对您有价值,欢迎 ❤️,也欢迎关注我的 博客 或者 GitHub。
如果您觉得文章还差了那么点东西,也请通过关注督促我写出更好的文章——万一哪天我进步了呢?