Leetcode _入门 _栈和队列

1、用栈实现队列(232、Easy)

1)题目要求

使用栈实现队列的下列操作:

push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。

示例:

MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false

说明:

你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

2)我的解法

c++

class MyQueue {
public:
    stack<int> s1,s2;
    /** Initialize your data structure here. */
    MyQueue() {}
    
    /** Push element x to the back of queue. */
    void push(int x) {s1.push(x);}
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() 
    {
        while(!s1.empty()){s2.push(s1.top());s1.pop();}
        int x=s2.top();
        s2.pop();
        while(!s2.empty()){s1.push(s2.top());s2.pop();}
        return x;
    }
    
    /** Get the front element. */
    int peek() 
    {
        while(!s1.empty()){s2.push(s1.top());s1.pop();}
        int x=s2.top();
        while(!s2.empty()){s1.push(s2.top());s2.pop();}
        return x;
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {return s1.empty()?true:false;}
};

在这里插入图片描述一次性双百

3)其他解法

方法一(使用两个栈 入队 - O(n), 出队 - O(1))
算法

入队(push)

一个队列是 FIFO 的,但一个栈是 LIFO 的。这就意味着最新压入的元素必须得放在栈底。为了实现这个目的,我们首先需要把 s1 中所有的元素移到 s2 中,接着把新元素压入 s2。最后把 s2 中所有的元素弹出,再把弹出的元素压入 s1。
在这里插入图片描述

Java

private int front;

public void push(int x) {
    if (s1.empty())
        front = x;
    while (!s1.isEmpty())
        s2.push(s1.pop());
    s2.push(x);
    while (!s2.isEmpty())
        s1.push(s2.pop());
}

复杂度分析

时间复杂度:O(n)
对于除了新元素之外的所有元素,它们都会被压入两次,弹出两次。新元素只被压入一次,弹出一次。这个过程产生了 4n + 24n+2 次操作,其中 nn 是队列的大小。由于 压入 操作和 弹出 操作的时间复杂度为 O(1), 所以时间复杂度为 O(n)。

空间复杂度:O(n)
需要额外的内存来存储队列中的元素。

出队(pop)

直接从 s1 弹出就可以了,因为 s1 的栈顶元素就是队列的队首元素。同时我们把弹出之后 s1 的栈顶元素赋值给代表队首元素的 front 变量。

在这里插入图片描述

Java

// Removes the element from the front of queue.
public void pop() {
    s1.pop();
    if (!s1.empty())
        front = s1.peek();
}

复杂度分析

时间复杂度:O(1)
空间复杂度:O(1)
判断空(empty)

s1 存储了队列所有的元素,所以只需要检查 s1 的是否为空就可以了。

Java

// Return whether the queue is empty.
public boolean empty() {
    return s1.isEmpty();
}

时间复杂度:O(1)
空间复杂度:O(1)

取队首元素(peek)

在我们的算法中,用了 front 变量来存储队首元素,在每次 入队 操作或者 出队 操作之后这个变量都会随之更新。

Java

// Get the front element.
public int peek() {
  return front;
}

时间复杂度:O(1)
队首元素(front)已经被提前计算出来了,同时也只有 peek 操作可以得到它的值。

空间复杂度:O(1)

作者:LeetCode
链接:link
来源:力扣(LeetCode)

方法二(使用两个栈 入队 - O(1)O(1),出队 - 摊还复杂度 O(1)O(1))
算法

入队(push)

新元素总是压入 s1 的栈顶,同时我们会把 s1 中压入的第一个元素赋值给作为队首元素的 front 变量。

在这里插入图片描述

Java

private Stack<Integer> s1 = new Stack<>();
private Stack<Integer> s2 = new Stack<>();

// Push element x to the back of queue.
public void push(int x) {
    if (s1.empty())
        front = x;
    s1.push(x);
}

复杂度分析

时间复杂度:O(1)
向栈压入元素的时间复杂度为O(1)

空间复杂度:O(n)
需要额外的内存来存储队列元素

出队(pop)

