栈和队列刷题
232、Implement Queue using Stacks
1、一入一出:双栈法
①、代码
class MyQueue {
//一、算法思想
//通过:双栈实现队列, 一个栈(inStack), 另一个栈(outStack)
//二、实现细节
//1、创建两个栈:用LinkedList, 构造方法中进行初始化
//2、写一个outStack为空,则inStack全出栈,入outStack的方法: inPutOut
//3、入栈时:直接入inStack
//4、出栈时:调用 inPutOut 方法
//5、判断是否为空:instack && outStack 都为空是才为空
//6、获取栈顶元素:调用inPutOut方法, 然后outstak.peek即可
//三、测试用例
//1、[1, 2, 3] --->>> push, push, pop, peek [null, null, 2 , 1]
//四、复杂度
//1、Time Complexity:O(n)
//2、Space Complexity: O(n)
private Deque<Integer> inStack;
private Deque<Integer> outStack;
public MyQueue() {
inStack = new LinkedList<>();
outStack = new LinkedList<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
inStack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
inPutOut(inStack, outStack);
return outStack.pop();
}
/** Get the front element. */
public int peek() {
inPutOut(inStack, outStack);
return outStack.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty();
}
/**inStack入栈 outStack的方法*/
public void inPutOut(Deque inStack, Deque outStack){
if(outStack.isEmpty()){
while(!inStack.isEmpty()){
outStack.push(inStack.pop());
}
}
}
}
②、代码图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9mG3wqZ3-1617371300847)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20201111213307102.png)]
③、复杂度
739、Daily Temperatures (Medium)
1、暴力法:
①、(强烈不推荐)代码如下
class Solution {
//一、算法思路:
//通过暴力法:两个for循环
//二、实现细节
//1、a[i] , j = i+1, a[i]与a[j]依次比较
//2、如果a[j] >= a[i] 则直接相减:存入a[i]
//3、break跳出内层循环
//三、测试用例:
//1、[70, 92, 66]
//2、[73, 74, 75, 71]
//四、Complexity
//1、Time Complexity: O(n^2)
//2、Space Complexity: O(1)
public int[] dailyTemperatures(int[] T) {
for(int i = 0; i < T.length; i++){
boolean flag = true; //true为T[j] < T[i],作为标志
for(int j = i+1; j < T.length; j++){
if(T[j] > T[i]){
T[i] = j-i;
flag = false; //此时 T[j] > T[i]
break;
}
}
if(flag){ //此时 T[i], 后面没有比它:气温高的日子
T[i] = 0;
}
}
return T;
}
}
②、代码图(便于分析)
③、复杂度(LeetCode)
2、栈 + 数组
①、(推荐)代码如下
class Solution {
//一、算法思路
//通过利用:栈(存放遍历温度下标) + 数组(存放升温距离天数)
//二、实现细节
//1、栈采用:Deque<Integer> stack = new LinkedList<>() --->>> 双端链表模拟,速度优于Stack
//2、数组采用: int[] distance;
//三、测试用例
//1、[73, 56, 88]
//2、[74, 75, 71, 69, 72, 76, 73]
//四、complexity
//1、Time Complexity: O(n) 进行了for循环 n次 + 栈中元素出栈次数
//2、Space Complexity: O(n) 栈n + 数组n
public int[] dailyTemperatures(int[] T) {
//1、创建一个栈
Deque<Integer> stack = new LinkedList<>();
//2、创建一个数组
int length = T.length; //数组长度
int[] distance = new int[length];
//3、进行遍历
for(int curIndex = 0; curIndex < length; curIndex++){
//distance[curIndex] = 0; //此处不用进行初始化
//因为java中:数组没有赋初值时:为0
while(!stack.isEmpty() && T[curIndex] > T[stack.peek()]){
int preIndex = stack.pop(); //栈顶元素的下标
distance[preIndex] = curIndex - preIndex;
}
stack.push(curIndex);
}
return distance;
}
}
②、代码图(便于分析)
③、复杂度
503、Next Greater Element II
一、栈 + 数组
①、代码图
②、源代码
class Solution {
//一、算法思想
//1、利用栈 + 返回数组:初始化-1
//二、算法细节
//1、将数组:通过取余 2 * length 可以,模拟循环数组
//2、遍历需要两遍, 才能最终确认下来,每一个元素的下一个:最大值
//3、提前将:返回数组赋值为 -1, 避免了找不到:赋值-1操作
//4、核心:if(!stack.isEmpty && num > stack.peek()) 则 next[stack.pop()] = num
//5、核心:i < length 则 stack.push(i)
//三、测试用例
//1、[1, 2, 1]
//四、复杂度
//1、Time Complexity:O(N) -->>因为一个 for(2*length次)+ 小于等于n次出栈 + i < length入栈
//2、Space Complexity: O(N) -->>一个栈(入栈n次) + 一个数组长n
public int[] nextGreaterElements(int[] nums) {
//1、获取nums的长度
int length = nums.length;
//2、前置预判断:length == 0 则直接返回
if(length == 0){
return nums;
}
//3、创建一个栈
Deque<Integer> stack = new LinkedList<>();
//4、创建一个:存放nums[]数组的(下一个大元素的数组), 并赋值为:-1
int maxNext[] = new int[length];
Arrays.fill(maxNext, -1);
//5、模拟循环数组:遍历
for(int i = 0; i < 2*length; i++){
//5.1、记录当前数组元素的:value
int curValue = nums[i%length];
//5.2、进行判断
while(!stack.isEmpty() && curValue > nums[stack.peek()]){
maxNext[stack.pop()] = curValue;
}
if(i < length){
stack.push(i);
}
}
return maxNext;
}
}
③、复杂度
225、Implement Stack using Queues
一、双端队列法
①、代码图
②、源代码
class MyStack {
//一、算法思想
//采用:双端队列Deque
//二、算法细节
//1、add时:判断
//第一步:(元素)直接入队, 求出队列长度 length
//第二步:通过while(length-- > 1)的元素全部出队,再入队,实现逆转, 相当于将(新元素)前的所有元素(都重新入队)
//2、remove时:直接出栈, 因为(All the calls to pop and top are valid.)
//3、peek时:直接查看
//4、empty:直接判断
//注意:切记poll()和remove()效果是一样的,别乱用
//三、测试用例
//push(1) , push(2) --->>> pop() --->>> 2
//四、复杂度
//1、Time Complexity: O(n) --->>> 主要在 push()处累加和用到了n, 其余操作为O(1)
//2、Space Complexity: O(n) --->>> 双端队列
/** Initialize your data structure here. */
private Deque<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
/** Push element x onto stack. */
public void push(int x) {
//1、直接入队
queue.add(x);
//2、求出:队列长度
int length = queue.size();
//3、将入队(新元素)前的(所有元素)重新入队, 实现逆转,变成栈的效果
while(length-- > 1){
queue.add(queue.remove());
}
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue.remove();
}
/** Get the top element. */
public int top() {
return queue.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue.isEmpty();
}
}
③、复杂度
155、Min Stack (Easy)
一、栈 + Node结点法
①、代码图
②、源代码
class MinStack {
//一、算法思想
//利用:内部结点类Node + 模拟栈Deque 实现, 始终保持栈顶元素(结点.minValue属性)存放着(栈最小值)
//二、算法细节
//1、创建一个内部类:Node
//成员变量(value): 存放当前:入栈值x
//成员变量(minValue) : 存放栈中:最小值
//2、创建一个LinkedList模拟栈
//3、在push:进行判断
//1、当stack为空时: 直接Node入栈,
//2、当stack不为空时:将(Node.minValue值) 与(栈顶元素.minValue) 进行比较
//若Node.minValue < 栈顶元素.minValue 则直接入栈
//否则:将Node.minValue = 栈顶元素.minValue 再入栈
//3、stack.getMin() --->>> 直接获取(栈顶.minValue)即可
//4、其余:pop, peek, isEmpty 不用更改
//三、测试用例
//push 1 -->>> push -2 -->>> push 0 ---->>getMin = -2, pop = 0, getMin = -2,
//四、复杂度
//Time Complexity:O(1) --->>> push、pop、top、getMin: 这几个方法(都是常数阶),所以为O(1)
//Space Complexity: O(n) --->>> 用到了:栈 + Node, 当最坏情况是,全部入栈, 每次x都比栈顶的(minValue)要小,O(n)跑不掉了
//1、创建一个栈
private Deque<Node> stack;
/** initialize your data structure here. */
public MinStack() {
stack = new LinkedList<>();
}
public void push(int x) {
if(!stack.isEmpty() && x > stack.peek().minValue){ //此时栈不为空, (当前栈顶)存放的最小值:依然是最小值
int minValue = stack.peek().minValue;
stack.push(new Node(x, minValue));
}else{
stack.push(new Node(x, x));
}
}
public void pop() {
stack.pop();
}
public int top() {
return stack.peek().value;
}
public int getMin() {
return stack.peek().minValue;
}
private class Node{
private int value; //存放当前:入栈值x
private int minValue; //存放栈中:最小值
public Node(int value, int minValue){
this.value = value;
this.minValue = minValue;
}
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
③、复杂度
二、数据栈 + 辅助(最小值)栈
①、代码图
②、源代码
class MinStack {
//一、算法思想
//运用:数据栈 + 辅助(最小值)栈 = 双栈模式
//二、算法细节
//1、创建两个栈:
//①、Deque<Integer> dataStack = new LinkedList<>();
//②、Deque<Integer> minStack = new LinkedList<>();
//2、push时:
//①、直接入栈:dataStack
//②、判断minStack是否为空
//如果为空:直接入栈
//否则:判断入栈值 (与)minStack.peek()大小
//如果:入栈值(小于等于),则入栈, 因为 = 不入,会导致出栈后,拿peek时空指针
//否则:不入栈
//3、pop时:将dataStack直接入栈, 并记录值 value, 并用value 和 minStack.peek()比较大小
//如果:相等时,minStack.pop()也出栈
//三、测试用例
//push 1, push -2, push 0 --->>> getMin() = -2, pop() = 0, getMin() = -2
//四、复杂度
//Time Complexity : O(1) -->>>所有方法操作都是:O(1)
//Space Complexity: O(n) -->>> 双栈
//1、创建:数据栈 + 辅助(最小值)栈
private Deque<Integer> dataStack;
private Deque<Integer> minStack;
/** initialize your data structure here. */
public MinStack() {
dataStack = new LinkedList<>();
minStack = new LinkedList<>();
}
public void push(int x) {
dataStack.push(x);
if(minStack.isEmpty() || x <= minStack.peek()){ //切记:x == 最小值也要入栈
minStack.push(x); //因为出栈时: 最小栈可能也会出栈,不入栈导致:最小栈.peek时空指针
}
}
public void pop() {
int value = dataStack.pop();
if(value == minStack.peek()){
minStack.pop();
}
}
public int top() {
return dataStack.peek();
}
public int getMin() {
return minStack.peek();
}
}
③、复杂度
三、自定义:链栈
①、代码图
②、源代码
class MinStack {
//一、算法思路
//采用:自己创建(一个链栈) + 头插法
//二、算法细节
//1、创建:链结点LinkStack(数据结构)
//int value:存放当前入栈值
//int minValue: 存放栈最小值
//StackNode next: 存放下一个结点
//2、push时:若链栈 LinkStack == null --->>> 直接入栈, 最小值为x
//否则:用Math.min(x, LinkStack.minValue)找出最小值, 然后入栈
//入栈:采用头插法, 通过head = LinkStack的构造方法,巧妙插入
//3、pop时:直接head = head.next
//4、top时:直接head.value
//5、getMin时:直接head.getMin
//三、测试用例
//push 1, push -2, push 0 --->>> getMin() = -2, pop() = 0, getMin() = -2
//四、复杂度
//Time Complexity: O(1)
//Space Complexity: O(n)
//1、创建一个:链栈的头指针
private LinkedNode head;
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
if(head == null){
head = new LinkedNode(x, x, head);
}else{
head = new LinkedNode(x, Math.min(x, head.minValue), head);
}
}
public void pop() {
head = head.next;
}
public int top() {
return head.value;
}
public int getMin() {
return head.minValue;
}
private class LinkedNode{
private int value; //存放当前入栈值
private int minValue; //存放栈最小值
private LinkedNode next; //存放下一个结点
LinkedNode(int value, int minValue, LinkedNode head){
this.value = value;
this.minValue = minValue;
this.next = head;
}
}
}
③、复杂度
20、Valid Parentheses (Easy)
一、栈 + 直接遍历
①、代码图
可以补充:前置判断括号个数, if(length % 2 == 1) return false;
②、源代码
class Solution {
//一、算法思想
//采用:遍历字符串 + 栈
//二、算法细节
//1、创建一个栈:Deque<Character> stack = new LinkedList<>()
//2、遍历字符串, 通过if、else if 进行选择
//①、遇到( , [, { 左括号则入栈它们的(右括号)
//②、遇到), ], } 时
//判断:栈是否为空, 如果为空 return false
//判断:右括号是否 == 出栈元素, 如果不等于 return false
//3、return stack.isEmpty() 作为是否是:有效括号的结果
//三、测试用例
//1、([)] --->>> return false
//2、{[]} --->>> return true
//3、[] --->>> return true
//四、复杂度
//1、Time Complexity:O(n) -->> 因为要遍历length长度
//2、Space Complexity:O(n) -->> 用到了:栈
//五、优缺点
//1、advantage: 通俗易懂, 代码量少
//2、shortcoming: 拓展性不好, 一旦匹配变多, if将变多,而且修改起来困难
public boolean isValid(String s) {
//1、创建一个栈
Deque<Character> stack = new LinkedList<>();
//2、获取字串长度
int length = s.length(); //因为题目给定:1 <= s.length <= 10^4 所以不用前置判断了
//3、遍历字符串
for(int i = 0; i < length; i++){
Character c = s.charAt(i);
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();
}
}
③、复杂度
二、遍历字符串 + 栈 + Map
①、代码图
②、源代码
class Solution {
//一、算法思想
//采用:遍历字符串 + 栈 + Map
//二、算法细节
//1、创建一个栈:Deque<Character> stack = new LinkedList<>()
//2、创建一个:Map<Character, Character> = new HashMap<>()
//3、将()、[], {}, 放入map中, 左括号为key, 右括号为value
//4、进行遍历:当前字符到(Map查找)
//若找到:则将Map的value入栈
//若找不到:判断当前栈是否为空 || 当前遍历字符 != 出栈元素
//栈若为空:说明(右括号)找不到匹配的(左括号) -->> 直接return false
//当前遍历字符 != 出栈元素: 说明匹配不上 -->> 直接return false
//5、最后用:stack.isEmpty()的原因:因为若能匹配,则都出栈了,不能匹配则(栈不为空)
//三、测试用例
//1、([)] --->>> return false
//2、{[]} --->>> return true
//3、[] --->>> return true
//四、复杂度
//1、Time Complexity:O(n) -->> 因为要遍历length长度
//2、Space Complexity:O(n) -->> 最坏超过 n , 因为用到了Map
//五、优缺点
//1、advantage: 添加了前置判断、拓展性强(增加匹配元素时)易于添加
//2、shortcoming: 耗费了空间
public boolean isValid(String s) {
//1、获取字串长度
int length = s.length(); //因为题目给定:1 <= s.length <= 10^4 所以对此做:判断
if(length % 2 == 1){
return false; //奇数个括号, 不可能匹配
}
//2、创建一个栈
Deque<Character> stack = new LinkedList<>();
//3、创建一个Map, 并将成对括号:放入
Map<Character, Character> map = new HashMap<>(); //直接用map.put()而不用:匿名内部类,统一初始化
map.put('(', ')'); //是因为:map.put()的效率更高
map.put('[', ']');
map.put('{', '}');
//4、遍历字符串
for(int i = 0; i < length; i++){
Character c = s.charAt(i);
if(map.containsKey(c))
stack.push(map.get(c));
else if(stack.isEmpty() || c != stack.pop())
return false;
}
return stack.isEmpty();
}
}
③、复杂度
三、哨兵 + stack
①、代码图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sEfCgbOh-1617371300878)(file:///C:\Users\Lenovo\AppData\Roaming\Tencent\Users\2560055298\QQ\WinTemp\RichOle\OM[QUMNDY[DHG8%Q_V83@KE.png)]
②、源代码
class Solution {
//1、创建Map
private static final Map<Character, Character> map = new HashMap<>(){
{
put('(', ')');
put('[', ']');
put('{', '}');
put('?', '?'); //作为哨兵
}
};
public boolean isValid(String s) {
//1、前置欲判断
if(s.length() % 2 == 1 || !map.containsKey(s.charAt(0)))
return false;
//2、创建一个栈
Deque<Character> stack = new LinkedList<>();
stack.push('?'); // (?, ?)作为哨兵
//3、遍历:判断
for(char c : s.toCharArray()){
if(map.containsKey(c)){
stack.push(c);
}else if(map.get(stack.pop()) != c){
return false;
}
}
return stack.size() == 1;
}
}
③、复杂度