232.用栈实现队列
【分析】时间push/emptyO(1)pop/peekO(n),空间O(n)
时间复杂度:push 和 empty 为 O(1),pop 和 peek 为均摊 O(1)。对于每个元素,至多入栈和出栈各两次,故均摊复杂度为 O(1)。
空间复杂度:O(n)。其中 n 是操作总数。对于有 n 次 push 操作的情况,队列中会有 n 个元素,故空间复杂度为 O(n)。
【思想】栈与队列的特性:FILO、FIFO
用两个栈才能模拟队列的FIFO
java
class MyQueue {
private LinkedList<Integer> stOut;
private LinkedList<Integer> stIn;
public MyQueue() {
stOut = new LinkedList<>();
stIn = new LinkedList<>();
}
public void push(int x) {
stIn.push(x);
}
public int pop() {
transferIfEmpty();
return stOut.pop();
}
public int peek() {
transferIfEmpty();
return stOut.peek();
}
public boolean empty() {
return stIn.isEmpty() && stOut.isEmpty();
}
private void transferIfEmpty() {
while(stOut.isEmpty()) {
while(!stIn.isEmpty()) {
stOut.push(stIn.pop());
}
}
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
225. 用队列实现栈
【分析】
时间:入栈操作 O(n),其余操作都是 O(1),其中 n 是栈内的元素个数。
- 入栈操作需要将队列中的 n 个元素出队,并入队 n+1 个元素到队列,共有 2n+1 次操作,每次出队和入队操作的时间复杂度都是 O(1),因此入栈操作的时间复杂度是 O(n)。
- 出栈操作对应将队列的前端元素出队,时间复杂度是 O(1)。
- 获得栈顶元素操作对应获得队列的前端元素,时间复杂度是 O(1)。
- 判断栈是否为空操作只需要判断队列是否为空,时间复杂度是 O(1)。
空间:O(n),其中 n 是栈内的元素个数。需要使用一个队列存储栈内的元素。
【思想】用一个队列就能模拟栈的FILO,队头队尾元素罢了
top查看栈顶元素,所以需要将最后一个元素还给qout,对比一下两段的代码
java
public int pop() {
while(qin.size() > 1) {
qout.offer(qin.poll());
}
int res = qin.poll();
public int top() {
while(qin.size() > 1) {
qout.offer(qin.poll());
}
int res = qin.poll();
qout.offer(res);
java
class MyStack {
LinkedList<Integer> qin;
LinkedList<Integer> qout;
public MyStack() {
qin = new LinkedList<>();
qout = new LinkedList<>();
}
public void push(int x) {
qin.offer(x);
}
public int pop() {
while(qin.size() > 1) {
qout.offer(qin.poll());
}
int res = qin.poll();
LinkedList<Integer> tmp = qin;
qin = qout;
qout = tmp;
return res;
}
public int top() {
while(qin.size() > 1) {
qout.offer(qin.poll());
}
int res = qin.poll();
qout.offer(res);
LinkedList<Integer> tmp = qin;
qin = qout;
qout = tmp;
return res;
}
public boolean empty() {
return qin.isEmpty() && qout.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
20. 有效的括号
- 时间复杂度:O(n),其中 n 是 s 的长度。
- 空间复杂度:O(n) 。
两个 return 语句可以互换位置,但是匹配的必须在最后
java
if(tt == -1) return false; // 空了,但是还没遍历完
if(tt != -1 && st[tt] != c) return false; // 不匹配
if(tt != -1 && st[tt] == c) tt--; // 匹配往后退
出现错误的情况:匹配了回退tt 0 -> -1 触发 if(tt == -1) return false;明明匹配,还没遍历完,但是认为错了。举例如下:
java
场景: 栈中有一个元素 ')',栈顶 tt=0,当前字符 c = ')'
第一个 if: tt != -1 && st[tt] == c → 条件成立,tt-- 变为 -1
第二个 if: tt != -1 && st[tt] != c → tt == -1,条件不成立
第三个 if: tt == -1 → 条件成立,返回 false ❌
结果: 明明匹配成功,却返回了 false,这是错误的!
java
class Solution {
public boolean isValid(String s) {
char[] st = new char[10010];
int tt = -1;
for(int i = 0;i < s.length();i++) {
char c = s.charAt(i);
if(c == '(') {
st[++tt] = ')';
} else if(c == '[') {
st[++tt] = ']';
} else if(c == '{') {
st[++tt] = '}';
} else {
if(tt == -1) return false; // 空了,但是还没遍历完
if(tt != -1 && st[tt] != c) return false; // 不匹配
if(tt != -1 && st[tt] == c) tt--; // 匹配往后退
}
}
return tt == -1;
}
}