根据栈 LIFO 的特性,s1 中第一个压入的元素在栈底。为了弹出 s1 的栈底元素,我们得把 s1 中所有的元素全部弹出,再把它们压入到另一个栈 s2 中,这个操作会让元素的入栈顺序反转过来。通过这样的方式,s1 中栈底元素就变成了 s2 的栈顶元素,这样就可以直接从 s2 将它弹出了。一旦 s2 变空了,我们只需把 s1 中的元素再一次转移到 s2 就可以了。
在这里插入图片描述
Java

// Removes the element from in front of queue.
public void pop() {
    if (s2.isEmpty()) {
        while (!s1.isEmpty())
            s2.push(s1.pop());
    }
    s2.pop();    
}

复杂度分析

时间复杂度: 摊还复杂度 O(1),最坏情况下的时间复杂度 O(n)
在最坏情况下,s2 为空,算法需要从 s1 中弹出 nn 个元素,然后再把这 nn 个元素压入 s2,在这里nn代表队列的大小。这个过程产生了 2n2n 步操作,时间复杂度为 O(n)。但当 s2 非空时,算法就只有 O(1)的时间复杂度。所以为什么叫做摊还复杂度 O(1)呢? 读了下一章你就知道了。

空间复杂度 :O(1)

摊还分析

摊还分析给出了所有操作的平均性能。摊还分析的核心在于,最坏情况下的操作一旦发生了一次,那么在未来很长一段时间都不会再次发生,这样就会均摊每次操作的代价。

来看下面这个例子,从一个空队列开始,依次执行下面这些操作:

在这里插入图片描述

单次 出队 操作最坏情况下的时间复杂度为 O(n)。考虑到我们要做 n次出队操作,如果我们用最坏情况下的时间复杂度来计算的话,那么所有操作的时间复杂度为 O(n^2)
)。

然而,在一系列的操作中,最坏情况不可能每次都发生,可能一些操作代价很小,另一些代价很高。因此,如果用传统的最坏情况分析,那么给出的时间复杂度是远远大于实际的复杂度的。例如,在一个动态数组里面只有一些插入操作需要花费线性的时间,而其余的一些插入操作只需花费常量的时间。

在上面的例子中,出队 操作最多可以执行的次数跟它之前执行过 入队 操作的次数有关。虽然一次 出队 操作代价可能很大,但是每 n 次 入队 才能产生这么一次代价为 n 的 出队 操作。因此所有操作的总时间复杂度为:n(所有的入队操作产生) + 2 * n(第一次出队操作产生) + n - 1(剩下的出队操作产生), 所以实际时间复杂度为 O(2*n)。于是我们可以得到每次操作的平均时间复杂度为 O(2n/2n)=O(1)。

判断空(empty)

s1 和 s2 都存有队列的元素,所以只需要检查 s1 和 s2 是否都为空就可以了。

Java

// Return whether the queue is empty.
public boolean empty() {
    return s1.isEmpty() && s2.isEmpty();
}

时间复杂度:O(1)
空间复杂度:O(1)

取队首元素(peek)

我们定义了 front 变量来保存队首元素,每次 入队 操作我们都会随之更新这个变量。当 s2 为空,front 变量就是对首元素,当 s2 非空,s2 的栈顶元素就是队首元素。

Java

// Get the front element.
public int peek() {
    if (!s2.isEmpty()) {
        return s2.peek();
    }
    return front;
}

时间复杂度:O(1)
队首元素要么是之前就被计算出来的,要么就是 s2 栈顶元素。因此时间复杂度为 O(1)。

空间复杂度:O(1)

作者:LeetCode
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

c++

class MyQueue {
public:
    stack<int> s1,s2;
    int front;
    /** Initialize your data structure here. */
    MyQueue() {}
    
    /** Push element x to the back of queue. */
    void push(int x) {if(s1.empty()){front=x;}s1.push(x);}
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() 
    {
        while(!s1.empty()){s2.push(s1.top());s1.pop();}
        int x=s2.top();
        s2.pop();
        if(!s2.empty())front=s2.top();
        while(!s2.empty()){s1.push(s2.top());s2.pop();}
        return x;
    }
    
