单调栈 leetcode整理(一)

单调栈知识

单调栈就是一个内部元素有序的栈(大->小 or 小->大),但是只用到它的一端。
核心代码:摘自C++ | 图解算法 | 这个单调栈不一般!

insert x
while (!sta.empty() && sta.top()<x)
    sta.pop()
sta.push(x)

单调栈只能在栈顶操作.
单调栈可以解决的问题:
1、找到一个序列的字典序最小的序列(序列元素位置不可移动)
2、最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。
3、给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。
4、给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。

参考有关文章:
一招吃遍力扣四道题,妈妈再也不用担心我被套路啦~
单调栈的介绍以及一些基本性质
下面是刷题代码:

402. 移掉K位数字

402. 移掉K位数字

class Solution {
public:
    string removeKdigits(string num, int k) {
        vector<char> st;
        string result;
        for(int i = 0; i < num.size(); i++)
        {
            while(st.size() > 0 && st.back() > num[i] && k > 0)
            {
                st.pop_back();
		        k--;
            }
            //否则,进行
            st.push_back(num[i]);
        }
        //考虑到所有情况之后,还有k剩余,但是此时字符是单调递减的,需要将末尾的字符进行去除
        while(k > 0)
        {
            st.pop_back();
		    k--;
        }
        //去除前导0
        int j;
        for(j=0; j < st.size(); j++)
        {
            if(st[j] != '0') break;
        }
        for(int i = j; i < st.size() ; i++)
        {
            result += st[i];
        }
        //如果最后结果为空,返回0即可
        if(result.size() == 0) return "0";
        return result;
    }
};

1673. 找出最具竞争力的子序列

1673. 找出最具竞争力的子序列

class Solution {
public:
    vector<int> mostCompetitive(vector<int>& nums, int k) {
        vector<int> st;
        int remain = nums.size() - k;
        for(int i = 0; i < nums.size(); i++)
        {
            while(st.size() > 0 && st.back() > nums[i] && remain > 0)
            {
                st.pop_back();
		        remain--;
            }
            //否则,进行
            st.push_back(nums[i]);
        }
        while(remain > 0)
        {
            st.pop_back();
		    remain--;
        }
        return st;
    }
};

316. 去除重复字母(1081. 不同字符的最小子序列)

316. 去除重复字母
错误代码,没考虑包含text中所有不同的字符一次。
所以出现结果只是不含相同字符的字典序最小的子序列

class Solution {
public:
    string smallestSubsequence(string s) {
        vector<char > st;
        int hash[26]={0};
        for(int i = 0; i < s.size(); i++)
        {
             //如果元素大于栈顶元素,并且这个元素没有出现过,则插入
            if(st.size() == 0 || (st.back() < s[i] && hash[s[i]-'a'] == 0))
            {
                st.push_back(s[i]);
                hash[s[i]-'a'] = 1;
                //cout << st.back() <<endl;
            }
            else
            {
                //如果这个元素小于栈顶元素,并且这个元素没有出现过,那么我们就删除栈顶元素,插入这个元素
                while(st.size() > 0 && s[i] < st.back() && hash[s[i]-'a'] == 0)
                {
                    //被删除的栈顶元素对应的hash也需要清除
                    hash[st.back()-'a'] = 0;
                    st.pop_back();
                    //cout << st.back() <<endl;
                }
                st.push_back(s[i]);
                hash[s[i]-'a'] = 1;
            }
            //如果这个元素已经出现过了,那么不做任何操作
        }
        string result;
        //将vector元素转化为string
        for(int i = 0; i < st.size(); i++)
        {
            result += st[i];
        }
        return result;
    }
};

正确代码:

class Solution {
public:
    string smallestSubsequence(string s) {
        vector<char > st;
        int hash[26]={0};
        int cnt_s[26]={0};
        for(int i = 0; i < s.size() ; i++)
        {
            cnt_s[s[i] - 'a'] += 1;
        }
        for(int i = 0; i < s.size(); i++)
        {
            cnt_s[s[i] - 'a'] -= 1;
            if(hash[s[i] - 'a'] == 1) continue;
            while(st.size() > 0)
            {
                if(st.back() > s[i] && cnt_s[st.back() - 'a'] > 0)
                {
                    hash[st.back() - 'a'] = 0;
                    st.pop_back();
                }
                else
                    break;
            }
            st.push_back(s[i]);
            hash[s[i] - 'a'] = 1;
        }
        string result;
        //将vector元素转化为string
        for(int i = 0; i < st.size(); i++)
        {
            result += st[i];
        }
        return result;
    }
};

321. 拼接最大数

