算法题总结(七)——栈与队列

news2024/11/27 14:42:22

1、栈常用操作

1)栈定义
Stack<Integer> stack = new Stack<Integer>();2)栈操作
.栈是否为空
isEmpty();
.查询栈顶元素,不改变栈
peek();
.弹出栈顶元素,改变栈
pop();
.压入栈顶
push();
.栈中元素的个数
size();

2、队列常用操作

Queue queue = new LinkedList();

Queue 单端队列:

Deque双端队列:

3、用栈实现队列

用两个栈来模拟队列,一个负责进栈,一个负责出栈,当出队的时候,从输出栈里出,如果是空的,把入栈全部放入输出栈中。

class MyQueue {
    Stack<Integer> StackIn;
    Stack<Integer> StackOut;

    public MyQueue() {
        StackIn =new Stack<>(); //负责进栈
        StackOut =new Stack<>(); //负责出栈
    }

    public void push(int x) {
        StackIn.push(x);

    }

    public int pop() {
        dumpstackIn();
        return StackOut.pop();
    }

    public int peek() {

        dumpstackIn();
        return StackOut.peek();

    }

    public boolean empty() {

        return StackIn.isEmpty() && StackOut.isEmpty();

    }

    //判断输出栈是否为空,如果为空,把输入栈的元素全部放入输出栈中
    private void dumpstackIn()
    {
        if(!StackOut.isEmpty())  return ;
        while(!StackIn.isEmpty())
        {
            StackOut.push(StackIn.pop());
        }
    }

}

4、用队列实现栈

用一个队列来实现栈的操作,弹出栈或者得到栈顶元素的时候,把队列的前n-1和元素重新插到队尾,然后出队

class MyStack {

    // 用一个队列来实现栈的操作
    Queue<Integer> queue;

    public MyStack() {
        queue= new LinkedList<>();
    }

    public void push(int x) {
        queue.add(x);
    }

    public int pop() {
        RePosition();
        return queue.poll();
    }

    public int top() {
        RePosition();
        int result =queue.poll();
        queue.add(result);
        return result;
    }

    public boolean empty() {
        return queue.isEmpty();

    }
    //把队列的前n-1和元素重新插到队尾
    private void RePosition()
    {
        int size =queue.size();
        size--;
        while(size>0)
        {
            queue.add(queue.poll());
            size--;
        }
    }
}

20、有效的括号

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

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

首先应该明确三种不匹配的情况:

第一种情况,字符串里左方向的括号多余了 ,所以不匹配。

第二种情况,括号没有多余,但是括号的类型没有匹配上。

第三种情况,字符串里右方向的括号多余了,所以不匹配。

即:

第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false

那么什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈是空的,就说明全都匹配了。

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for(int i=0;i<s.length();i++)
        {
            //如果是左括号,就把相应的右括号入栈,这样后面碰见右括号直接和栈顶元素比较是否相等就可以了,不需要再分情况讨论。
            char ch=s.charAt(i);
            if(ch=='(')  stack.push(')');
            else if(ch=='{')    stack.push('}');
            else if(ch=='[')  stack.push(']');
                //其余是右括号
            else{
                //第三种情况,栈为空
                if(stack.isEmpty())
                    return false;
                    //第二种情况:括号不匹配
                else if(ch!=stack.peek())
                    return false; 
                else if(ch==stack.peek())
                    stack.pop();
            }
        }
        if(stack.isEmpty())
            return true;
            //第一种情况:遍历完成后栈不为空
        else
            return false;

    }
}

1047、 删除字符串中的所有相邻重复项

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:

输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。

方法一:使用栈

