Leetcode——321.Create Maximum Number


本周的题目是产生最大的数字,题目描述如下:

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数组进行直接比较,是可以得出字典序大小的。

考虑过上述代码计算复杂度较大,但是暂时没有想到什么优化的方案,之后有的话再更新~



点赞