    /** Get the front element. */
    int peek() 
    {
        return front;
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {return s1.empty()?true:false;}
};

5)学到的东西

思想:双栈合并即可形成队列

用front指针指向队列头部,使用peek函数的时候会比较方便

2、用队列实现栈(225、Easy)

1)题目要求

使用队列实现栈的下列操作:

push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
注意:

你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

2)我的解法

c++

class MyStack {
public:
    /** Initialize your data structure here. */
    queue<int> q,re;
    int back;
    MyStack() {

    }
    
    /** Push element x onto stack. */
    void push(int x) {q.push(x);back=x;}
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        while(q.size()>2)
        {
            re.push(q.front());q.pop();
        }
        if(q.size()==2){back=q.front();re.push(q.front());q.pop();}
        else {back=-1;}
        int x=q.front();
        q.pop();
        while(!re.empty())
        {
            q.push(re.front());re.pop();
        }
        return x;

    }
    
    /** Get the top element. */
    int top() {return back;}
    
    /** Returns whether the stack is empty. */
    bool empty() {return q.empty();}
};

3)其他解法

方法一 借用辅助队列来将要插入的元素排至队头

class MyStack {
public:
    queue<int> qi; //主队列
    queue<int> qt; //辅助队列
    MyStack() {
    }
    void push(int x) {
        while(qi.size()) { //将qi中的元素全部搬至qt
            qt.push(qi.front());
            qi.pop();
        }
        qi.push(x);  //插入新元素
        while(qt.size()) {  //将qt中的元素全部搬回qi
            qi.push(qt.front());
            qt.pop();
        }
    }
    int pop() {
        int temp = qi.front();
        qi.pop();
        return temp;
    }
    int top() {
        return qi.front();
    }
    bool empty() {
        return qi.empty();
    }
};

作者:OrangeMan
链接:link
来源:力扣(LeetCode)

方法二 通过原地进行排列将要插入的元素排至队头

class MyStack {
public:
    queue<int> q; //单队列
    MyStack() {
    }
    void push(int x) {
        q.push(x);
        for(int i = 0; i < q.size() - 1; i ++) { //交换位置
            q.push(q.front());
            q.pop();
        }
    }
    int pop() {
        int temp = q.front();
        q.pop();
        return temp;
    }
    int top() {
        return q.front();
    }
    bool empty() {
        return q.empty();
    }
};

作者:OrangeMan
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

c++

class MyStack {
public:
    /** Initialize your data structure here. */
    queue<int> q,re;
    int back;
    MyStack() {}
    
    /** Push element x onto stack. */
    void push(int x) {q.push(x);back=x;}
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        while(q.size()>2)
        {
            re.push(q.front());q.pop();
        }
        if(q.size()==2){back=q.front();re.push(q.front());q.pop();}
        else {back=-1;}
        int x=q.front();
        q.pop();
        while(!re.empty())
        {
            q.push(re.front());re.pop();
        }
        return x;

    }
    
    /** Get the top element. */
    int top() {return back;}
    
    /** Returns whether the stack is empty. */
    bool empty() {return q.empty();}
};

5)学到的东西

queue为队列,deque为双端队列

queue常用:front(),push(),pop(),size(),empty()以及back()(本题不让用back())

3、 最小值栈(155、Easy)

1)题目要求

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。

示例:

输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

提示:

pop、top 和 getMin 操作总是在 非空栈 上调用。

2)我的解法

c++

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> min;
    stack<int> s;
    MinStack() {
        
    }
    
    void push(int x) {
        if(s.empty())min.push(x);
        if(x<=min.top())min.push(x);
        s.push(x);
    }
    
    void pop() {
        if(s.top()==min.top())min.pop();
        s.pop();
        
    }
    
    int top() {return s.top();}
    
    int getMin() {return min.top();}
};

3)其他解法

方法一:辅助栈
思路

要做出这道题目,首先要理解栈结构先进后出的性质。

