拼接最大数

问题描述:有两个整数数组nums1和nums2,它们的长度分别是m和n,现在给一个整数k,需要在这两个数组中按顺序取出k个数字,最后组成一个长度为k的数组,要求最后长度为k的数组最大。

思路:

这一题的难点在于存在两个目标数组,我们从直觉上不是很容易判断出某个数组应该取出数字的具体个数。

因此我们可以将这个问题分解成几个小部分来解决。

1.我们需要解决从单个数组取出特定数字后组成数值最大的子串。

2.解决将两个子串按照顺序能够拼接成数值最大的串。

3.既然无法具体判断某个数组应该取出多少个数字,那么我们就使用穷举法,把能够出现的情况都进行计算,记录最终结果最大的串。

所以对于第一部分,我们可以创建函数MaxSubsequence.

    vector<int> MaxSubsequence(vector<int>& nums, int k) {
        int length = nums.size();
        vector<int> stack(k, 0);
        int top = -1;
        int remain = length - k;
        for (int i = 0; i < length; i++) {
            int num = nums[i];
            while (top >= 0 && stack[top] < num && remain > 0) {
                top--;
                remain--;
            }
            if (top < k - 1) {
                stack[++top] = num;
            } else {
                remain--;
            }
        }
        return stack;
    }

对于该函数,输入一个数组和k,得到一个k个元素的按顺序最大的数组子串。

我们用栈来存储结果,其中top表示栈顶指针,remain表示数组减去k还剩的元素个数。

通过栈顶指针的移动来表示某个数字是选择还是舍弃,当前遍历数字与栈顶元素比较,如果当前遍历数字更大那么就舍弃栈顶元素,那么remain就要减1,当remain为0时说明我们已经舍弃了remain个数字,即表明供我们选择的只剩下k个了不能再舍弃只能全部选择了。

按顺序选择数字需要用栈来维护当前选择的子串,具体需要当前遍历元素与栈顶元素比较,如果不满足情况就弹出栈顶元素继续比较。本质上我们还是舍弃某些元素的,因此对于选择k个数字需要用一个remain来记录舍弃的个数,以判断是否达到了k的要求。

第二部分需要拼接两个子串,但是拼接的时候并不是首尾直接拼接的。事实上对于原题目我们是随机从两个数组中抽取数字,因此,我们应该是交叉拼接。要完成这个任务,首先先创建一个函数compare:

    int compare(vector<int>& subsequence1, int index1, vector<int>& subsequence2, int index2) {
        int x = subsequence1.size(), y = subsequence2.size();
        while (index1 < x && index2 < y) {
            int difference = subsequence1[index1] - subsequence2[index2];
            if (difference != 0) {
                return difference;
            }
            index1++;
            index2++;
        }
        return (x - index1) - (y - index2);
    }

这个函数的作用是判断两个子串从index1,index2开始哪一个更大,函数返回值是正值第一个子串大,反之第二个大。

拼接函数merge:

    vector<int> merge(vector<int>& subsequence1, vector<int>& subsequence2) {
        int x = subsequence1.size(), y = subsequence2.size();
        if (x == 0) {
            return subsequence2;
        }
        if (y == 0) {
            return subsequence1;
        }
        int mergeLength = x + y;
        vector<int> merged(mergeLength);
        int index1 = 0, index2 = 0;
        for (int i = 0; i < mergeLength; i++) {
            if (compare(subsequence1, index1, subsequence2, index2) > 0) {
                merged[i] = subsequence1[index1++];
            } else {
                merged[i] = subsequence2[index2++];
            }
        }
        return merged;
    }

定义两个指针来遍历两个数组,从头开始,依次找出两个数组中的更大的元素

最后就是最后的求结果的函数maxNumber

    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        int m = nums1.size(), n = nums2.size();
        vector<int> maxSubsequence(k, 0);
        int start = max(0, k - n), end = min(k, m);
        for (int i = start; i <= end; i++) {
            vector<int> subsequence1(MaxSubsequence(nums1, i));
            vector<int> subsequence2(MaxSubsequence(nums2, k - i));
            vector<int> curMaxSubsequence(merge(subsequence1, subsequence2));
            if (compare(curMaxSubsequence, 0, maxSubsequence, 0) > 0) {
                maxSubsequence.swap(curMaxSubsequence);
            }
        }
        return maxSubsequence;
    }

由于我们不知道具体应该在两个数组中各取多少数字,所以我们应该对于所有情况都进行计算。

在循环中的开始和结束都应该怎么定义呢?

我们假设一下,对于nums1,它的大小为m

那么一般来说遍历它的范围应该是0~min(m,k)

但是nums1并不是一定能够从0开始的,由于还有另一个数组,如果nums1选择取出0个元素,那么nums2就要取出k个元素,但是如果nums2的大小n小于k就不能满足最后的数组大小为k了。

因此开始的范围需要改进,考虑nums2是否能够完成取出。

如果n大于k那么nums1可以选择0个,如果n小于k,nums1需要选择k-n个,加上nums2选择的n个正好满足结果的k个元素。

这样我们只需使开始为max(0,k-n)即可。

每次得到的两个子串进行拼接,然后用一个数组存储历史最大的数组。

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值