Leetcode870:优势洗牌

77 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

题目描述

2022/10/08 每日一题 给定两个大小相等的数组 nums1 和 nums2nums1 相对于 nums2 的优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描述。

返回 nums1 的任意排列,使其相对于 nums2 的优势最大化。

算法思想

从题目可以联想到“田忌赛马”故事,nums1为我们的马,nums2为对手的马,我们的目标是使我们赢的局数最多。
首先将两个“马群”按照“下等马、中等马、上等马”升序排序,然后分别取两排序数组的首元素比大小。此时可能出现的情况有两种:
1、nums1>nums2,这是我们期望的情况:
a.此时直接使这两个元素配对,增加一点优势,并将他们一起移除数组
b.如果nums1不与当前的nums2配对,那么nums2的后续元素都比当前元素大,造成当前nums1本来能增加优势现在反而不一定能增加优势
2、nums1<nums2,此时nums2是待配对数组中“下等马”(最小的元素),之后的元素肯定都比当前nums1大,此元素不能增加优势。由于两数组中的元素都必须被配对,因此就将此“下等马”与nums2中的“上等马”配对,即将当前nums1nums2中的最大元素配对,以用下等马来消耗对手的上等马。

代码实现

1、使用数组存储排序后元素的下标,而非元素本身,以方便后续将结果存到与nums2对应的下标中
2、双指针:left指向nums2数组中左侧第一个未被配对的元素,right指向nums2数组中右侧第一个未被配对的元素。情况1的时候在配对最左侧元素,情况2的时候配对最右侧元素。
3、比较器:
a.return 正数:交换 return 负数或0:不交换
b.(i, j) -> nums1[i] - nums1[j]:序列相同升序。
nums1[i] > nums1[j],即原本为降序,此时返回正数进行交换形成升序;
nums1[i] < nums1[j],即原本为升序,此时返回负数不进行交换,仍为升序。
同理可得:(i, j) -> nums1[i] - nums1[j]:序列不同降序。

public int[] advantageCount(int[] nums1, int[] nums2) {
        int n = nums1.length;
        // 数组存储排序后元素的下标
        Integer[] id1 = new Integer[n];
        Integer[] id2 = new Integer[n];
        for(int i = 0; i < n; i++){
            id1[i] = id2[i] = i;
        }
        
        // 按nums大小对其下标数组升序排序,即数值越大的元素的下标越排在前面
        // 比较器:相同升序
        Arrays.sort(id1, (i, j) -> nums1[i] - nums1[j]);
        Arrays.sort(id2, (i, j) -> nums2[i] - nums2[j]);

        int[] ans = new int[n];
        // 双指针
        for(int i = 0, left = 0, right = n - 1; i < n; i++){
            if(nums1[id1[i]] > nums2[id2[left]]){
                ans[id2[left]] = nums1[id1[i]];
                left++;
            }else{
                ans[id2[right]] = nums1[id1[i]];
                right--;
            }
        }
        return ans;
}