对于栈来说,如果一个元素 a 在入栈时,栈里有其它的元素 b, c, d,那么无论这个栈在之后经历了什么操作,只要 a 在栈中,b, c, d 就一定在栈中,因为在 a 被弹出之前,b, c, d 不会被弹出。

因此,在操作过程中的任意一个时刻,只要栈顶的元素是 a,那么我们就可以确定栈里面现在的元素一定是 a, b, c, d。

那么,我们可以在每个元素 a 入栈时把当前栈的最小值 m 存储起来。在这之后无论何时,如果栈顶元素是 a,我们就可以直接返回存储的最小值 m。

算法

按照上面的思路,我们只需要设计一个数据结构,使得每个元素 a 与其相应的最小值 m 时刻保持一一对应。因此我们可以使用一个辅助栈,与元素栈同步插入与删除,用于存储与每个元素对应的最小值。

当一个元素要入栈时,我们取当前辅助栈的栈顶存储的最小值,与当前元素比较得出最小值,将这个最小值插入辅助栈中;

当一个元素要出栈时,我们把辅助栈的栈顶元素也一并弹出;

在任意一个时刻,栈内元素的最小值就存储在辅助栈的栈顶元素中。

class MinStack {
    stack<int> x_stack;
    stack<int> min_stack;
public:
    MinStack() {
        min_stack.push(INT_MAX);
    }
    
    void push(int x) {
        x_stack.push(x);
        min_stack.push(min(min_stack.top(), x));
    }
    
    void pop() {
        x_stack.pop();
        min_stack.pop();
    }
    
    int top() {
        return x_stack.top();
    }
    
    int getMin() {
        return min_stack.top();
    }
};

作者:LeetCode-Solution
链接:link
来源:力扣(LeetCode)

方法二
解题思路
题目要求在常数时间内获得栈中的最小值,因此不能在 getMin() 的时候再去计算最小值,最好应该在 push 或者 pop 的时候就已经计算好了当前栈中的最小值。

前排的众多题解中,基本都讲了「辅助栈」的概念,这是一种常见的思路,但是有没有更容易懂的方法呢?

可以用一个栈,这个栈同时保存的是每个数字 x 进栈的时候的值 与 插入该值后的栈内最小值。即每次新元素 x 入栈的时候保存一个元组:(当前值 x,栈内最小值)。

这个元组是一个整体,同时进栈和出栈。即栈顶同时有值和栈内最小值,top()函数是获取栈顶的当前值,即栈顶元组的第一个值; getMin() 函数是获取栈内最小值,即栈顶元组的第二个值;pop() 函数时删除栈顶的元组。

每次新元素入栈时,要求新的栈内最小值:比较当前新插入元素 x 和 当前栈内最小值(即栈顶元组的第二个值)的大小。

新元素入栈:当栈为空,保存元组 (x, x);当栈不空,保存元组 (x, min(此前栈内最小值, x)))
出栈:删除栈顶的元组。

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
    }
    
    void push(int x) {
        if (st.size() == 0) {
            st.push({x, x});
        } else {
            st.push({x, min(x, st.top().second)});
        }
    }
    
    void pop() {
        st.pop();
    }
    
    int top() {
        return st.top().first;
    }
    
    int getMin() {
        return st.top().second;
    }
private:
    stack<pair<int, int>> st;
};


作者:fuxuemingzhu
链接:link
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4)自己的优化代码

c++

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> min;
    stack<int> s;
    MinStack() {
        
    }
    
    void push(int x) {
        if(s.empty())min.push(x);
        if(x<=min.top())min.push(x);
        s.push(x);
    }
    
    void pop() {
        if(s.top()==min.top())min.pop();
        s.pop();
        
    }
    
    int top() {return s.top();}
    
    int getMin() {return min.top();}
};

5)学到的东西

辅助栈

pair<int ,int>存储一个pair即一次性存储两个数

4、用栈实现括号匹配(20、Easy)

1)题目要求

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:

输入: “()”
输出: true
示例 2:

输入: “()[]{}”
输出: true
示例 3:

