题目
我的思路和代码 超时…
用一个res数组存储最后的结果,其中的数字就是num1数组中的数字
用一个vis数组标记已经使用了num1数组中的哪些数字
先对num1数组排序,然后遍历num2数组,如果num1当前位置 i 上的数字大于num2当前位置 j 上的数字,那么,res数组的 j 位置上存储 num1数组 i 位置上的数字,并且,vis[i] 赋值为1,表示num1数组第 i 个位置上的数字已经使用过了。
当把num2数组遍历一遍以后,如果num1中数组中还有数字未使用,那么就可以在res数组中任意找位置填入
代码超时…
import java.util.Arrays;
class Solution {
public int[] advantageCount(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
int len = nums1.length;
int[] res = new int[len];
Arrays.fill(res, -1);
int[] vis = new int[len];
Arrays.fill(vis, 0);
int index = 0;
for (int j = 0; j < len; j++) {
for (int i = 0; i < len; i++) {
if (vis[i] == 0 && nums1[i] > nums2[j]) {
vis[i] = 1;
res[j] = nums1[i];
break;
}
}
}
int i = 0;
while (Arrays.stream(vis).sum() != len) {
if (res[i] == -1) {
for (int j = 0; j < len; j++) {
if (vis[j] == 0) {
vis[j] = 1;
res[i] = nums1[j];
break;
}
}
}
i++;
}
return res;
}
}
官方思路 贪心算法 排序(lambda表达式)+双指针
思路
首先,分别将数组num1和num2排序,随后,只需要不断判断这两个数组的首个元素:
-
如果num1的首个元素大于num2的首个元素,那么就将它们在答案中对应起来,同时从数组中移除这两个元素【增加一点优势】
-
如果num1的首个元素小于等于num2的首个元素,那么移除num1的首个元素
当num1中没有元素时,遍历结束
这样做的正确性在于:
-
对于第一种情况,由于num1是有序的,那么num1的任意元素大于num2的首个元素
-
如果我们不与num2的首个元素配对,由于num2是有序地之后的元素会更大,这样并不划算
-
如果与num2的首个元素配对,使用num1的首个元素,可以使得剩余的元素尽可能的大,之后可以获得更多的【优势】
-
因为num1的首个元素大于num2的首个元素,num1是有序的,所以num1的任意元素都大于num2的首个元素,num1越往后,与num2的首个元素差别越大,所以,我们就选取与num2的首个元素最接近、又比num2首个元素大的那个元素,贪心思想
- 对于第二种情况,由于num2是有序的,那么num1中的首个元素小于等于num2中的任意元素,因此,num1的首个元素无法增加任何【优势】,可以直接移除
在本题中,由于num1中的每一个元素都要与num2中的元素配对,而我们是按照顺序考虑num2中的元素的。因此,在遍历结束后,num2中剩余的元素实际上是原先num2的一个后缀。因此当num1的首个元素无法配对时,我们给它配对num2的尾元素即可,并将该尾元素移除。
关于移除这一说法
在实际的代码编写中,我们无需真正的移除元素。
对于num1,可以使用一个循环依次遍历其中的每个元素
对于num2,可以使用双指针,left和right
如果num1中的首个元素可以增加优势,就配对left对应的元素并向右移动一个位置
如果无法配对,就配对right对应的元素,并向左移动一个位置
代码和结果
import java.util.Arrays;
import java.util.Comparator;
class Solution {
public int[] advantageCount(int[] nums1, int[] nums2) {
int n = nums1.length;
Integer[] idx1 = new Integer[n];
Integer[] idx2 = new Integer[n];
for (int i = 0; i < n; ++i) {
idx1[i] = i;
idx2[i] = i;
}
//记录数组是如何排序的
Arrays.sort(idx1, Comparator.comparingInt(i -> nums1[i]));
Arrays.sort(idx2, (i, j) -> nums2[i] - nums2[j]);
int[] ans = new int[n];
int left = 0, right = n - 1;
for (int i = 0; i < n; ++i) {
//idx1[i]作为索引,i从[0,n),num1[idx1[i]]是有序的
//同样,idx2[left]作为索引,left从0开始,num2[idx2[left]]也是有序的
//如果num1中的首个元素大于num2中的首个元素,那么就将它们在答案中对应起来
if (nums1[idx1[i]] > nums2[idx2[left]]) {
ans[idx2[left]] = nums1[idx1[i]];
++left;
} else {
//当num1中的首个元素无法配对时,把它配对给num2的尾元素即可
ans[idx2[right]] = nums1[idx1[i]];
--right;
}
}
return ans;
}
}
他人思路 TreeMap
TreeMap
-
TreeMap存储key-value对时,需要根据key-value进行排序,
-
TreeMap可以保证所有的key-value处于有序状态
-
TreeSet底层使用红黑树结构存储数据
-
TreeMap对key的排序
-
自然排序
- TreeMap的所有的key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则会抛出ClassCastException
-
定制排序
- 创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中所有key进行排序。此时,不需要Map的key实现Comparable接口
-
-
TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0
思路
-
若num1中有大于num2的数字,选择最小的大于num2的数字
-
若num1中没有大于num2的数字,则选择num1中最小的数字
采用TreeMap存放num1中的数字,然后遍历num2,找num2中每个元素匹配的num1中的元素
代码和结果
import java.util.TreeMap;
class Solution {
public int[] advantageCount(int[] nums1, int[] nums2) {
TreeMap<Integer, Integer> map = new TreeMap<>();
int len = nums1.length;
int[] res = new int[len];
for (int j : nums1) {
map.put(j, map.getOrDefault(j, 0) + 1);
}
Integer key;
//遍历nums2
for (int i = 0; i < len; i++) {
//找比nums2[i]大,但是与nums2[i]差距最小的num1中的数字
key = map.higherKey(nums2[i]);
//没找到
if (key == null) {
key = map.firstKey();
}
res[i] = key;
if (map.get(key) == 1) {
map.remove(key);
} else {
map.put(key, map.get(key) - 1);
}
}
return res;
}
}