leetcode : 321. Create Maximum Number : 有点难度的贪心法

来源:互联网 发布:天津河东区淘宝街 编辑:程序博客网 时间:2024/05/16 17:22

321. Create Maximum Number

My Submissions
Total Accepted: 4587 Total Submissions: 22666 Difficulty: Hard

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.

Example 1:

nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
return [9, 8, 6, 5, 3]

Example 2:

nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
return [6, 7, 6, 0, 4]

Example 3:

nums1 = [3, 9]
nums2 = [8, 9]
k = 3
return [9, 8, 9]


开始看题目,没有想法,肯定不会是暴力搜索所有可能的拼接。后来想到:如果能分别从两个数组中选出候选子集,那么就应该能够根据类似归并的样子,将其拼接成最大数组。但是,如何从一个数组中选出候选子集。瞄了一眼提示才知道:长度为subK候选子集就是在子数组中最大的子数组。
1.推论:假设数组x和数组y分别选出长度是r的subX和长度是(k-r)的subY组成的数组Z最大,并且保证元素保持原有的顺序(元素o在subX中哪些元素前后,在Z中仍然保持这样的顺序)。那么subX和subY分别是X,Y的固定长度的最大数组
原因很简单:贪心法证明,假设subX是拼接成最大数组Z的子数组,如果subX不是X的最大数组,那么存在subXX是X数组在固定长度的最大数组。subX和subY可以拼接成Z1,那么由subXX的数组元素来替换subX拼接成Z2,很明显Z2一定大于Z1。因此subX不是拼接成最大数组Z的子数组,与假设矛盾。因此,subX是X的最大数组。

既然知道subX是X中长度为r的最大数组,找出这个数组只要在元素的可能范围内选取最大元素,即可贪心法找出。

问题的下一步是:如何将两个最大数组合并成一个最大数组。开始的想法是:反正所有元素都要使用,使用类似归并法的方法,将大值尽可能排在前面即可。整体思路是对的,但是有个致命的bug:当遇到相等的比较,那么使用哪一个?一开始没有意识到这个问题,随便是用哪一个,结果wrong了好久。最后终于意识到,选择哪一个结果不一样的:
例如:
案例1:
5,3
5,8
案例2:
5,5,8
5,8
案例3:
5
5,8
案例4:
5,5,3,7,9
5,5,3,7,8
等考虑完成这些情况后,调试AC
public class Solution {    public int[] submax(int[]numx,int subk){if(subk>numx.length||subk==0)return null;if(subk==numx.length)return numx;int[] subre=new int[subk];int left=0,right=numx.length-subk;for(int i=0;i<subk;i++){int max=Integer.MIN_VALUE;int maxIndex=-1;for(int j=left;j<=right;j++){if(max<numx[j]){max=numx[j];maxIndex=j;}}if(maxIndex!=-1){subre[i]=max;left=maxIndex+1;right++;}}return subre;}public int[] merge(int[] sub1,int[] sub2){if(sub1==null||sub2==null)return sub1!=null?sub1:sub2;int len1=sub1.length;int len2=sub2.length;int len=len1+len2;int[] mergeList=new int[len];int i=0,j=0;int index=0;while(i<len1&&j<len2){if(sub1[i]<sub2[j])    mergeList[index++]=sub2[j++];else {if(sub1[i]>sub2[j])mergeList[index++]=sub1[i++];else {int equalNum;boolean find=false;for(equalNum=1;i+equalNum<len1&&j+equalNum<len2;equalNum++){if(sub1[i+equalNum]!=sub2[j+equalNum]){find=true;break;}}if(!find&&(i+equalNum<len1||j+equalNum<len2))find=true;if(find){int whichone=0;if(i+equalNum>=len1||j+equalNum>=len2)whichone=i+equalNum<len1?1:2;else {whichone=sub1[i+equalNum]<sub2[j+equalNum]?2:1;}mergeList[index++]=whichone==1?sub1[i++]:sub2[j++];}else {mergeList[index++]=sub1[i++];}}}}while(i<len1)mergeList[index++]=sub1[i++];while(j<len2)mergeList[index++]=sub2[j++];return mergeList;}public int comparIntList(int[] first,int[] second){if(first.length!=second.length)return first.length-second.length;for(int i=0;i<first.length;i++)if(first[i]!=second[i]){return first[i]-second[i];}return 0;}public int[] maxNumber(int[] nums1, int[] nums2, int k) {int[] re=new int[k];int[] subre1,subre2;int len1=nums1.length;int len2=nums2.length;if(k>len1+len2)return null;int minNum1=k-len2<0?0:k-len2;int maxNum1=len1<k?len1:k;for(int i=minNum1;i<=maxNum1;i++){subre1=submax(nums1, i);subre2=submax(nums2, k-i);int[] temp=merge(subre1,subre2);if(i==minNum1||comparIntList(temp,re)>0){System.arraycopy(temp, 0, re, 0, k);}}return re;}}

实际上,针对相等比较的情况,需要比较的是剩余数组的大小,然后对大的数组,压一个元素进结果数据。隔壁家的代码就是精炼
public int[] maxNumber(int[] nums1, int[] nums2, int k) {    int n = nums1.length;    int m = nums2.length;    int[] ans = new int[k];    for (int i = Math.max(0, k - m); i <= k && i <= n; ++i) {        int[] candidate = merge(maxArray(nums1, i), maxArray(nums2, k - i), k);        if (greater(candidate, 0, ans, 0)) ans = candidate;    }    return ans;}private int[] merge(int[] nums1, int[] nums2, int k) {    int[] ans = new int[k];    for (int i = 0, j = 0, r = 0; r < k; ++r)        ans[r] = greater(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];    return ans;}public boolean greater(int[] nums1, int i, int[] nums2, int j) {    while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) {        i++;        j++;    }    return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]);}public int[] maxArray(int[] nums, int k) {    int n = nums.length;    int[] ans = new int[k];    for (int i = 0, j = 0; i < n; ++i) {        while (n - i + j > k && j > 0 && ans[j - 1] < nums[i]) j--;        if (j < k) ans[j++] = nums[i];    }    return ans;}




0 0
原创粉丝点击