LeetCode第 321 题:拼接最大数(C++)

321. 拼接最大数 - 力扣(LeetCode)

贪心算法,每次都尽可能地从可选范围内选择最大的数字。因为要保持元素的相对顺序,可选范围即为使得剩下的元素个数加上已选的元素依然有 k 个。

但是这题难在两个数组中有重复元素,比如示例 3 里面,两个数组里面都有元素 9 ,到底应该选择哪个 9 呢?只能根据后面的元素进行判断,这时可以写一个递归函数,将后面的元素作为新的数组进行选择,但是如果重复元素很多,递归栈就会很深。

那么换个思路,我们一共需要选择 k 个数,假设把这 k 个数分为两份,一份来自nums1,一份来自nums2,个数分别为p、q,那么显然 p + q = k。

而且也不难明白,来自nums1的p个元素组成的数字,应该是nums1中的任意p个元素能拼接出的最大值。同理nums2也是。也即是说,在两个数组中选择的数,不仅要保持顺序,还要尽可能的使它们拼接的数字最大。

那我们可以先写一个函数find_kMax,该函数的作用是在一个数组中选择k个元素,使得这k个元素拼接出来的数字最大,同时也要求保持元素在数组中的相对顺序。那就可以对原题中的k进行枚举,分别在两个数组上运行find_kMax,再将得到的结果拼接(merge),最后在所有拼接的结果中,选择最大的。

find_kMax的实现:类似LeetCode第 402 题:移掉K个数字(C++)_qq_32523711的博客-CSDN博客,可以转化话移除n-k个元素使得剩下的数字最小,当然不转化直接贪心寻找也是可以的。

class Solution {
public:
    //寻找k个数,使其拼接得到的值最大
    //同样需要保持相对顺序
    vector<int> find_kMax(vector<int> &num, int k){
        vector<int> res;
        int i = 0;
        while(k > 0){
            int idx = i;//比较对象初始化为查找区间第一个元素
            //尽量在剩余元素个数满足要求的情况下寻找最大数
            //有重复元素时取靠前的
            for(; i < num.size()-k+1; ++i){
                if(num[i] > num[idx])   idx = i;
            }
            res.push_back(num[idx]);
            i = idx+1;//下一次的遍历起点
            --k;
        }
        return res;
    }
    /*
    //转化为溢出 n - k个元素
    vector<int> find_kMax(vector<int>&nums, int k)
    {
        if(k==0) return{};
        vector<int>ans;
        int to_pop=nums.size()-k;
        for(const int num:nums)
        {
            while(!ans.empty()&&num>ans.back()&&to_pop-->0)
                ans.pop_back();
            ans.push_back(num);
        }
        ans.resize(k);
        return ans;
    }
    */

    vector<int> merge(vector<int> &nums1, vector<int> &nums2){//merge两个数组为一个
        vector<int> res;
        /*auto it1 = nums1.begin(), it2 = nums2.begin();
        while(it1 != nums1.end() && it2 != nums2.end()){
            if(*it1 < *it2) res.push_back(*it2++);
            else if(*it1 == *it2){
                x需要一个函数比较该元素后面的第一个不相等的元素才能知道字典序大小
            }
            else res.push_back(*it1++);
        }
        while(it1 != nums1.end())   res.push_back(*it1++);
        while(it2 != nums2.end())   res.push_back(*it2++);
        */
        
        for (auto iter1 = nums1.begin(), iter2 = nums2.begin(); iter1 != nums1.end() || iter2 != nums2.end(); ) {
                if (lexicographical_compare(iter1, nums1.end(), iter2, nums2.end()))    res.push_back(*iter2++);
                else  res.push_back(*iter1++);
        }
        return res;
    }
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<int> res;
        for(int i = 0; i <= k; ++i ){
            int j = k - i;
            if(i > nums1.size() || j > nums2.size())    continue;
            auto tmp1 = find_kMax(nums1, i);
            auto tmp2 = find_kMax(nums2, j);
            res = max(res, merge(tmp1, tmp2));
        }
        return res;
    }
};

这个题也确实是比较杂。不过上面的思路想来想去还是觉得不太好,因为枚举 k 还是比较耗时的。

还是一开始的思路比较靠谱,不想写了,贴一个:

c++贪心取k位数 - 拼接最大数 - 力扣(LeetCode)

猜你喜欢

转载自blog.csdn.net/qq_32523711/article/details/107891789