870.优势洗牌「贪心」

思路

贪心

类似于田忌赛马

对于任意的nums2[i],找到nums1中大于nums2[i]的元素中的最小值,这样可以使得对于后续的元素,能够得到的优势更大。

暴力做法的时间复杂度为O(n^2),显然会超时。


容易想到的一种方法是将nums1进行排序,然后对于每个nums2[i]num1中进行二分查找,找到第一个大于它的数,

若不存在nums[j] > nums2[i],则下标i位置上应放上nums1余下元素中最小的一个(好像不太对?),可以使得优势最大化。

此时存在一个问题,就是nums1中每次会被取出一个元素,然后该元素应该从数组中移除,而移除的方法时间复杂度又是O(n)的,导致最终的时间复杂度是O(n^2)。


因此二分查找的思路也不太可行。从田忌赛马的思想来考虑:对于nums1中最小的元素,

  • 如果比nums2中最小的元素大,则可以得到一个优势位置
  • 如果没有nums2中最小的元素大,则与nums2中最大的元素抵掉。

此时nums1nums2中各剩下n-1个元素,变成了一个相同的子问题,继续进行上述判断并填放元素即可。

从上面的考虑可以看出,需要将nums1nums2都进行排序,而nums2排序后还需要保留其原始下标位置。

经过上述考虑后想到的代码如下:

class Solution {
    public int[] advantageCount(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int[] ans = new int[n];
        //visit[i]=true表示nums1[i]已经被判断过
        boolean[] visit = new boolean[n]; 
        
        Arrays.sort(nums1);
        ArrayList<int[]> list = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            list.add(new int[]{nums2[i], i});
        }
        list.sort(Comparator.comparingInt(a -> a[0]));

        int i, j = 0;
        for (i = 0; i < n; i++) {
            int[] pair = list.get(i);
            int val = pair[0];
            int idx = pair[1];
            
            while(j<n && (visit[j] || nums1[j] <= val)){
                j++;
            }
            if (j<n){
                ans[idx] = nums1[j];   
                visit[j] = true;
            } else {
                break;
            }
        }
        
        //此时在nums1中已经找不到大于list.get(i)[0]的数了,j=n
        //因此 nums1[j]&&!visit[j] 倒序加入ans中
        j = n-1;
        while(i < n){
            int[] pair = list.get(i);
            int val = pair[0];
            int idx = pair[1];
            
            while(j >= 0 && visit[j]){
                j--;
            }
            ans[idx] = nums1[j--];
            i++;
        }
        return ans;
    }
}

没有想到的地方:两个方向上可以同时进行,这里一个循环只做了一个方向上的填充。

更优雅的代码

参考灵佬解析

class Solution {
    public int[] advantageCount(int[] nums1, int[] nums2) {
        int n = nums1.length;
        Arrays.sort(nums1);
        Integer[] ids = new Integer[n];
        int[] ans = new int[n];

        for (int i = 0; i < n; i++) {
            ids[i] = i;
        }
        Arrays.sort(ids, Comparator.comparingInt(i -> nums2[i]));

        //双指针,指向排序后nums2的左右边界
        int l = 0, r = n-1;  
        for (int i = 0; i < n; i++) {
            if (nums1[i] > nums2[ids[l]]){
                ans[ids[l]] = nums1[i];
                l++;
            } else {
                ans[ids[r]] = nums1[i];
                r--;
            }
        }
        
        return ans;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值