ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

单调栈、单调队列

2021-07-25 01:02:23  阅读:185  来源: 互联网

标签:输出 队列 tt ++ stk int hh 单调


目录


前言

本文介绍单调栈和单调队列的使用,并且提供模板。

一、单调栈?

栈地到栈顶是单调增加或者单调减少的。

1.代码模板:

//常见模型:找出每个数左边离它最近的比它大/小的数
//stk[0]是不存放元素的,stk[tt]存放栈顶元素
int tt = 0;
for (int i = 1; i <= n; i ++ )
{
    while (tt && check(stk[tt], i)) tt -- ;	//根据栈的后进先出特点,弹出的栈顶元素一定是离i最近且比他小/大的元素
    stk[ ++ tt] = i;
}

2.例题LeetCode316

316. 去除重复字母 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

示例 1:

输入:s = "bcabc"
输出:"abc"
示例 2:

输入:s = "cbacdcbc"
输出:"acdb"

[分析]
若输入为bcacc

  1. b 入栈
  2. c 入栈时因为字典序靠后,且栈中没出现过c,直接压栈
  3. a 入栈,因为 a 的字典序列靠前且栈中a没有出现过,此时要考虑弹出栈顶元素.
    元素 c 因为在之后还有 至少一次 出现次数,所以这里可以弹出.
    元素 b 之后不再出现,为了保证至少出现一次这里不能再舍弃了.
  4. c 入栈时依然因为字典序靠后且栈中没出现过直接压栈
  5. c 入栈时栈中已经出现过c,跳过该元素
    输出结果为 bac

    [解答]
    public String removeDuplicateLetters(String s) {
        char[] chars = s.toCharArray();
        int[] count = new int[26];
        for (int i = 0; i < chars.length; i++) {
            count[chars[i] - 'a']++;
        }
        boolean[]exists = new boolean[26];
        //模板
        int tt = 0;
        int[] stk = new int[s.length() + 1];
        for (int i = 0; i < chars.length; i++) {
            if(!exists[chars[i] - 'a']) {
                while (tt != 0 && (stk[tt] > chars[i] && count[stk[tt] - 'a'] > 0)) { //根据栈的后进先出特点,弹出的栈顶元素一定是比入栈元素大,且后续还有该元素
                    exists[stk[tt] - 'a'] = false;
                    tt--;
                }
                //入栈
                stk[++tt] = chars[i];
                exists[chars[i] - 'a'] = true;
            }
            count[chars[i] - 'a']--;
        }
        StringBuilder sb=new StringBuilder();
        for (int i = 1; i <= tt; i++) { //stk[0]是不存放元素的,stk[tt]存放栈顶元素
            sb.append((char) stk[i]);
        }
        return sb.toString();
    }

3.单调栈经典题目

给定一个长度为N的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出-1。
输入格式
第一行包含整数N,表示数列长度。
第二行包含N个整数,表示整数数列。
输出格式
共一行,包含N个整数,其中第i个数表示第i个数的左边第一个比它小的数,如果不存在则输出-1。
数据范围
1≤N≤105
1≤数列中元素≤109
输入样例:
5
3 4 2 7 5
输出样例:
-1 3 -1 2 2

    public void findMin(int[] arr,int n){
        int[] stk=new int[n+1];
        int tt = 0;
        for(int i = 0; i<n; i++)
        {
            int x=arr[i];
            while (tt!=0 && stk[tt] >= x) tt--;	//把栈中大于等于x的数剔除,此时栈中只有小于x的数
            if(tt!=0) System.out.printf("%d ", stk[tt]);	//如果栈不为空,输出栈顶元素,即离x最近且小于x的值
            else System.out.printf("-1 ");	//如果栈为空,输出-1
            stk[++tt] = x;		//把x插入栈中
        }
    }

二、单调队列

1.模板

代码如下(示例):

常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
    while (hh <= tt && check_out(q[hh])) hh ++ ;  // 判断队头是否滑出窗口
    while (hh <= tt && check(q[tt], i)) tt -- ;		//判断队尾是否满足要求
    q[ ++ tt] = i;
}
原理:利用一个循环控制队头,利用一个循环控制队尾,队头决定队的长度,队尾控制队列中的序列单调

2.滑动窗口求值

给定一个大小为n≤10^6的数组。 有一个大小为k的滑动窗口,它从数组的最左边移动到最右边。 您只能在窗口中看到k个数字。 每次滑动窗口向右移动一个位置。 以下是一个例子: 该数组为[1 3 -1 -3 5 3 6 7],k为3。

您的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数n和k,分别代表数组长度和滑动窗口的长度。
第二行有n个整数,代表数组的具体数值。
同行数据之间用空格隔开。
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7

[解答:]

    public void minWindows(int arr[],int k){
        // 队列里面存的是下标
        int n=arr.length;
        int[] q=new int[n+1];
        //模板
        int hh = 0, tt = -1;
        for (int i = 0; i < n; i ++ )
        {
            while (hh <= tt && i - k + 1 > q[hh]) hh ++ ;  // 判断队头是否滑出窗口
            while (hh <= tt && arr[q[tt]] >= arr[i]) tt -- ;		//判断队尾是否满足要求
            q[ ++ tt] = i;
            if(i >= k - 1) System.out.printf("%d ", arr[q[hh]]);		//当窗口中有k个数时从队头开始输出
        }
    }

    public void maxWindows(int arr[],int k){
        // 队列里面存的是下标
        int n=arr.length;
        int[] q=new int[n+1];
        //模板
        int hh = 0, tt = -1;
        for (int i = 0; i < n; i ++ )
        {
            while (hh <= tt && i - k + 1 > q[hh]) hh ++ ;  // 判断队头是否滑出窗口
            while (hh <= tt && arr[q[tt]] <= arr[i]) tt -- ;		//判断队尾是否满足要求
            q[ ++ tt] = i;
            if(i >= k - 1) System.out.printf("%d ", arr[q[hh]]);		//当窗口中有k个数时从队头开始输出
        }
    }


总结

灵活使用模板注意:该模板是由c++ 改动来的,所以创建队列大小时,记得确定数组的大小。

标签:输出,队列,tt,++,stk,int,hh,单调
来源: https://www.cnblogs.com/clarencezzh/p/15056872.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有