class Solution {
    public String removeDuplicates(String s) {
        //使用栈,判断当前元素与上一个元素是否相等
        Stack<Character> stack =new Stack<>();

        char ch;
        for(int i=0;i<s.length();i++)
        {
            ch=s.charAt(i);
            //等于空或者不相等
            if(stack.isEmpty() || stack.peek()!=ch)
                stack.push(ch);
            else if(!stack.isEmpty() && stack.peek()==ch)
            {
                stack.pop();
            }

        }

        //用字符串拼接的方法

        // String str= "";
        // while(!stack.isEmpty()) 
        //  {
        //      str=stack.pop()+ str;   //使用string加在前面
        //}

        //用一个数组从后往前接收
        int n=stack.size();
        char[] chars =new char[n];
        n--;
        while(!stack.isEmpty())
        {
            chars[n--]=stack.pop();
        }
        return new String(chars);  

    }
}

方法二:使用StringBuilder来代替栈:

class Solution {
    public String removeDuplicates(String s) {
        //使用StringBuilder来代替栈
        StringBuilder sb =new StringBuilder();
        int top=-1; //充当栈指针
        char ch;
        for(int i=0;i<s.length();i++)
        {
            ch=s.charAt(i);
            if(top>=0 && ch==sb.charAt(top))
            {
                sb.deleteCharAt(top);
                top--;
            }
            else
            {   
                sb.append(ch);
                top++;
            }
        }
        return sb.toString();    
    }
}

方法三:使用双指针

class Solution {
    public String removeDuplicates(String s) {
        //使用双指针
        char[] chars=s.toCharArray();
        int slow=0;
        for(int fast=0;fast<chars.length;fast++)
        {
            //因为移动新元素后还有可能消除,所以先覆盖,再判断,即本题是在新的数组上进行判断比较的
            chars[slow]=chars[fast];
            if(slow>0 && chars[slow]==chars[slow-1])
                slow--;
            else
                slow++;
        }
        return new String(chars,0,slow);
    }
}

150、逆波兰表达式求值

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

示例 1:

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
class Solution {
    public int evalRPN(String[] tokens) {

        Stack<Integer> stack =new Stack<>();
        for(String s:tokens)
        {
            if(s.equals("+")) stack.push(stack.pop()+stack.pop());
            else if(s.equals("-")) 
                stack.push( -stack.pop()+stack.pop()); //注意先取出来的是负的
            else if(s.equals("*")) 
                stack.push(stack.pop() * stack.pop());
            else if(s.equals("/"))
            {    int temp1=stack.pop();
             int temp2=stack.pop();
             stack.push(temp2/temp1);

            }
            else{

                stack.push(Integer.valueOf(s));
            }
        }
        return stack.peek();

    }
}

Integer和String的转换

//Integer转为String
//方法一:Integer类的静态方法toString()
Integer a = 2;
String str = Integer.toString(a)

//方法二:Integer类的成员方法toString()
Integer a = 2;
String str = a.toString();

//方法三:String类的静态方法valueOf()
Integer a = 2;
String str = String.valueOf(a);

//String转为Integer
i = Integer.valueOf(str);

#239、滑动窗口的最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

定义一个单调队列,里面维护能成为最大值的元素,当窗口滑动时,滑出的元素和单调队列的最大值,即出口元素进行比较,如果相等则弹出。

滑进的元素,和队列元素相比,如果大于队尾元素,则队尾元素出队。

class MyQueue {
    Deque<Integer> deque = new LinkedList<>();
    //弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
    //同时判断队列当前是否为空
    void poll(int val) {
        if (!deque.isEmpty() && val == deque.peek()) {
            deque.poll();
        }
    }
    //添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
    //保证队列元素单调递减
    //比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
    void add(int val) {
        while (!deque.isEmpty() && val > deque.getLast()) {
            deque.removeLast();
        }
        deque.add(val);
    }
    //队列队顶元素始终为最大值
    int peek() {
        return deque.peek();
    }
}

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length == 1) {
            return nums;
        }
        int len = nums.length - k + 1;
        //存放结果元素的数组
        int[] res = new int[len];
        int num = 0;
        //自定义队列
        MyQueue myQueue = new MyQueue();
        //先将前k的元素放入队列
        for (int i = 0; i < k; i++) {
            myQueue.add(nums[i]);
        }
        res[num++] = myQueue.peek();
        for (int i = k; i < nums.length; i++) {
            //滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
            myQueue.poll(nums[i - k]);
            //滑动窗口加入最后面的元素
            myQueue.add(nums[i]);
            //记录对应的最大值
            res[num++] = myQueue.peek();
        }
        return res;
    }
}