321. 拼接最大数
1、将k拆分为x,y,格子找nums1,nums2对应的x,y长度的最有竞争力的子序列(如果x,y大于任意数组长度,则pass这个解法)
2、对x+y=k的对应的两个子序列进行合并
3、对合并后的每个子序列,进行比较,找到最终结果.
4、补充一下比较的规则,从序列的第一个开始比较,返回对应位的较小的序列。
5、合并的规则也需要注意,并不能用简单的双指针比较,需要注意到当前数相同的情况,还要往后比较,才能选择出我们我们送入的数
哪个大取哪个,当前位置相同就继续比较下一位。这个比较的过程和比较函数有重复的操作,所以我们需要将比较函数做一下修改,保证在合并比较的时候也能使用。

compare函数从两个index开始对比,如果nums1顺位大于nums2,返回值大于0。
有个序列是另一个序列的子序列,这时我们选择较长的序列。

int compare(vector<int>& nums1,int index1, vector<int>& nums2,int index2)
    {
        int n = nums1.size();
        int m = nums2.size();
        while(index1 < n && index2 < m)
        {
            int difference = nums1[index1] - nums2[index2];
            //如果不相同,返回nums1与nums2的差值
            if (difference != 0) {
                return difference;
            }
            //如果相同,比较下一位
            index1++;
            index2++;
        }
        //如果比较到这个时候还没结果,说明有个序列是另一个序列的子序列,这时我们选择较长的序列
        return (n - index1) - (m - index2);
    }

合并函数
每次将顺位比较中较大的nums中对应的index送入result数组中。

    vector<int> merge(vector<int>& nums1, vector<int>& nums2)
    {
        int x = nums1.size();
        int y = nums2.size();
        vector<int> result(x + y,0);
        int index1 = 0, index2 = 0;
        if(x == 0) return nums2;
        else if( y == 0) return nums1;
        //此时需要进行比较,从头开始比较
        for(int i = 0; i < x + y; i++)
        {
            if(compare(nums1,index1,nums2,index2) > 0)
            {
                result[i] = nums1[index1];
                index1++;
            }
            else
            {
                result[i] = nums2[index2];
                index2++;
            }
        }
        return result;
    }

最终代码:

class Solution {
private:
    //function1:找出最具竞争力的子序列
    /*给你一个整数数组 nums 和一个正整数 k ,返回长度为 k 且最具 竞争力 的 nums 子序列。*/
    vector<int> mostCompetitive(vector<int>& nums, int k) {
        vector<int> st;
        int remain = nums.size() - k;
        for(int i = 0; i < nums.size(); i++)
        {
            while(st.size() > 0 && st.back() < nums[i] && remain > 0)
            {
                st.pop_back();
		        remain--;
            }
            //否则,进行
            st.push_back(nums[i]);
        }
        while(remain > 0)
        {
            st.pop_back();
		    remain--;
        }
        return st;
    }
    vector<int> merge(vector<int>& nums1, vector<int>& nums2)
    {
        int x = nums1.size();
        int y = nums2.size();
        vector<int> result(x + y,0);
        int index1 = 0, index2 = 0;
        if(x == 0) return nums2;
        else if( y == 0) return nums1;
        //此时需要进行比较,从头开始比较
        for(int i = 0; i < x + y; i++)
        {
            if(compare(nums1,index1,nums2,index2) > 0)
            {
                result[i] = nums1[index1];
                index1++;
            }
            else
            {
                result[i] = nums2[index2];
                index2++;
            }
        }
        return result;
    }
    int compare(vector<int>& nums1,int index1, vector<int>& nums2,int index2)
    {
        int n = nums1.size();
        int m = nums2.size();
        while(index1 < n && index2 < m)
        {
            int difference = nums1[index1] - nums2[index2];
            //如果不相同,返回nums1与nums2的差值
            if (difference != 0) {
                return difference;
            }
            //如果相同,比较下一位
            index1++;
            index2++;
        }
        //如果比较到这个时候还没结果,说明有个序列是另一个序列的子序列,这时我们选择较长的序列
        return (n - index1) - (m - index2);
    }
public:
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        int n = nums1.size();
        int m = nums2.size();
        //用来放最终结果
        vector<int> maxSubsequence(k,0);
        vector<int> cur_maxSubsequence(k,0);
        //【1】将k拆分为x,y,格子找nums1,nums2对应的x,y长度的最有竞争力的子序列
        for(int x = 0; x <= n; x++)
        {
            int y = k - x;
            //如果x,y大于任意数组长度,则pass这个解法)
            if(y > m || y < 0 || x < 0 || x > n) continue;
            vector<int> maxSubsequence1 = mostCompetitive(nums1,x);
            vector<int> maxSubsequence2 = mostCompetitive(nums2,y);
            //【2】对x+y=k的对应的两个子序列进行合并
            cur_maxSubsequence = merge(maxSubsequence1,maxSubsequence2);
            //【3】
            if(compare(cur_maxSubsequence,0,maxSubsequence,0) > 0)
                maxSubsequence = cur_maxSubsequence;
        }
        return maxSubsequence;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

拾牙慧者

欢迎请作者喝奶茶

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

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

打赏作者

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

抵扣说明:

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

余额充值