输入: “(]”
输出: false
示例 4:

输入: “([)]”
输出: false
示例 5:

输入: “{[]}”
输出: true

2)我的解法

c++ 双百

   bool isValid(string s) {
        stack<char> st;
        int size=s.length();
        for(int i=0;i<size;i++)
        {
            if(s[i]=='('||s[i]=='{'||s[i]=='[')st.push(s[i]);
            else if(s[i]==')'){if(!st.empty()&&st.top()=='(')st.pop();else st.push(s[i]);}
            else if(s[i]==']'){if(!st.empty()&&st.top()=='[')st.pop();else st.push(s[i]);}
            else if(s[i]=='}'){if(!st.empty()&&st.top()=='{')st.pop();else st.push(s[i]);}
        }
        return st.empty();
    }

3)其他解法

(1)

class Solution {
public:
    bool isValid(string s) {
        stack<int> temp;
        bool flag=true;
        for(int i=0;i<s.length();i++)
        {
            if(s[i]=='['||s[i]=='('||s[i]=='{')
            {
                temp.push(s[i]);
            }
            else
            {
                if(temp.empty())
                {
                    flag=false;
                    break;
                }
                char ctemp;
                if(s[i]==']')
                {
                    ctemp='[';
                }
                else if(s[i]==')')
                {
                    ctemp='(';
                }
                else
                {
                    ctemp='{';
                }
                if(temp.top()!=ctemp)
                {
                    flag=false;
                    break;
                }
                temp.pop();
            }
        }
        if(temp.empty())
            return flag;
        else    
            return false;
    }
};

作者:zzy-34f
链接:link
来源:力扣(LeetCode)

(2)
初始化栈 S。
一次处理表达式的每个括号。
如果遇到开括号,我们只需将其推到栈上即可。这意味着我们将稍后处理它,让我们简单地转到前面的 子表达式。
如果我们遇到一个闭括号,那么我们检查栈顶的元素。如果栈顶的元素是一个 相同类型的 左括号,那么我们将它从栈中弹出并继续处理。否则,这意味着表达式无效。
如果到最后我们剩下的栈中仍然有元素,那么这意味着表达式无效。

class Solution {

  // Hash table that takes care of the mappings.
  private HashMap<Character, Character> mappings;

  // Initialize hash map with mappings. This simply makes the code easier to read.
  public Solution() {
    this.mappings = new HashMap<Character, Character>();
    this.mappings.put(')', '(');
    this.mappings.put('}', '{');
    this.mappings.put(']', '[');
  }

  public boolean isValid(String s) {

    // Initialize a stack to be used in the algorithm.
    Stack<Character> stack = new Stack<Character>();

    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);

      // If the current character is a closing bracket.
      if (this.mappings.containsKey(c)) {

        // Get the top element of the stack. If the stack is empty, set a dummy value of '#'
        char topElement = stack.empty() ? '#' : stack.pop();

        // If the mapping for this bracket doesn't match the stack's top element, return false.
        if (topElement != this.mappings.get(c)) {
          return false;
        }
      } else {
        // If it was an opening bracket, push to the stack.
        stack.push(c);
      }
    }

    // If the stack still contains elements, then it is an invalid expression.
    return stack.isEmpty();
  }
}

作者:LeetCode
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

c++

  bool isValid(string s) {
        stack<char> st;
        int size=s.length();
        for(int i=0;i<size;i++)
        {
            if(s[i]=='('||s[i]=='{'||s[i]=='[')st.push(s[i]);
            else if(s[i]==')'){if(!st.empty()&&st.top()=='(')st.pop();else return false;}
            else if(s[i]==']'){if(!st.empty()&&st.top()=='[')st.pop();else return false;}
            else if(s[i]=='}'){if(!st.empty()&&st.top()=='{')st.pop();else return false;}
        }
        return st.empty();
    }

5)学到的东西

尽量不写重复代码

java知识:
map的get(key)//根据key找value
containsKey函数

5、数组中元素与下一个比它大的元素之间的距离(739、Medium)

1)题目要求

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