使用双端队列作为单调队列。

class Solution {
    //单调队列,维护一个单调递减队列,队头维护的是最大值
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n=nums.length;
        Deque<Integer> duque=new LinkedList<>();
        int[] ans = new int[n-k+1];
        for(int i=0;i<k;i++)
        {   
            //右边的元素如果大于左边的,那么就可以把左边的元素除去
            while(!duque.isEmpty()&& nums[i]>duque.peekLast())
            {    
                duque.pollLast();
            }
            duque.offerLast(nums[i]);         
        }
        ans[0]=duque.peekFirst();
        for(int i=k;i<n;i++)
        {
            while(!duque.isEmpty()&&nums[i]>duque.peekLast())
            {
                duque.pollLast();
            } 
            duque.offerLast(nums[i]);
            //左窗口移动,如果移动出去的是最大值,则把最大值出队
            if(duque.peekFirst()==nums[i-k])
                duque.pollFirst();
            ans[i-k+1]=duque.peekFirst();

        }  
        return ans;   
    }
}

347、前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

思路:

  1. 要统计元素出现频率

使用一个map

  1. 对频率排序

使用优先级队列

  1. 找出前K个高频元素

优先级队列一定要制定比较规则。

利用大顶堆:时间复杂度O(n log n)

/*Comparator接口说明:
 * 返回负数,形参中第一个参数排在前面,所以从小到大的时候是第一个减去第二个;返回正数,形参中第二个参数排在前面,
 所以从大到小的时候是第二个减去第一个
 * 对于队列:排在前面意味着往队头靠
 * 对于堆(使用PriorityQueue实现): 从队头到队尾按从小到大排就是最小堆(小顶堆),
 *                               从队头到队尾按从大到小排就是最大堆(大顶堆)--->队头元素相当于堆的根节点
 **/

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //用大顶堆实现排序

        //map存储元素,以及元素出现的次数
        Map<Integer,Integer> map =new HashMap<>();
        for(int num:nums)
        {
            map.put(num,map.getOrDefault(num,0)+1);
        }

        //优先队列 存储数组[元素,次数], 从大到小排,想当于大根堆
        PriorityQueue<int[]> pq =new PriorityQueue<>((pair1,pair2)->pair2[1]-pair1[1]);
        
        for(Map.Entry<Integer,Integer> entry :map.entrySet()){
            pq.add(new int[]{entry.getKey(),entry.getValue()});
        }

        int ans[] =new int [k];
        for(int i=0;i<k;i++)
        {
            ans[i]=pq.poll()[0];
        }
        return ans;

    }
}

使用大顶堆,每次需要对n大小的堆进行排序

方法二:利用小根堆,复杂度O(n log k) (重点)

只需要维持一个k大小的小顶堆,每个元素与堆顶元素比较

//解法2:基于小顶堆实现
public int[] topKFrequent2(int[] nums, int k) {
    Map<Integer,Integer> map = new HashMap<>();//key为数组元素值,val为对应出现次数
    for(int num:nums){
        map.put(num,map.getOrDefault(num,0)+1);
    }
    //在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
    //出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(相当于小顶堆)
    PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);
    for(Map.Entry<Integer,Integer> entry:map.entrySet()){//小顶堆只需要维持k个元素有序
        if(pq.size()<k){//小顶堆元素个数小于k个时直接加
            pq.add(new int[]{entry.getKey(),entry.getValue()});
        }else{
            if(entry.getValue()>pq.peek()[1]){//当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
                pq.poll();//弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了
                pq.add(new int[]{entry.getKey(),entry.getValue()});
            }
        }
    }
    int[] ans = new int[k];
    for(int i=k-1;i>=0;i--){ //依次弹出小顶堆,先弹出的是堆的根,出现次数少,后面弹出的出现次数多
        ans[i] = pq.poll()[0];
    }
    return ans;
}
}

