leetcode-优势洗牌

99 阅读1分钟

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

题目描述

给定两个大小相等的数组 nums1 和 nums2,nums1 相对于 nums2 的优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描述。

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

 

示例 1:

输入:nums1 = [2,7,11,15], nums2 = [1,10,4,11]
输出:[2,11,7,15]

示例 2:

输入:nums1 = [12,24,8,32], nums2 = [13,25,32,11]
输出:[24,32,8,12]

提示:

  • 1 <= nums1.length <= 105
  • nums2.length == nums1.length
  • 0 <= nums1[i], nums2[i] <= 109

思路

经典的贪心题目,贪心题目的最重要特征就是,局部的最优解是全局最优解的一部分,即全局最优解的任何一个小部分单独拿出来看,也是最优解。本题可以用田忌赛马的思路来解决。对于nums2中的每个数,我们最划算的做法,是从nums1剩下的数字中,选取大于nums2[i]的最小的数来跟它配对,这样既能使得这一次对位有优势,又让剩下的数字尽可能有优势。如果当前不存在任何大于nums2[i]的数字了,那么就选取当前剩下最小的数去对位,这样在这1位无法有优势的情况下,尽量让剩下的数字优势最大,类似于用下等马去对上对方的上等马。快速找到大于nums2[i]的最小的数,可以使用treeset来解决。

Java版本代码

class Solution {
    public int[] advantageCount(int[] nums1, int[] nums2) {
        int len = nums2.length;
        // 构建treeSet
        TreeSet<Integer> treeSet = new TreeSet<>();
        // 使用1个map来记录每个数字出现的次数
        Map<Integer, Integer> cntMap = new HashMap<>();
        for (int i : nums1) {
            cntMap.put(i, cntMap.getOrDefault(i, 0) + 1);
            if (cntMap.get(i) == 1) {
                treeSet.add(i);
            }
        }
        int[] ans = new int[len];
        int index = 0;
        for (int j : nums2) {
            Integer min = treeSet.ceiling(j+1);
            if (min == null) {
                min = treeSet.ceiling(-1);
            }
            ans[index++] = min;
            cntMap.put(min, cntMap.get(min) - 1);
            if (cntMap.get(min) == 0) {
                treeSet.remove(min);
            }
        }
        return ans;
    }
}