2)我的解法

c++(最后两个例子没通过,超出时间限制)

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        stack<int> s;
        vector<int> result(T.size());
        int j=0;
        int m=-1;
        for(int i=1;i<T.size();i++)
        {
            if(m>=0&&T[m]==T[j]){result[j]=0;j++;i=j;continue;}
            else if(T[i]>T[j]) result[j]=s.size()+1;
            else if(i!=T.size()-1){s.push(T[i]);continue;}
            else {result[j]=0;m=j;}
            j++;
            i=j;
            while(!s.empty())s.pop();
        }
        return result;

    }
};

3)其他解法

方法一:暴力
对于温度列表中的每个元素 T[i],需要找到最小的下标 j,使得 i < j 且 T[i] < T[j]。

由于温度范围在 [30, 100] 之内,因此可以维护一个数组 next 记录每个温度第一次出现的下标。数组 next 中的元素初始化为无穷大,在遍历温度列表的过程中更新 next 的值。

反向遍历温度列表。对于每个元素 T[i],在数组 next 中找到从 T[i] + 1 到 100 中每个温度第一次出现的下标,将其中的最小下标记为 warmerIndex,则 warmerIndex 为下一次温度比当天高的下标。如果 warmerIndex 不为无穷大,则 warmerIndex - i 即为下一次温度比当天高的等待天数,最后令 next[T[i]] = i。

为什么上述做法可以保证正确呢?因为遍历温度列表的方向是反向,当遍历到元素 T[i] 时,只有 T[i] 后面的元素被访问过,即对于任意 t,当 next[t] 不为无穷大时,一定存在 j 使得 T[j] == t 且 i < j。又由于遍历到温度列表中的每个元素时都会更新数组 next 中的对应温度的元素值,因此对于任意 t,当 next[t] 不为无穷大时,令 j = next[t],则 j 是满足 T[j] == t 且 i < j 的最小下标。

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        int n = T.size();
        vector<int> ans(n), next(101, INT_MAX);
        for (int i = n - 1; i >= 0; --i) {
            int warmerIndex = INT_MAX;
            for (int t = T[i] + 1; t <= 100; ++t) {
                warmerIndex = min(warmerIndex, next[t]);
            }
            if (warmerIndex != INT_MAX) {
                ans[i] = warmerIndex - i;
            }
            next[T[i]] = i;
        }
        return ans;
    }
};

作者: LeetCode-Solution
链接: link
来源:力扣(LeetCode)

方法二:单调栈
可以维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。

正向遍历温度列表。对于温度列表中的每个元素 T[i],如果栈为空,则直接将 i 进栈,如果栈不为空,则比较栈顶元素 prevIndex 对应的温度 T[prevIndex] 和当前温度 T[i],如果 T[i] > T[prevIndex],则将 prevIndex 移除,并将 prevIndex 对应的等待天数赋为 i - prevIndex,重复上述操作直到栈为空或者栈顶元素对应的温度小于等于当前温度,然后将 i 进栈。

为什么可以在弹栈的时候更新 ans[prevIndex] 呢?因为在这种情况下,即将进栈的 i 对应的 T[i] 一定是 T[prevIndex] 右边第一个比它大的元素,试想如果 prevIndex 和 i 有比它大的元素,假设下标为 j,那么 prevIndex 一定会在下标 j 的那一轮被弹掉。

由于单调栈满足从栈底到栈顶元素对应的温度递减,因此每次有元素进栈时,会将温度更低的元素全部移除,并更新出栈元素对应的等待天数,这样可以确保等待天数一定是最小的。

在这里插入图片描述在这里插入图片描述

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        int n = T.size();
        vector<int> ans(n);
        stack<int> s;
        for (int i = 0; i < n; ++i) {
            while (!s.empty() && T[i] > T[s.top()]) {
                int previousIndex = s.top();
                ans[previousIndex] = i - previousIndex;
                s.pop();
            }
            s.push(i);
        }
        return ans;
    }
};

作者: LeetCode-Solution
链接: link
来源:力扣(LeetCode)