总结:两大利器:单调队列和优先级队列

215、数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:

  • 1 <= k <= nums.length <= 105
  • -104 <= nums[i] <= 104

方法一:使用大根堆

  • 时间复杂度:O(NlogN),遍历数据 O(N),堆内元素调整 O(logN);
  • 空间复杂度:O(N)
class Solution {
    public int findKthLargest(int[] nums, int k) {
        //优先级队列,从大到小排列
        PriorityQueue<Integer> queue =new PriorityQueue<>((num1,num2)->num2-num1);
        for(int i=0;i<nums.length;i++)
        {
            queue.add(nums[i]);    //复杂度nlogn
        }
        //把前k-1大个元素除去
        for(int i=0;i<k-1;i++)
        {
            queue.poll();
        }
        return queue.peek();
    }
}

方法二:使用小根堆

  • 时间复杂度:O(NlogK),遍历数据 O(N),堆内元素调整 O(logK);
  • 空间复杂度:O(K)
class Solution {
    public int findKthLargest(int[] nums, int k) {
    
    PriorityQueue<Integer> pq =new PriorityQueue<>((num1,num2)->num1-num2);
    for(int num:nums){ 
        if(pq.size()<k){    //使用最小堆,堆不满时,就添加元素,堆满时,如果元素大于最小的元素,就弹出并把这个元素加入,
                            //因为找的是前k个最大的值,如果元素比堆中最小值还小,就没必要添加
            pq.add(num);
        }else{
            if(num>pq.peek()){ //当前元素大于堆中最小的一个
                pq.poll();
                pq.add(num);
            }
        }
    }
        return pq.peek();  //此时堆中就是前k个最大的值,因为是最小堆,所以第一个就是第k个最大的值。


    }
}

方法三:基于快速排序的快速选择

快速排序的核心包括“哨兵划分” 和 “递归” 。

哨兵划分: 以数组某个元素(一般选取首元素)为基准数,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。

递归: 对 左子数组 和 右子数组 递归执行 哨兵划分,直至子数组长度为 1 时终止递归,即可完成对整个数组的排序。

下图展示了数组 [2,4,1,0,3,5] 的快速排序流程。

「快速选择」:设 N 为数组长度。根据快速排序原理,如果某次哨兵划分后,基准数的索引正好是 N−k ,则意味着它就是第 k 大的数字 。此时就可以直接返回它,无需继续递归下去了。

我们知道快速排序的性能和「划分」出的子数组的长度密切相关。直观地理解如果每次规模为 n 的问题我们都划分成 1 和 n−1,每次递归的时候又向 n−1 的集合中递归,这种情况是最坏的,时间代价是 O(n2)。我们可以引入随机化来加速这个过程,它的时间代价的期望是 O(n),证明过程可以参考「《算法导论》9.2:期望为线性的选择算法」。需要注意的是,这个时间复杂度只有在 随机数据 下才成立,而对于精心构造的数据则可能表现不佳。

使用「三路划分」,即每轮将数组划分为三个部分:小于、等于和大于基准数的所有元素。这样当发现第 k 大数字处在“等于基准数”的子数组中时,便可以直接返回该元素。

