栈、队列:LeedCode刷题笔记

栈和队列刷题

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)]

③、复杂度

image-20210402214259575

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;
    }
}
②、代码图(便于分析)

image-20210402214340868

③、复杂度(LeetCode)

image-20210402214352563

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;
    }
}
②、代码图(便于分析)

image-20210402214417848

③、复杂度

image-20210402214426865

503、Next Greater Element II

一、栈 + 数组

①、代码图

image-20210402214447050

②、源代码
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;
        
    }
}
③、复杂度

image-20210402214512795

225、Implement Stack using Queues

一、双端队列法

①、代码图

image-20201112215139179

②、源代码
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();
    }
}

③、复杂度

image-20201112215513297

155、Min Stack (Easy)

一、栈 + Node结点法

①、代码图

image-20201113200016135

②、源代码
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();
 */
③、复杂度

image-20201113200106710

二、数据栈 + 辅助(最小值)栈

①、代码图

image-20201113204553119

②、源代码
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();
    }
}

③、复杂度

image-20201113204631108

三、自定义:链栈

①、代码图

image-20201113212327249

②、源代码
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;
        }
    }
}

③、复杂度

image-20201113212415967

20、Valid Parentheses (Easy)

一、栈 + 直接遍历

①、代码图
可以补充:前置判断括号个数, if(length % 2 == 1) return false;

image-20201114195658544

②、源代码
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();
    }
}
③、复杂度

image-20201114195824153

二、遍历字符串 + 栈 + Map

①、代码图

image-20201114205911117

②、源代码

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();        
    }
}

③、复杂度

image-20201114210000430

三、哨兵 + 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;
    }
}

③、复杂度

image-20201114215053853

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漫漫求

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值