LeetCode每日一题--321. 拼接最大数(单调栈)

题目:跳转至 321. 拼接最大数
给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。
说明: 请尽可能地优化你算法的时间和空间复杂度。
示例 1:
输入:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
输出:
[9, 8, 6, 5, 3]
示例 2:
输入:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
输出:
[6, 7, 6, 0, 4]
示例 3:
输入:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
输出:
[9, 8, 9]

class Solution {
public:
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {

    }
};

思路:
看题解,刚开始还没太明白为什么要单调栈以及怎么维护,看懂了还是夸赞。要获取单个序列nums中k个数量的最大(以原顺序)序列,计算nums的长度与k的差值,从左往右遍历nums,如果单调栈中有值并且栈顶值小于当前遍历值,且nums还有值,就把栈顶值和差值都减1,如果栈头没满,进栈,满了就差值减1。
写的有点乱,举个例子吧,序列nums为[9, 1, 2, 5, 8, 3, 1],取k=3时,单调栈中应该如下:[9,0,0]->[9,1,0]->[9,2,0]->[9,5,0]->[9,8,0]->[9,8,3]->[9,8,3]
理解了单调栈,再来看题目,两个序列按原顺序合并取出长度为k的最大数,那么要从两个序列中获取各自的最大子序列再进行合并(两个序列按原顺序比较大小合并这个比较简单),就要确认两个序列中分别要取出的个数,就得把所有可能循环。每个序列中能得出的子序列数目必然大于等于0,小于等于k,同时起点大于等于k与另一个序列长度的差值,终点小于等于k。比较所有可能结果获取最大值。
写完提交提示错误,查看发现把合并想简单了,不能简单用双指针判断两个序列当前值的大小比较合并,因为他们是按原序列中的顺序排列的。继续看解答代码,合并函数中也用到比较函数,好厉害,也对,比较函数本来就是比较两个序列大小,只不过按上述思路只要求比较同等数量下的序列大小,现在多加两个索引,比较从索引开始的序列大小。

class Solution {
public:
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        int len1=nums1.size();
        int len2=nums2.size();
        int start=max(0,k-len2);
        int end=min(k,len1);
        vector<int> res;
        for(int i=start;i<=end;++i){
            vector<int> maxseq1=maxSequence(nums1,i);
            vector<int> maxseq2=maxSequence(nums2,k-i);
            vector<int> meargeseq=meargeSequence(maxseq1,maxseq2);
            if(res.size()==0)
                res=meargeseq;
            else
                res=compareSequence(res,0,meargeseq,0)>0?res:meargeseq;
        }
        return res;
    }
    vector<int> maxSequence(vector<int>& nums, int k){
        int len=nums.size();
        vector<int> res(k,0);  //初始化单调栈
        int top=-1;
        int remain=len-k;
        for(int i=0;i<len;++i){
            int num=nums[i];
            while(top>=0 && res[top]<num && remain>0){   //维护最大序列
                --top;
                --remain;
            }
            if(top<k-1)
                res[++top]=num;   //栈没满时才进栈
            else
                --remain;   //栈满时当前值未被用到,序列剩余可用值-1
        }
        return res;
    }
    vector<int> meargeSequence(vector<int>& nums1, vector<int>& nums2){
        int len1=nums1.size();
        int len2=nums2.size();
        vector<int> res(len1+len2,0);
        int index1=0,index2=0;
        while(index1<len1 && index2<len2){
            if(compareSequence(nums1,index1,nums2,index2)>0){
                res[index1+index2]=nums1[index1];
                ++index1;
            }
            else{
                res[index1+index2]=nums2[index2];
                ++index2;
            }
        }
        while(index1<len1){
            res[index1+index2]=nums1[index1];
            ++index1;
        }
        while(index2<len2){
            res[index1+index2]=nums2[index2];
            ++index2;
        }
        return res;
    }
    int compareSequence(vector<int>& nums1,int index1,vector<int>& nums2,int index2){ //返回值大于0返回nums1
        int len1=nums1.size();
        int len2=nums2.size();
        while(index1<len1 && index2<len2){
            int difference=nums1[index1]-nums2[index2];
            if(difference!=0)
                return difference;
            ++index1;
            ++index2;
        }
        //return (len1 - index1) - (len2 - index2);
        //上面是题解的直接没太懂,换个写法,如果一方值用完还相等,返回先用完的是不对的,因为单调递减,所以先用完的最后一个值也是小于另一个序列对应位置的之前的值
        if(index1==len1)
            return -1;
        return 1;
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值