Q870 优势洗牌(田忌赛马)
题目描述
通过题目描述,我们很容易联想到田忌赛马的故事。
第一种解题思路(超时)
我在思考这道题的时候,想到直接将nums1数组升序排列得到clone,然后将clone数组从小到大和nums2中的数进行对,如果clone中有数大于nums2[i],就将这个数赋值给nums1[i],如果找遍clone都没有找到大于nums2[i]的数,则用list记录下下标index,然后用clone中剩余的数挨个赋值给nums[index];
public int[] advantageCount(int[] nums1, int[] nums2) {
ArrayList<Integer> clone = new ArrayList<>();
int n = nums1.length;
for (int i = 0; i < n; i++) {
clone.add(nums1[i]);
}
Collections.sort(clone);
List<Integer> index = new ArrayList<>();
L:
for (int i = 0; i < n; i++) {
for (int num : clone) {
if (num > nums2[i]) {
nums1[i] = num;
clone.remove(Integer.valueOf(num));
continue L;
}
}
index.add(i);
}
if (!clone.isEmpty()) {
int size = clone.size();
for (int i = 0; i < size; i++) {
nums1[index.get(i)] = clone.get(i);
}
}
return nums1;
}
超时原因(时间复杂度分析):
这种算法施加复杂度为O(n2),for循环嵌套for循环。(Collection.sort()的时间复杂度为nlogn)造成的原因是未对nums2进行排序,导致了对每一个nums2的数,都需要遍历nums1,虽然题目要求nums2的顺序不能改变,但是如果不对nums2进行排序的话,肯定会超时。
优先队列+双指针
我们使用优先队列加双指针的方式,先对nums1和nums2进行排序,这里我们使用在优先队列中使用int[]数组来记录nums2[i]的值和下标i,然后对nums2[i]进行降序排列。
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (b[1] - a[1]));
int n = nums2.length;
for (int i = 0; i < n; i++) {
pq.add(new int[]{i, nums2[i]});
}
Arrays.sort(nums1);
int l = 0, r = n - 1;
int[] res = new int[n];
while (!pq.isEmpty()) {
int[] pair = pq.poll();
if (nums1[r] > pair[1]) {
res[pair[0]] = nums1[r];
r--;
} else {
res[pair[0]] = nums1[l];
l++;
}
}
return res;
然后我们对nums1进行降序排列。
随后,我们用两个指针l,r分别指向nums1的开始和结尾。
这里必须要说明的是,我们用nums1的最大的数和nums2最大的数比较,会有两种情况:
1、如果nums1最大的数大于nums2最大的数(田忌的上等马跑过齐王的上等马):
则让index=pq.poll()[0];使res[index]=nums1[r];
2、如果nums1最大的数小于nums2最大的数:则找个下等马(nums1最小的数)去送,使res[index]=nums1[l](nums1[l]是nums1数组中最小的数);
时间复杂度分析:
很好分析;优先队列排序的时间复杂度为nlogn,算主体的复杂度为滑动窗口的复杂度O(n),所以总体来说复杂度为nlogn。