剑指Offer Day01
剑指 Offer 09. 用两个栈实现队列
题目:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead
,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof
难点:理解题目意思、栈与队列特性
“示例
”的意思其实是,按照"CQueue",“appendTail”,“deleteHead”,“deleteHead”,"deleteHead"的顺序执行这些方法,
对应的,第二行数组表示执行方法时的传入参数
其中只有appendTail
方法需要传参,所以第二行数组中第二个是3
“输出
”其实是对应方法的返回值,仅deleteHead
有返回值,其它方法都是void类型所以返回输出的都是null。
思考:
1、队列是先入先出
2、栈是先入后出
3、假设现在仅一个栈(inStack
):
新增时,头结点在栈底,末节点在栈顶,
删除时,怎么删除栈底元素?
(将删除操作顺序与我们实际存储顺序相比较,其实就是逆序
)
4、使用另一个栈(outStack
),用来逆序的队列
so,将inStack
中pop出来,再push到outStack
,这样相当于逆序
删除一个头元素,也就是直接pop一个outStack
注意点:
解决逆序问题!
outStack
为空:去inStack
拿,inStack
没有才返回-1
使用ArrayDeque
而不是Stack
,二者性能有明显差异。
class CQueue {
private Deque<Integer> inStack = new ArrayDeque<>();
private Deque<Integer> outStack = new ArrayDeque<>();
public CQueue() {
}
public void appendTail(int value) {
inStack.push(value);
}
public int deleteHead() {
if (outStack.size() <= 0) {
if (!inStack.isEmpty()) {
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}else {
return -1;
}
}
return outStack.pop();
}
}
剑指 Offer 30. 包含min函数的栈
题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop
的时间复杂度都是 O(1)。示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/bao-han-minhan-shu-de-zhan-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思考
时间复杂度要O(1)? 意味着我们push、pop和min都是只需要一步就够了,而不能够递归或是遍历。
首先想到的是:以空间换时间
最初的想法(错误思路
):
内部动态维护一个int变量,push只需要比较一次就行了,但当pop的这个元素刚好是最小值呢?
那么前面栈底元素中,剩下的最小值怎么找?
应该意识到,错误思路中只有pop时出了问题。如果我们每次pop都能知“最小值”呢?
每次拿最小值,都是当前值和前面的栈元素共同比较的结果。
如果说每个元素都知道【自己+前面元素】这个集合中的最小值呢?
每一个栈元素,都应该对应一个“最小值”
这个“最小值”是指当前位置和往前所有栈元素的最小值
解决
使用辅助栈
内部维护一个minStack,这也是官方的解答。
每次push值x时,x与minStack栈顶元素进行比较
存min值进minStack,minStack的栈顶永远是最小值
而不用去遍历比较,保证了时间复杂度。
代码:
class MinStack {
private Deque<Integer> deque = new ArrayDeque<>();
private Deque<Integer> minDeque = new ArrayDeque<>();
/** initialize your data structure here. */
public MinStack() {
minDeque.push(Integer.MAX_VALUE);//默认int最大值保证初始时有
}
public void push(int x) {
deque.push(x);
minDeque.push(Math.min(minDeque.peek(),x));
}
/**
* pop时就应该考虑到,万一这个是最小元素,没了之后,剩下的栈元素中最小元素又是哪个呢?
* **/
public void pop() {
deque.pop();
minDeque.pop();
}
public int top() {
return deque.peek();
}
public int min() {
return minDeque.peek();
}
}
解决方法二:
评论区第一位,一个大佬的解决办法是内部做成带数据的链表形式,思路很开阔。
每个对象除了前后指针,内部还存有一个最小值,其实也是新增时比较前一个元素的最小值。
不看数据结构,解决问题的方式是差不多的。
Day01两题总结:
- 善于利用算法上:空间与时间的置换关系
- 熟悉队列先进先出与栈先入后出的数据结构特点
- 若是有类似最大、最小值之类需要遍历比较的需求,考虑如何化繁为简,将比较的步骤划分到数组内元素的增删上,而不是每次需要的时候才去遍历。