4)自己的优化代码

c++

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        int n = T.size();
        vector<int> ans(n);
        stack<int> s;
        for (int i = 0; i< n; i++) 
        {
            while(!s.empty()&&T[i]>T[s.top()])
            {
                ans[s.top()]=i-s.top();
                s.pop();
            }
            s.push(i);
        }
        return ans;
    }
};

5)学到的东西

单调栈
从后往前遍历
用数组存放存放比当前温度高的所有温度的下标

6、循环数组中比当前元素大的下一个元素(503、Medium)

1)题目要求

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。

示例 1:

输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

2)我的解法

c++

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        stack<int> s;
        vector<int> result(nums.size(),-1);
        bool tag=true;
        for(int i=0;i<nums.size();i++)
        {
            while(!s.empty()&&nums[i]>nums[s.top()])
            {
                result[s.top()]=nums[i];
                s.pop();
            }
            if(!s.empty()&&i==s.top())break;//若同一元素两次入栈,则退出
            s.push(i);
            if(tag&&i==nums.size()-1){i=-1;tag=false;}//若循环了两次,则退出
        }
        return result;
    }
};

3)其他解法

遍历数组两次,每次pop之前都去更新一下res(因为数组有可能出现重复元素啊)

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        stack<int> stk;
        vector<int> res(nums.size(), -1);
        for(int i = 0; i < nums.size() * 2; i++)
        {
            while(stk.size() && nums[stk.top()] < nums[i%nums.size()])
            {
                if(res[stk.top()] == -1)
                {
                    res[stk.top()] = nums[i%nums.size()];
                }
                stk.pop();
            }
            stk.push(i%nums.size());
        }
        return res;
    }
};

作者:EL1S
链接: link
来源:力扣(LeetCode)

4)自己的优化代码

c++

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        stack<int> s;
        vector<int> result(nums.size(),-1);
        for(int i=0;i<nums.size()*2;i++)
        {
            while(!s.empty()&&nums[i%nums.size()]>nums[s.top()])
            {
                result[s.top()]=nums[i%nums.size()];
                s.pop();
            }
            s.push(i%nums.size());
        }
        return result;
    }
};

5)学到的东西

单调栈
遍历两次,用i%nums.size()代表下标

总结

单调栈

queue为队列,deque为双端队列

queue常用:front(),push(),pop(),size(),empty()以及back()(本题不让用back())

辅助栈

pair<int ,int>存储一个pair即一次性存储两个数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]提供了一个朴素的解法,使用两个栈来存储字符串,一个栈用来存储普通字符,另一个栈用来存储特殊字符。遍历字符串,如果是普通字符则压入第一个栈,如果是特殊字符则弹出第一个栈的栈顶元素。最后比较两个栈是否相同即可判断字符串是否有效。这个解法的时间复杂度是O(M + N),空间复杂度也是O(M + N)。\[1\] 引用\[2\]提供了另一个栈的应用场景,即判断括号是否有效。遍历字符串,如果是左括号则压入栈,如果是右括号则判断和栈顶元素是否匹配,不匹配则返回false,匹配则弹出栈顶元素。最后判断栈是否为空即可判断括号是否有效。\[2\] 引用\[3\]也提供了一个判断括号是否有效的解法,使用栈来操作。遍历字符串,如果是左括号则压入栈,如果是右括号则判断和栈顶元素是否匹配,不匹配则返回false,匹配则弹出栈顶元素。最后判断栈是否为空即可判断括号是否有效。这个解法使用了HashMap来存储括号的对应关系。\[3\] 综上所述,栈在解决字符串相关问题中有着广泛的应用,包括判断字符串是否有效、逆波兰表达式等。在解决这些问题时,栈可以帮助我们保存和处理字符的顺序,从而简化问题的处理过程。 #### 引用[.reference_title] - *1* *3* [Leetcode刷题03-栈](https://blog.csdn.net/weixin_47802917/article/details/123007699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] - *2* [leetCode-栈类型详解](https://blog.csdn.net/zhiyikeji/article/details/125508011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值