class Solution {
    public int findKthLargest(int[] nums, int k) {
       List<Integer> numList =new ArrayList<>();
       for(int num:nums)
       {
            numList.add(num);
       }
       return quickSelect(numList,k);
    }
    public int quickSelect(List<Integer> nums,int k)
    {
        Random rand=new Random();
        int pivot =nums.get(rand.nextInt(nums.size())); 
                                //在0-nums.size()-1中生成随机数
        List<Integer> smail=new ArrayList<>();
        List<Integer> equal =new ArrayList<>();
        List<Integer> big=new ArrayList<>();
        for(int num:nums)
        {
            if(num>pivot)
                big.add(num);
            else if(num<pivot)
                smail.add(num);
            else
                equal.add(num);
        }
        if(k<=big.size())
            return quickSelect(big,k);
        else if(big.size()+equal.size()<k)
            return quickSelect(smail,k-big.size()-equal.size());
        else  //第k大元素在equal中, 返回pivot
            return pivot;
    }
}

295、 数据流的中位数

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3 。
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。
  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

class MedianFinder {

    PriorityQueue<Integer> queMin;  //记录小于中位数的数
    PriorityQueue<Integer> queMax;  //记录大于中位数的数

    //当累计添加的数的数量为奇数时。
    //queMin中的数的数量比 queMax多一个
    //此时中位数为 queMin的对头
    //当累计添加的数的数量为偶数时,两个优先队列中的数的数量相同,
    //此时中位数为它们的队头的平均值。

    //维护两个队列,来使得数保持有序的分开存储
    public MedianFinder() {
        queMin = new PriorityQueue<Integer>((a, b) -> (b - a));  //从大到小
        queMax = new PriorityQueue<Integer>((a, b) -> (a - b));  //从小到大
    }
    
    public void addNum(int num) {
        if(queMin.isEmpty() ||num<=queMin.peek())  //
        {
            queMin.add(num);
            if(queMin.size()>queMax.size()+1)   //保持queMin比queMax多一个或者相等
                queMax.add(queMin.poll());
        }
        else
        {
            queMax.add(num);
            if(queMax.size()>queMin.size())
                queMin.add(queMax.poll());
        }

    }

    public double findMedian() {
        if(queMin.size()>queMax.size())
            return queMin.peek();
        return (queMin.peek()+queMax.peek()) /2.0;

    }
}

394、字符串编码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"
class Solution {
    public String decodeString(String s) {
        int k=0;
        StringBuilder res=new StringBuilder();  //保存结果
        Stack<Integer> kstack=new Stack<>();
        Stack<StringBuilder> restack=new Stack<>();

        for(char c:s.toCharArray())
        {
            if(c=='[')   //碰到左括号,记录k和当前的res,并归零
            {
                kstack.push(k);
                restack.push(res);
                k=0;
                res=new StringBuilder();
            }
            else if(c==']')
                //遇到右括号,出栈最近的一个k,即左括号之前的栈,并重复计算
            {
                int curk=kstack.pop();
                StringBuilder temp=new StringBuilder();
                //重复计算括号内的字符
                for(int i=0;i<curk;i++)
                {
                    temp.append(res);
                }
                res=restack.pop().append(temp);
            }
            else if(c>='0' && c<='9')
            {
                k=c-'0'+k*10;
            }
            else{
                //如果是字母就添加
                res.append(c);
            }
        } 
        return res.toString();
    }
}

155、最小栈

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

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

示例 1:

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

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

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

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

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

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

class MinStack {
    Deque<Integer> stack;
    Deque<Integer> minstack;   //记录与栈相对应的最小值
    public MinStack() {
        stack=new LinkedList<>();
        minstack=new LinkedList<>();
        minstack.push(Integer.MAX_VALUE);
    }

    public void push(int val) {
        stack.push(val);
        minstack.push(Math.min(minstack.peek(),val)); //最小值队列中相当于存储的是元素a之前 对应的栈的最小值
    }

