Medium
题目描述
给定两个大小相等的数组 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 <= 10^5nums2.length == nums1.length0 <= nums1[i], nums2[i] <= 10^9
解题思路
解题思路
这道题的核心思想是田忌赛马的策略,目标是让 nums1 中尽可能多的元素大于对应位置的 nums2 元素。
贪心策略:
- 对于
nums2中的每个元素,我们要从nums1中选择刚好能够"击败"它的最小元素 - 如果
nums1中没有元素能击败当前nums2的元素,就用nums1中最小的元素去"浪费"
算法步骤:
- 将
nums1排序,方便查找合适的元素 - 创建
nums2的索引数组并按值排序,保持原始位置信息 - 对排序后的
nums2,贪心地为每个元素分配nums1中的元素:- 如果能找到刚好大于它的最小元素,就选择该元素
- 否则选择
nums1中剩余的最小元素
- 根据原始索引还原结果数组
这种贪心策略确保了我们能获得最大的优势数量,因为我们总是用最小的"有效"元素去击败对手,为更大的对手保留更强的元素。
推荐解法: 排序 + 贪心分配
代码实现
class Solution {
public:
vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
vector<int> result(n);
// 对nums1排序
sort(nums1.begin(), nums1.end());
// 创建nums2的索引数组并排序
vector<pair<int, int>> nums2_indexed;
for (int i = 0; i < n; i++) {
nums2_indexed.push_back({nums2[i], i});
}
sort(nums2_indexed.begin(), nums2_indexed.end());
deque<int> available(nums1.begin(), nums1.end());
// 贪心分配
for (auto& p : nums2_indexed) {
int val = p.first;
int idx = p.second;
// 找到第一个大于val的元素
auto it = upper_bound(available.begin(), available.end(), val);
if (it != available.end()) {
result[idx] = *it;
available.erase(it);
} else {
// 没有更大的元素,用最小的
result[idx] = available.front();
available.pop_front();
}
}
return result;
}
};
class Solution:
def advantageCount(self, nums1: List[int], nums2: List[int]) -> List[int]:
n = len(nums1)
result = [0] * n
# 对nums1排序
nums1.sort()
# 创建nums2的索引数组并排序
nums2_indexed = [(val, idx) for idx, val in enumerate(nums2)]
nums2_indexed.sort()
available = nums1[:]
# 贪心分配
for val, idx in nums2_indexed:
# 找到第一个大于val的元素
pos = bisect_right(available, val)
if pos < len(available):
result[idx] = available.pop(pos)
else:
# 没有更大的元素,用最小的
result[idx] = available.pop(0)
return result
public class Solution {
public int[] AdvantageCount(int[] nums1, int[] nums2) {
int n = nums1.Length;
int[] result = new int[n];
// 对nums1排序
Array.Sort(nums1);
// 创建nums2的索引数组并排序
var nums2Indexed = new List<(int val, int idx)>();
for (int i = 0; i < n; i++) {
nums2Indexed.Add((nums2[i], i));
}
nums2Indexed.Sort((a, b) => a.val.CompareTo(b.val));
var available = new List<int>(nums1);
// 贪心分配
foreach (var (val, idx) in nums2Indexed) {
// 找到第一个大于val的元素
int pos = BinarySearch(available, val);
if (pos < available.Count) {
result[idx] = available[pos];
available.RemoveAt(pos);
} else {
// 没有更大的元素,用最小的
result[idx] = available[0];
available.RemoveAt(0);
}
}
return result;
}
private int BinarySearch(List<int> list, int target) {
int left = 0, right = list.Count;
while (left < right) {
int mid = left + (right - left) / 2;
if (list[mid] <= target) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
}
var advantageCount = function(nums1, nums2) {
const n = nums1.length;
const result = new Array(n);
// 对nums1排序
nums1.sort((a, b) => a - b);
// 创建nums2的索引数组并排序
const nums2Indexed = nums2.map((val, idx) => [val, idx]);
nums2Indexed.sort((a, b) => a[0] - b[0]);
const available = [...nums1];
// 贪心分配
for (const [val, idx] of nums2Indexed) {
// 找到第一个大于val的元素
let pos = binarySearch(available, val);
if (pos < available.length) {
result[idx] = available.splice(pos, 1)[0];
} else {
// 没有更大的元素,用最小的
result[idx] = available.shift();
}
}
return result;
function binarySearch(arr, target) {
let left = 0, right = arr.length;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] <= target) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
};
复杂度分析
| 复杂度 | 分析 |
|---|---|
| 时间复杂度 | O(n log n) |
| 空间复杂度 | O(n) |
时间复杂度分析:
- 排序
nums1和nums2索引数组:O(n log n) - 对每个元素进行二分查找和删除操作:O(n log n)
- 总时间复杂度:O(n log n)
空间复杂度分析:
- 存储排序后的索引数组和可用元素列表:O(n)
- 结果数组:O(n)
- 总空间复杂度:O(n)