专栏 | 九章算法
网址 | http://www.jiuzhang.com
创建最大数
题目描述
给出两个长度分别是m和n的数组来表示两个大整数,数组的每个元素都是数字0-9。从这两个数组当中选出k个数字来创建一个最大数,其中k满足k <= m + n。选出来的数字在创建的最大数里面的位置必须和在原数组内的相对位置一致。返回k个数的数组。你应该尽可能的去优化算法的时间复杂度和空间复杂度。
样例
给出 nums1 = [3, 4, 6, 5], nums2 = [9, 1, 2, 5, 8, 3], k = 5返回 [9, 8, 6, 5, 3]给出 nums1 = [6, 7], nums2 = [6, 0, 4], k = 5返回 [6, 7, 6, 0, 4]给出 nums1 = [3, 9], nums2 = [8, 9], k = 3返回 [9, 8, 9]复制代码
算法分析
本题要求从两个数组中选择k个数字来创建最大数,同时要求保持数字在原数组中的相对位置。我们容易想到的是可以分别在两个 数组中选择x和k-x个数字,再通过将选择出的数字合并成一个最大数的算法。因此,我们首先来解决问题1。
问题一
如何从一个数组中选择k个数,组一个最大数
对于这个问题,容易想到如果越大的数字在结果中的位置越靠前(即在高位上),那么最终得到的结果越大。所以我们应该尽量将大的数字放置在结果数组的头部。但是有一个条件需要考虑,即剩余未选择的数的个数能否保证有合法解。因此,这个问题可以借助栈和贪心算法来实现,具体步骤如下:
1.如果栈不为空,同时栈顶元素比当前元素小,同时,剩余的元素大于等于k个,那么弹出栈顶元素;
直到 栈空或者 栈顶元素大于当前元素 或者 剩余元素不足k个;
2.同时如果栈内元素小于k,则将当前元素压入栈中;
我们可以利用数组来模拟栈,同时设置变量drop记录已经出栈的元素个数,具体代码如下:
//数组中选k个数组成的最大数 vector maxVec(vector & num, int k){ int len = num.size(); int drop = len - k; vector res; for(int i = 0; i < len ; i++){ while(res.size() > 0 && drop > 0 && res[res.size() - 1] < num[i]){ res.erase(res.end() - 1); drop --; } res.push_back(num[i]); } res.resize(k); return res; }复制代码
问题2
假设两个数组分别有m和n个元素,如何组成一个k = m+n 的最大数?
这个问题想法比较直观,在选取数字的时候,我们只需要选择尽量大的数字在前面即可,因此我们会选择两个数组头部元素中比较大的一个。但是有一种情况需要注意,当两个数组头部的元素一样大的时候,我们需要一直比较,直到选择出比较大的数组,选择该数组的头部元素。
1.比较两个数组大小的代码如下:
int findmax(vector &num1, vector & num2){ int len1 = num1.size(); int len2 = num2.size(); int i = 0,j = 0; while(i < len1 && j < len2 &&num1[i] == num2[j]){ i++;j++; } if(i < len1 && j < len2) return num1[i] < num2[j]; else if(i == len1) return 1; else return 0; }复制代码
2.两个长度分别为m和 n的数组组成k=m+n的最大数的代码如下:
// 两个数组组成的最大数 vector maxRes(vector & nums1, vector & nums2){ int len1 = nums1.size(); int len2 = nums2.size(); vector res; while(nums1.size()||nums2.size()){ int tmp = findmax(nums1 ,nums2); if(tmp) { res.push_back(nums2[0]); nums2.erase(nums2.begin()); }else{ res.push_back(nums1[0]); nums1.erase(nums1.begin()); } } return res; }复制代码
问题3
原问题:如果两个数组长度分别为m和n,如何能够构建k <= m+n 的最大数?
我们把k个数分成两部分,i和 k-i ,分别从两个数组中选择i和k-i个数字构成的最大值,之后利用问题2中的方法将两个最大值合并,就得到了最终的结果。代码如下:
vector maxNumber(vector& nums1, vector& nums2, int k) { vector vec1; vector vec2; vector tmp; vector res(k,0); int m = nums1.size(); int n = nums2.size(); for(int i = max(0,k-n); i <= min(m,k) ; i++){ vec1 = maxVec(nums1, i); vec2 = maxVec(nums2, k-i); tmp = maxRes(v1,v2); if(findmax(res,tmp)) res = tmp; } return res; }
参考程序
class Solution {public: /** * @param nums1 an integer array of length m with digits 0-9 * @param nums2 an integer array of length n with digits 0-9 * @param k an integer and k <= m + n * @return an integer array */ //数组中选k个数组成的最大数 vector maxVec(vector & num, int k){ int len = num.size(); int drop = len - k; vector res; for(int i = 0; i < len ; i++){ while(res.size() > 0 && drop > 0 && res[res.size() - 1] < num[i]){ res.erase(res.end() - 1); drop --; } res.push_back(num[i]); } res.resize(k); return res; } int findmax(vector &num1, vector & num2){ int len1 = num1.size(); int len2 = num2.size(); int i = 0,j = 0; while(i < len1 && j < len2 &&num1[i] == num2[j]){ i++;j++; } if(i < len1 && j < len2) return num1[i] < num2[j]; else if(i == len1) return 1; else return 0; } vector maxNumber(vector & nums1, vector & nums2, int k) { vector vec1; vector vec2; vector tmp; vector res(k,0); int m = nums1.size(); int n = nums2.size(); for(int i = max(0,k-n); i <= min(m,k) ; i++){ vec1 = maxVec(nums1, i); vec2 = maxVec(nums2, k-i); tmp = maxRes(v1,v2); if(findmax(res,tmp)) res = tmp; } return res; }};复制代码
这样的话,我们需要k次分割,每次分割构建最大值的时间复杂度是O(mn)的(问题2中比较大小的复杂度)。这样的话,总的时间复杂度是O(kmn)的。
面试官角度分析
这道题目属于medium难度的一类,主要考察面试者的分治思想。面试者给出上述算法即可达到hire的程度