本周的题目是产生最大的数字,题目描述如下:
Given two arrays of length m and n with digits 0-9 representing two numbers. Create the maximum number of length k <= m + n from digits of the two. The relative order of the digits from the same array must be preserved. Return an array of the k digits. You should try to optimize your time and space complexity.
简单来说,就是给出数组nums1和nums2,程度分别是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]
根据题目的描述,首先想到的思路是,要求二合一的结果,那可以先一分为二来考虑。为了从两个数组中找到k个最大数字,需要从数组1中找到i个最大数字,从数组2中找到k-i个数字,将它们合并在一起。比较所有可能的i得到的合并数组,就能得到两个数组的最大k个数。
其次,根据例子和问题描述得到的结论有:得到的结果不是升序或者降序,而是结合两个要求来排的,一是同一数组中的数字按照已有的顺序,二是遇到相同的数字时要按照字典序来排的。
1)我们看例子1中nums2取的是[9, 8, 3] 而不是[9, 5, 8],虽然在数字上看后者比较大,但是从字典序来说取的是前者。因此,选取最大数字的过程,除了要注意i个数字是相位位置不变,还要注意是字典序最大的;
2)再看例子2,该例子中k=5,那必然nums1和nums2的全部数字都要取到。两个数组的第一个数字都是6,取哪个先?应该是nums1的6,因为如果取的是nums2的6,那么得到的数组就是[6, 6, 7, 0, 4],显然是小于答案的。因此,在合并的过程中,在遇到两个数组有在某一位置有相等元素时,就要考虑两个数组的字典序问题。
根据上述基本思想,该题目的关键过程包括:
1)找出数组的i个最大数字MaxNumber(vector<int> nums, int i);
思路是:可以使用栈来实现,首先将第一个数字压入栈中,接下循环,如果下一个数字比栈顶大,那么将栈顶移出,新的数字入栈,记录移出栈的数字个数;如果下一个数字比栈顶小,新数字入栈。当移出栈的数字个数到达nums.size() – i时,说明剩下的数字是i个,那么剩下的数字全部都入栈就好。
实际上,用vector也可以实现这个过程。
2)合并两个数组merge(vector<int> nums1, vector<int> nums2);
思路是:循环直到nums1为空或者nums2为空,在循环中比较nums1和nums2(整个vector比较),如果nums1大,则将nums1的第一个数字加入结果数组中,并将nums1的第一个数字erase;同理,如果num2大,则将nums2的第一个数字加入结果数组中,并将nums2的第一个数字erase。出循环后,将nums1或者nums2数组剩下数加入结果数组中。
代码如下所示:
vector<int> maxNumber(vector<int> nums, int k)
{
vector<int> res;
//rest用来记录需要移出的数字个数
int rest = nums.size() - k;
for(int i = 0; i < nums.size(); i ++){
while(rest > 0 && res.size() > 0 && res.back() < nums[i]){
res.pop_back();
rest --;
}
res.push_back(nums[i]);
}
//使用该函数,将结果数组设置在k个元素
res.resize(k);
return res;
}
vector<int> merge(vector<int> nums1, vector<int> nums2){
vector<int> res;
while(nums1.size() > 0 && nums2.size() > 0)
{
//合并时,比较两个vector数组
if(nums1 > nums2){
res.push_back(nums1[0]);
nums1.erase(nums1.begin());
}
else{
res.push_back(nums2[0]);
nums2.erase(nums2.begin());
}
}
if(nums1.empty()){
while(!nums2.empty()){
res.push_back(nums2[0]);
nums2.erase(nums2.begin());
}
}
if(nums2.empty()){
while(!nums1.empty()){
res.push_back(nums1[0]);
nums1.erase(nums1.begin());
}
}
//cout << endl;
return res;
}
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
int size1 = nums1.size();
int size2 = nums2.size();
vector<int> res;
for(int i = max(k-size2, 0); i <= min(k, size1); i ++){
vector<int> vec1 = maxNumber(nums1, i);
vector<int> vec2 = maxNumber(nums2, k-i);
//将合并到的数组与已有的数组比较,取最大的
res = max(res, merge(vec1, vec2));
}
return res;
}
在做本题时,一开始就没什么思路,后来想到把问题分解的方法,但是,也没有用什么技巧。遇到的问题是在merge函数中,一开始没有注意到数字相等时,应该考虑后面数字的字典序问题。所以出了一些错误。将vector数组进行直接比较,是可以得出字典序大小的。
考虑过上述代码计算复杂度较大,但是暂时没有想到什么优化的方案,之后有的话再更新~