    public void pop() {
        stack.pop();
        minstack.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minstack.peek();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2192677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Quantify LLM

大模型如果用bfloat16需要特别大的显存&#xff0c;所以都在用INT4、INT8做量化&#xff0c;效果不错 量化学习 为什么量化 对Llama13B模型来说&#xff0c;不同算子加载需要的显存不同 中间是TensorCore&#xff0c;左右两边是显存&#xff0c;加载过程中模型要频繁地将wei…

【移动端】事件基础

一、移动端事件分类 移动端事件主要分为以下几类&#xff1a; 1. 触摸事件&#xff08;Touch Events&#xff09; 触摸事件是移动设备特有的事件&#xff0c;用来处理用户通过触摸屏幕进行的操作。主要的触摸事件有&#xff1a; touchstart&#xff1a;手指触摸屏幕时触发。…

k8s 之动态创建pv失败(踩坑)

作者&#xff1a;程序那点事儿 日期&#xff1a;2024/02/05 18:53 初学容易范的错&#xff01; 问题简述&#xff1a; StorageClass创建成功&#xff0c;StatefulSet 启动不成功&#xff0c;与pvc关联的po和pvc一直是pending状态。 也就是说&#xff0c;StorageClass没有动态…

MQTT--Java整合EMQX

目录 1、简介2、准备3、使用步骤3.1 引入依赖3.2 创建生产者和消费者3.3 测试 总结PS: 1、简介 本文章实现了一个简单的MQTT客户端&#xff0c;使用Eclipse Paho库让Java和EMQX整合&#xff0c;测试客户端初始化时配置Broker地址、客户端ID、用户名和密码。连接成功后&#xf…

python sqlite3 工具函数

起因&#xff0c; 目的: sqlite3 最常用的函数。 比如&#xff0c;某人给了一个 database.db 文件。 但是你登录的时候&#xff0c;不知道账号密码。 此文件就是&#xff0c;查看这个数据库的详细内容。 有哪些表某个表的全部内容。添加数据 代码&#xff0c; 见注释 impor…

字节跳动收购Oladance耳机:强化音频技术,加速VR/AR生态布局

字节跳动收购Oladance耳机&#xff1a;加码VR/AR领域布局 近日&#xff0c;字节跳动宣布已完成对开放式耳机品牌Oladance的收购&#xff0c;实现了对该品牌的100%控股。这一收购标志着字节跳动在AI硬件领域的进一步扩展和深化&#xff0c;特别是对其VR/AR领域布局的重要加码。 …

python之运算符

1、算术运算符 算术运算符常用的有&#xff1a;&#xff0c;-&#xff0c;*&#xff0c; &#xff0c;/&#xff0c;//&#xff0c;%&#xff0c;>>,<< 1.1、加 常见的是算术相加&#xff0c;还有一种是字符串拼接。 a 10 b 20 print(a b) c "My &quo…

css 简单网页布局——浮动(一)

1. 三种布局方式 1.1 标准流 1.2 浮动的使用 1.3 简述浮动 1.3.1 浮动三大特性 <style>.out {border: 1px red solid;width: 1000px;height: 500px;}.one {background-color: aquamarine;width: 200px;height: 100px;}.two {background-color: blueviolet;width: 200px;h…

如何在算家云搭建CosyVoice(文生音频)

一、CosyVoice简介 CosyVoice 是一个开源的超强 TTS&#xff08;‌文本转语音&#xff09;‌模型&#xff0c;‌它支持多种生成模式&#xff0c;‌具有极强的语音自然可控性。‌ 具有以下特点&#xff1a; 语音合成 &#xff1a;能够将文本转换为自然流畅的语音输出。多语种…

让你的Github Profile高大时尚!

目录 前言 正文 GitHub Profile 特点&#xff1a; GitHub Actions 核心概念&#xff1a; 应用场景&#xff1a; RSS RSS的主要特点&#xff1a; 使用场景&#xff1a; RSS的工作原理&#xff1a; 关于Github Readme Card 关于Github贡献的3D图 关于个人最新博文的获取 关于代码…

今日指数-day08实战完整代码

今日指数-day08 1. 个股最新分时行情数据 1.1 个股最新分时行情功能说明 1&#xff09;个股最新分时行情功能原型 2&#xff09;个股最新分时行情数据接口分析 功能描述&#xff1a;获取个股最新分时行情数据&#xff0c;主要包含&#xff1a;开盘价、前收盘价、最新价、最…

(作业)第三期书生·浦语大模型实战营(十一卷王场)–书生基础岛第4关---InternLM + LlamaIndex RAG 实践

基础任务 (完成此任务即完成闯关) 任务要求&#xff1a;基于 LlamaIndex 构建自己的 RAG 知识库&#xff0c;寻找一个问题 A 在使用 LlamaIndex 之前InternLM2-Chat-1.8B模型不会回答&#xff0c;借助 LlamaIndex 后 InternLM2-Chat-1.8B 模型具备回答 A 的能力&#xff0c;截…

【MaskGAN】MaskGAN: Towards Diverse and Interactive Facial Image Manipulation

文章目录 MaskGAN: Towards Diverse and Interactive Facial Image Manipulationkey points贡献方法密集映射网络DMN编辑行为模拟训练多目标学习CelebAMask-HQ数据集实验消融实验总结MaskGAN: Towards Diverse and Interactive Facial Image Manipulation 会议/期刊:CVPR 202…

磁盘存储链式结构——B树与B+树

红黑树处理数据都是在内存中&#xff0c;考虑的都是内存中的运算时间复杂度。如果我们要操作的数据集非常大&#xff0c;大到内存已经没办法处理了该怎么办呢&#xff1f; 试想一下&#xff0c;为了要在一个拥有几十万个文件的磁盘中查找一个文本文件&#xff0c;设计的…

测试-BUG篇

文章目录 软件测试的生命周期BUGbug的概念描述bug的要素bug级别bug的生命周期 与开发产生争执怎么办&#xff08;高频考题&#xff09; 软件测试的生命周期 软件测试贯穿于软件的整个生命周期 BUG bug的概念 是指计算机程序中存在的一个错误(error)、缺陷(flaw)、疏忽(mista…

容器领航者:Kubernetes集群部署秘籍

目录 前言 一、ubernetes简介 1.1 应用部署方式演变 1.2 容器编排应用 1.3 kubenetes简介 二、Kubernetes核心概念 2.1 K8S各个组件用途 2.2 K8S 各组件之间的调用关系 2.3 K8S 的常用名词感念 2.4 k8S的分层架构​编辑 三、部署前的准备 3.1 k8s中容器的管理方式 3…

自然种子启发,4D 打印软机器人,环境探索新工具

想象一下&#xff0c;一颗小小的种子&#xff0c;竟能成为软机器人的灵感源泉&#xff01;在科技的奇妙世界里&#xff0c;受牻牛儿苗科种子启发&#xff0c;研究人员利用 4D 打印技术&#xff0c;打造出了具有独特性能的软机器人——《4D Printing of Humidity‐Driven Seed I…

免费!推荐10个可商用模特图片素材网站!

平时我也会用到一些可商用的模特图片素材&#xff0c;包括正版商用和免费商用的都有&#xff0c;接下来罗列国内外10个主流图片下载站点&#xff0c;保证你可以快速得到你想要的正版模特素材&#xff01; 1.包图网 传送门&#xff1a;https://ibaotu.com/sy 国内的一个高清分…

Acwing 区间DP 计数类DP

1.区间DP Acwing 282.石子合并 思路分析&#xff1a; f(i,j)表示将第i堆石子到第j堆石子合并为一堆时的最小代价&#xff1b;状态划分&#xff1a;选一个分割点k&#xff0c;将[i-k]和[k1,j]这两个区间的石子合并&#xff0c;然后加上两个区间的合并总代价&#xff08;采用…

在线Xpath匹配定位测试工具

具体请前往&#xff1a;在线Xpath-匹配-定位-调试/测试工具