LeetCode探索(144):870-优势洗牌(田忌赛马问题)

304 阅读2分钟

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

题目

给定两个大小相等的数组 nums1nums2nums1 相对于 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 <= 10^5
  • nums2.length == nums1.length
  • 0 <= nums1[i], nums2[i] <= 10^9

思考

本题难度中等。

首先是读懂题意。给定两个大小相等的数组 nums1nums2nums1 相对于 nums2优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描述。我们需要返回 nums1 的 任意排列,使其相对于 nums2 的优势最大化。

这是有意思的一道题,也可以看成是“田忌赛马”问题。

孙子曰:“今以君之下驷与彼上驷,取君上驷与彼中驷,取君中驷与彼下驷。”既驰三辈毕,而田忌一不胜而再胜,卒得王千金。

我们把 nums1 当成是田忌的马,nums2 当成是齐威王的马。我们将田忌的马按照从慢到快进行排序(也就是nums1从小到大进行排序)。

考虑田忌的下等马(nums1 的最小值):

  • 如果它比齐威王的下等马(nums2 的最小值)快,那么胜出;

  • 如果它比不过齐威王的下等马,则用该马与齐威王的上等马(nums2 的最大值)比。

去掉这两匹马,问题变成一个规模更小(n-1)的子问题。重复上述过程,即得到了所有马的对应关系。

代码实现时,由于 nums2 不能排序,我们可以创建一个下标数组 idxs,并对idxs 排序,即 idxs[0] 对应 nums2 中最小值的下标,idxs[1] 对应 nums2 中第二小值的下标,…。我们用双指针操作 idxs,从而知道每个下标所要对应的 nums1 的元素,也就找到了所要求的 nums1 的排列。

考虑到我们需要对数组nums1和数组idxs进行排序,因此时间复杂度是O(nlogn),空间复杂度是O(logn)

解答

方法一:贪心算法(田忌赛马)

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var advantageCount = function(nums1, nums2) {
  const n = nums1.length
  const idx = []
  for (let i = 0; i < n; ++i) {
    idx[i] = i
  }
  idx.sort((i, j) => nums2[i] - nums2[j])
  nums1.sort((i, j) => i - j)
  // console.log(nums2, idx, nums1)
  const ans = []
  let left = 0, right = n - 1
  for (let i = 0; i < n; ++i) {
    if (nums1[i] > nums2[idx[left]]) {
      ans[idx[left]] = nums1[i]
      ++left
    } else {
      ans[idx[right]] = nums1[i]
      --right
    }
  }
  return ans
}
// 执行用时:236 ms, 在所有 JavaScript 提交中击败了89.87%的用户
// 内存消耗:60.7 MB, 在所有 JavaScript 提交中击败了54.19%的用户
// 通过测试用例:67 / 67

复杂度分析:

  • 时间复杂度:O(nlogn),其中 n 为数组 nums1 和 nums2 的长度。
  • 空间复杂度:O(logn)。

参考