Leetcode复盘3——栈和队列

Leetcode复盘3——栈和队列

导读

这是我写的第二篇复盘总结,总结了10道链表相关的题目,里面包含了所有链表的基本操作,包括:

  • 如何定义一个指针(C++ ListNode *pA = head, Java ListNode pA = head)
  • 如何反转链表;(设置一个临时指针temp,先记录cur的下一个,反转,pre和cur各往前走一步)
  • 如何区分指针移动和指向:当为指向时“.next”在等号左边,即用当前指针指向等号右边的节点;当为移动时”.next”在等号右边,即把某一个指针指向的节点赋值给等号左边
1.用栈实现队列(Leetcode232)

难度:简单Easy

idea: 双栈(two Stack)
由栈的特点,即先入后出,队列里面的值先进入第一个栈(in栈)后顺序被颠倒过来了,从in栈进到第二个栈(out栈)顺序又被反过来了,这样就实现了先入先出的方法

代码:
Java版

class MyQueue {

    // 在所有函数外定义两个栈,第一个函数体中赋值
    private Stack<Integer> in = new Stack<>();
    private Stack<Integer> out = new Stack<>();

    /** Initialize your data structure here. */
    public MyQueue() {
        in = new Stack<>();              // 第一个栈(in栈)
        out = new Stack<>();             // 第二个栈(out栈)
    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {                                   // 只是把元素加到"队列"里(后入后出)
        in.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {                                          // 当从"队列"中弹出元素时要注意了,一定是从out栈里弹,里面会涉及到一个从in栈到out栈转移的过程
        in2out();
        return out.pop();
    }
    
    /** Get the front element. */
    public int peek() {                                         // 当看一眼"队列"顶的元素注意了,一定是从out栈里看
        in2out();
        return out.peek();
    }
    
    /*单独定义一个从in栈到out栈转移的过程,每次转移的过程需要保证out栈是空的*/
    private void in2out() {                                     // 不传参数且不返回任何东西
        if(out.isEmpty()) {
            while(!in.isEmpty()) {                              // 调用1次in2out函数,把in中的元素全部都转移到out里
                out.push(in.pop());
            }
        }
    }


    /** Returns whether the queue is empty. */
    public boolean empty() {                                    // 保证in和out都为空即可
        return in.isEmpty() && out.isEmpty();
    }
}

/**
 * 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();
 */

2.用队列实现栈(Leetcode225)

难度:简单Easy

idea
本题跟上一题用栈实现队列不同,此处只需要一个队列即可,方法是当push进队列尾部一个元素时,要让其先出,即把其他的元素依次pop出去,在push进队列尾部,定义3个函数:
1.push函数: 把一个元素push进队列尾部(必须),再把之前其他的元素pop出去再push进队列(非必须)
2.pop函数: 把除队尾的其他元素依次pop出去再push进队列(必须),再把队尾的元素pop出去(必须)
3.peek函数: 把除队尾的其他元素依次pop出去再push进队列(必须),再看一眼队尾的元素(必须)
综上可知,2和3都需要把之前其他的元素先pop出去再push进来,故可以把这一步放在push函数里

class MyStack {
    queue<int> queue;                                        // 函数外定义+赋值(queue貌似不是C++中的关键字)
public:
    /** Initialize your data structure here. */
    MyStack() {
        //nothing to do
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        queue.push(x);
        // 把除了队尾的元素都pop出去再push进来
        for(int i = 0; i < queue.size() - 1; i++) {
            queue.push(queue.front());
            queue.pop();
        }
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int num = queue.front();                                 // C++好奇怪啊,pop出去的不能直接用
        queue.pop();
        return num;
    }
    
    /** Get the top element. */
    int top() {
        return queue.front();                                   // push函数都调整好了,此时队首的元素即刚加进队尾的元素
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return queue.empty();
    }
};

/**
 * 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();
 * bool param_4 = obj->empty();
 */

Java版

class MyStack {

    private Queue<Integer> queue;                           // 所有函数外定义

    /** Initialize your data structure here. */
    public MyStack() {
        queue = new LinkedList<>();                         // 函数内赋值
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
        queue.add(x);
        // 因为pop和peek都需要把除队尾之外的其他元素pop出去再push进队尾,故放在这里即可
        int cnt = queue.size();
        while (cnt-- > 1) {
            queue.add(queue.poll());
        }
    }
    
    /** 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();                             // 之前都弄好了,队首的元素即刚加进来的元素
    }
}

/**
 * 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();
 */
3.最小栈 / 最小值栈(LeetCode155)

难度:简单Easy

idea: 双栈法
定义两个栈Stack和minStack,其中Stack为一般的栈,而minStack栈只用来保存当前最小值,即栈顶元素
当有一个新的元素要入栈时,直接加入Stack中,再判断它与minStack栈顶元素的大小,若小于栈顶元素,则也把它加到minStack里;同理当要删除一个元素时,直接从Stack栈中删除,当要删除的元素和minStack栈顶的元素相等时,再把minStack栈顶元素删除

代码:
Java版

class MinStack {

    /** initialize your data structure here. */

    private Stack<Integer> stack;                           // 函数外定义
    private Stack<Integer> minStack;

    public MinStack() {
        stack = new Stack<>();                              // 函数内赋值
        minStack = new Stack<>();
    }
    
    public void push(int x) {
        stack.push(x);                                      // stack直接加入
        if(!minStack.isEmpty()) {
            int top = minStack.peek();
            if(x <= top) minStack.push(x);
        } else {
            minStack.push(x);                               // minStack为空的,直接把x加进去即可
        }
    }
    
    public void pop() {
        int pop = stack.pop();

        int top = minStack.peek();
        // 当pop和top相等时minStack再出栈
        if(pop == top) {
            minStack.pop();
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();                                 // minStack保存的是所有元素最小值
    }
}

/**
 * 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();
 */

Python版

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []

    def push(self, x: int) -> None:                                 # 当有一个新元素加入时
        self.stack.append((x, min(self.getMin(), x)))               # 第[0]维直接加入,第[1]维比较它和之前最小值的大小

    def pop(self) -> None:
        self.stack.pop()

    def top(self) -> int:                                           # 看一眼栈顶[-1]的元素[0]
        if self.stack:
            return self.stack[-1][0]

    def getMin(self) -> int:                                        # 得到当前所有元素[-1]的最小值[1]
        if self.stack:                                              # 若栈不为空
            return self.stack[-1][1]
        return float("inf")                                         # 若栈为空,最小值为正无穷


# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
4.有效的括号 / 用栈实现括号匹配(LeetCode20)

难度:简单Easy

idea: 栈(Stack)
先把字符串型转换成字符串数组,用Java里toCharArray()方法,这样就可以用for循环一个个去取了

代码:
Java版

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();                         // 定义栈stack并赋值
        for(char c : s.toCharArray()) {
            if(c == '(' || c == '[' || c == '{') {                      // 当为左括号时入栈
                stack.push(c);
            } else {                                                    // 此时为右括号,需要从stack弹出左括号来匹配
                if(stack.isEmpty()) return false;
                char cStack = stack.pop();                              // 从stack里弹出左括号去跟c这个右括号匹配
                boolean b1 = c == ')' && cStack != '(';                 // 当c为右小括号并且弹出的不为左小括号时置true
                boolean b2 = c == ']' && cStack != '[';
                boolean b3 = c == '}' && cStack != '{';
                if (b1 || b2 || b3) {                                   // 当b1或b2或b3有一个为true时就返回失败
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }
}
5.每日温度 / 数组中元素与下一个比它大的元素之间的距离(LeetCode739)

idea: 单调栈
本地的目的是找到右边第一个比它大的元素,并计算之间的距离,故考虑单调栈,从栈底到栈顶元素依次递减,当来一个新元素时,若比当前栈顶的元素大,则代表找到了栈顶元素那个值离它最近的较大值,返回坐标之差.
具体方法为生成一个和给定数组长度一样的数组(单调栈),用来存储各个维度对应的下标(因为本题要返回距离,故用下标计算),当当前元素大于栈顶元素时,证明栈顶元素minStack[-1]找到了离它最近的较大值,放到结果列表对应的位置,即res[minStack[-1]]
1.制作返回列表和最小栈
2.for循环遍历给定列表的每个元素,
3.while循环比较当前元素和栈顶元素的大小,
4.当当前元素大于栈顶元素时,计算在返回列表中的位置,并弹出栈顶元素
5.跳出while循环后,把当前元素放到它应该待的位置,压入栈

代码
Java版

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;                                        // 记录数组的长度
        int[] dist = new int[n];                                        // 生成一个和数组大小一样的数组,用来输出各个距离
        Stack<Integer> indexs = new Stack<>();                          // 定义单调栈,从栈底到栈顶元素依次递减
        for (int curIndex = 0; curIndex < n; curIndex++) {
            // while循环,当当前元素值(下标curIndex)大于栈顶元素(下标preIndex)时,就知道preIndex和curIndex距离之差了,
            // 若当前元素依旧大于栈顶元素,那么新的栈顶元素(preIndex)与当前元素的距离之差也知道了,依次类推.
            while (!indexs.isEmpty() && temperatures[curIndex] > temperatures[indexs.peek()]) {
                int preIndex = indexs.pop();                            // 当前元素大于栈顶元素,pop出栈顶元素,开始求距离
                dist[preIndex] = curIndex - preIndex;
            }
            indexs.add(curIndex);                                       // 找到当前元素应该待的位置了,压入栈
        }
        return dist;
    }
}

Python版

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        length = len(T)
        res = [0] * length                     # 生成一个和元素组一样长度的数组,用来记录每个温度离它最近的较大值之间的距离
        minStack = []
        for i in range(length):
            while(minStack and T[minStack[-1]] < T[i]):        # 若当前元素T[i]大于栈顶元素时,则可以给栈顶元素安排了
                res[minStack[-1]] = i - minStack[-1]
                minStack.pop()
            minStack.append(i)
        return res
6.下一个更大元素 II / 循环数组中比当前元素大的下一个元素(LeetCode503)

idea: 单调栈(stack)
把原始数组想象成一个个人,元素的大小为身高,环形数组即把原始数组"翻倍",就是在后面再接一个原始数组,这样的话,按照之前"比身高"的流程,每个元素不仅可以比较自己右边的元素,而且也可以和左边的元素比较了.
此题采用从后往前遍历的方法,即"即时性"方法:
如果当前元素大于栈顶元素,则一口气把小的全都pop出去,将当前元素当做定海神针;
如果当前元素小于栈顶元素,则直接计算当前元素在res中的位置(本题用res返回结果,这是一般情况,上一题用字典返回属于特殊情况)

代码:
C++版

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n);                                         // 存放结果
        stack<int> stack;                                           // 单调栈
        // 假装这个数组长度翻倍了
        for (int i = 2 * n - 1; i >= 0; i--) {                      // 从后往前入栈
            while (!stack.empty() && stack.top() <= nums[i % n])    // 当当前的值比栈顶的元素大时,证明来了新的占山之宝,把旧的pop出栈
                stack.pop();
            res[i % n] = stack.empty() ? -1 : stack.top();          // 否则新的数都以旧的栈顶元素计算离其最近的更大值
            stack.push(nums[i % n]);
        }
        return res;
    }
};

Python版

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        nums = nums * 2                                                  # 原数组扩大一倍
        stack = []
        res = [-1] * len(nums)
        for idx, num in enumerate(nums):
            while stack and nums[stack[-1]] < num:                      # 当前元素大于栈顶元素了,依次计算栈顶元素
                res[stack.pop()]  = num                                 # 分两步:1.pop出去; 2.计算在res中的位置
            
            stack.append(idx)                        # 不管是从左往右的"滞后性"还是从右往左的"即时性"最后都要把当前值压入栈
        return res[:len(nums) // 2]                                     # 只返回res前半部分的值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值