【LeetCode刷题笔记-24 321:拼接最大数】

题目:
在这里插入图片描述
之前刷的题里面有相似的,比如说移掉K位数字(笔记7),下一个排列数(笔记2)。所以,今天这道题说要在两个数组中找出最大排列的时候,我第一个想到的方法就是利用栈来处理。但是思考了很久,也不知道怎么组织两个栈之间的关系,后来根据题解,才完全弄清楚。

为什么要这么做?

我觉得我们做题,不只是要弄懂它的方法,还要尽量去想,为什么能想到这个做法。

1.首先呢,根据以往的经验,用栈来处理这种最大排列,最大子序列的问题是最方便的,因为它可以比较栈顶数字和下一个数字的大小,来选择性的入栈和出栈。

比较栈顶数字和下一个数字的大小一般都是从数学的角度分析的,比如移除K位数字的那题,从数学角度上来说,越靠前的数字越大影响力越大,那么我们就找到第一个非降序的数字,然后不断弹出栈顶直到可以继续构成降序序列为止。

同样,这道题我们也有相同的思考方式。

首先这个数组是由两个数组的数字构成,那么我们就要先去找出这两个数组的最大子序列(可以用反证法证明)。

找最大子序列毫无疑问使用栈会方便很多,但是还有一个问题,就是这两个数组每个数组出多少个元素呢?

所以,这就需要对每一种情况都进行考虑。假如数组nums1出X个,nums2就出K-X个。

2.找到最大子序列之后,怎么拼接呢?

我们遵循两条原则:

第一,如果当前元素不同,理所当然应当选择大的那个入队。

第二,如果当前元素相同,则需要去找到它们后面第一个不相同的元素对,去判定大小,然后将较大的那个数组的当前元素入队。

操作完成以后,有元素入队的那个数组指针自加。

3.所以我们就有了三大步

第一步,对每一种数组个数的选择(x+y=k),用栈找出最大子序列

第二步,按规则拼接元素

第三步,找出最大的那一个拼接起来的元素

然后,就可以去写算法代码了。

这里我引用的是官方题解的代码,因为我自己写的太臃肿了,题解的比较精炼,所有注释我都加好了,一些代码细节还需要一定的理解。

比如怎么保证最大子序列返回的元素个数。

比如怎么让拼接操作较长的那个数组将尾部数字逐一拼接。

题解的代码十分简洁而且逻辑性很强,所以我在这里贴出题解的代码附带详细注释:

#include<iostream>
#include<vector>
using namespace std;

class Solution {
public:
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        int m = nums1.size(), n = nums2.size();//m,n分别是nums1和nums2的长度 
        vector<int> maxSubsequence(k, 0);//返回的答案数组 
        int start = max(0, k - n), end = min(k, m);//需要得到 x+y=k所有可能对应的值 这种写法还顺带判断了数组大小的极限情况 
        for (int i = start; i <= end; i++) {
            vector<int> subsequence1(MaxSubsequence(nums1, i));//得到最大子序列1 
            vector<int> subsequence2(MaxSubsequence(nums2, k - i));//得到最大子序列2 ,序列1,2,都需要满足当前指定的长度 
            vector<int> curMaxSubsequence(merge(subsequence1, subsequence2));
            if (compare(curMaxSubsequence, 0, maxSubsequence, 0) > 0) {
                maxSubsequence.swap(curMaxSubsequence);//选择较大的那个序列
            }
        }
        return maxSubsequence;
    }

    vector<int> MaxSubsequence(vector<int>& nums, int k) {//其实是个最大子序列的问题,返回一个栈底到栈顶的递减栈 
        int length = nums.size();//获得长度 
        vector<int> stack(k, 0);//创建0栈,长度为k 
        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--;//弹出元素,直到栈顶元素大于等于num 
            }
            if (top < k - 1) {//入栈 
                stack[++top] = num;
            } else {
                remain--;
            }
        }
        return stack;//返回栈 
    }

    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++];//subsequence1的当前元素比较大 
            } else {
                merged[i] = subsequence2[index2++];//subsequence1的当前元素比较小 
            }
        }
        return merged;
    }

    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等于x,返回负数,如果index2等于y,返回正数,等于0则说明长度相同 
    }
};


int main()
{
	vector<int> a={3, 4, 6, 5};
	vector<int> b={9, 1, 2, 5, 8, 3};
	int k = 5;
	Solution s;
	vector<int> ans = s.maxNumber(a,b,k);
	for(int num:ans)
	{
		cout<<num<<endl;
	}
}

最后,多提一句!

这种涉及到数组某种最大或者最小排序的题,一定要记得考虑栈的数据结构。因为它真的很好使!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值