要点:
1. 栈和队列本身都属于限制型的数据结构,有各自的定义,也有可以混淆的部分
2. 他们也是源自数组和链表的一种延伸结构,比如数组实现的栈成为顺序栈,链表实现的栈成为链式栈
3.我们可以根据两者的定义去理解他们,但是实际使用其实没必要给他们彻底区分,比如Queue、Deque这些,为什么不能表达栈
题目一:这道题很实在,就是考一考对栈,队列,基础结构等等的一些理解和用法
剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数
appendTail
和deleteHead
,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead
操作返回 -1 )示例 1:
输入: ["CQueue","appendTail","deleteHead","deleteHead","deleteHead"] [[],[3],[],[],[]] 输出:[null,null,3,-1,-1]示例 2:
输入: ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"] [[],[],[5],[2],[],[]] 输出:[null,-1,null,null,5,2]
解法一:我很喜欢这个解法,看着deleteHead稍微显得复杂了一点,但是他陈述的很完整
static class CQueue2 {
LinkedList stack1;
LinkedList stack2;
public CQueue2() {
stack1 = new LinkedList<>();
stack2 = new LinkedList<>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if (stack2.isEmpty()){
if (stack1.isEmpty())return -1;
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
return stack2.pop();
}else {
return stack2.pop();
}
}
}
解法二:前面用Deque接也可以反正是自己的实现类,其实我们JAVA的结构类里面已经包含了这些方法
static class CQueue1 {
Deque stack1;
Deque stack2;
// LinkedList q1;
// LinkedList q2;
public CQueue1() {
stack1 = new LinkedList<>();
stack2 = new LinkedList<>();
}
public void appendTail(int value) {
stack1.addLast(value);
}
public int deleteHead() {
if (stack1.isEmpty()) return -1;
return stack1.removeFirst();
}
}
题目二:
剑指 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.提示:
- 各函数的调用总次数不超过 20000 次
解法:这道题很简单,首先把我们常用的Node节点实现出来,之后按照所需方法逐个实现即可,注意节点里面该有的val,min,next这些都要有
class MinStack {
private Node head;
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
if (head == null)
head = new Node(x,x,null);
else
head = new Node(x,Math.min(head.min,x),head);
}
public void pop() {
head = head.next;
}
public int top() {
return head.val;
}
public int min() {
return head.min;
}
private class Node{
int val;
int min;
Node next;
public Node(int val, int min, Node next) {
this.val = val;
this.min = min;
this.next = next;
}
}
}
题目三:
剑指 Offer 59 - I. 滑动窗口的最大值
给定一个数组
nums
和滑动窗口的大小k
,请找出所有滑动窗口里的最大值。示例:
输入: nums =[1,3,-1,-3,5,3,6,7]
, 和 k = 3 输出:[3,3,5,5,6,7] 解释:
滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
解法一:先简单校验一下,之后把数组搞出来,使用ArrayDeque,因为结合滑动窗口,所以需要i,j一起在循环里面转悠
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0) {
return new int[0];
}
int[] res = new int[nums.length - k + 1];
Deque queue = new ArrayDeque<>();
for (int i = 0, j = 0; i < nums.length; i++) {
if (!queue.isEmpty() && i-queue.peek() >= k){
queue.poll();
}
while (!queue.isEmpty() && nums[i]>nums[queue.peekFirst()]){
queue.pollLast();
}
queue.offer(i);
if (i>=k-1){
res[j++] = nums[queue.peek()];
}
}
return res;
}
题目四:又是实现队列
请定义一个队列并实现函数
max_value
得到队列里的最大值,要求函数max_value
、push_back
和pop_front
的均摊时间复杂度都是O(1)。若队列为空,
pop_front
和max_value
需要返回 -1示例 1:
输入: ["MaxQueue","push_back","push_back","max_value","pop_front","max_value"] [[],[1],[2],[],[],[]] 输出: [null,null,null,2,1,2]示例 2:
输入: ["MaxQueue","pop_front","max_value"] [[],[],[]] 输出: [null,-1,-1]
解法一:跟第一个题很类似,但是需要考虑最大值的问题
class MaxQueue {
private Deque q1;
private Deque q2;
public MaxQueue() {
q1 = new ArrayDeque<>();
q2 = new ArrayDeque<>();
}
public int max_value() {
return q1.isEmpty() ? -1 : q2.peek();
}
public void push_back(int value) {
q1.offer(value);
while (!q2.isEmpty() && value > q2.peekLast()){
q2.pollLast();
}
q2.offer(value);
}
public int pop_front() {
if (q1.isEmpty()){
return -1;
}
int val = q1.pop();
if (q2.peek() == val){
q2.pop();
}
return val;
}
}