题目
给你一个整数数组 nums ,请你将数组按照每个值的频率 升序 排序。如果有多个值的频率相同,请你按照数值本身将它们 降序 排序。
示例 1:
输入:nums = [1,1,2,2,2,3]
输出:[3,1,1,2,2,2]
解释:‘3’ 频率为 1,‘1’ 频率为 2,‘2’ 频率为 3 。
示例 2:
输入:nums = [2,3,1,3,2]
输出:[1,3,3,2,2]
解释:‘2’ 和 ‘3’ 频率都为 2 ,所以它们之间按照数值本身降序排序。
示例 3:
输入:nums = [-1,1,-6,4,5,-6,1,4,1]
输出:[5,-1,4,4,-6,-6,1,1,1]
范围:1 <= nums.length <= 100
-100 <= nums[i] <= 100
题目分析
题目要求:求出数组内每个元素出现的频率,然后按照频率来升序排序数组,如果存在频率相同的数,进行降序排列。
确保读懂题。然后慢慢分析。
根据数据的范围,我可以新设一个count数组,用于统计每个整数出现的频率,但是这个会存在一个问题。我们发现数组元素的范围在-100~100之间,也就是说存在负整数,那么我新设的数组count,就要避免这个问题。
然后我们再读题,一想到排序,我们应该想到学过的排序算法:冒泡、快速、选择、归并等等,我们需要思考能不能用我们学过的、我们掌握的技巧去解题。
法一:新设数组+循环+交换
我们先来看看我第一次写的代码:
public int[] frequencySort(int[] nums) {
//计算每个元素出现的频率
int[] count = new int[400];
for (int i : nums) {
count[i + 200]++;
}
//进行排序
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (count[nums[i] + 200] > count[nums[j] + 200]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
if (count[nums[i] + 200] == count[nums[j] + 200] && nums[j] > nums[i]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums;
}
设立了一个大小为400的数组,统计nums数组中元素频率的时候,我们让 i+200,相当于让该元素在count数组里的位置右移200位,那么我们在比较的时候,也要+200。
双重循环里面的第一个if,用于交换:将出现频率高的整数交换到后面。第二个if,用于比较相等频率的元素,谁更大,大的放前面,小的放后面。
最后返回nums即可。
这样做,是在原数组的基础上进行交换。重点要学会的是:count[nums[i]]。count统计的 是nums数组中每个元素出现的频率,比较频率的时候,就可以count[nums[i]],去看nums[i] 这个数的频率了。
<---------------------------------------------------------------------------------------------------------------------->
当然也可以设置新数组的大小比数据最大限度 大1。
public int[] frequencySort(int[] nums) {
// 计算每个元素出现的频率
int[] count = new int[201];
for (int i : nums) {
count[i + 100]++;
}
// 进行排序
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (count[nums[i] + 100] > count[nums[j] + 100]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
if (count[nums[i] + 100] == count[nums[j] + 100] && nums[j] > nums[i]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums;
}
法二:HashMap+不用额外考虑负数的情况
与法一思想一致,就是使用的是HashMap,不用移动负数
public int[] frequencySort2(int[] nums) {
// 计算每个元素出现的频率
HashMap<Integer, Integer> map = new HashMap<>();
for (int i : nums)
map.put(i, map.getOrDefault(i, 0) + 1);
// 进行排序
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (map.get(nums[i]) > map.get(nums[j])) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
if (map.get(nums[i]) == map.get(nums[j]) && nums[j] > nums[i]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums;
}
法三:HashMap+Arrays.sort()自定义排序
sort(T[] a, Comparator<? super T> c) 根据指定的比较器所指定的顺序对指定的对象数组进行排序。
有个麻烦的地方在于:我们需要先将int型nums转为一个Integer型的数组,然后对Integer型数组进行排序之和,又要将其转为Int型。为什么呢???因为比较的是对象!!!对象!!!写了半天总是错的,原来要转型。
public int[] frequencySort3(int[] nums) {
Integer[] array = new Integer[nums.length];
for (int i = 0; i < array.length; i++) {
array[i] = (Integer) nums[i];
}
System.out.println(Arrays.toString(array));
// 计算每个元素出现的频率
HashMap<Integer, Integer> map = new HashMap<>();
for (int i : nums)
map.put(i, map.getOrDefault(i, 0) + 1);
// 进行排序
//如果频率相同,就按照数值降序;如果频率不同,就按照频率大小升序排列
Arrays.sort(array, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
if (map.get(o1).equals(map.get(o2))) {
return o2 - o1;
}
return (int) map.get(o1) - map.get(o2);
}
});
//最后还要转回Int型
for (int i = 0; i < array.length; i++) {
nums[i] = (int) array[i];
}
return nums;
}
之前写的时候,没有进行转换,所以一直报错,一直用不起,后来知道要转型。
借鉴下面的讲解:
总之,虽然代码还是比较长,但是学到了Arrays.sort()自定义排序,还是可以了
法四:Lambda表达式
λ表达式由三部分组成:参数列表,箭头(->),以及一个表达式或语句块。
参考
Lambda表达式入门
打扰了,看不懂,第一次接触,以后来日方长呀Lambda!
// Lambda
public int[] frequencySort4(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
List<Integer> list = new ArrayList<Integer>();
for (int i : nums) {
list.add(i);
map.put(i, map.getOrDefault(i, 0) + 1);
}
list.sort((a, b) -> {
int temp1 = map.get(a);
int temp2 = map.get(b);
if (temp1 == temp2) {
return b - a;
}
return temp1 - temp2;
});
return list.stream().mapToInt(Integer::intValue).toArray();
}
做题反思
个人觉得,法一最好!法一简单易懂,就是自己想的话,可能就想不出来,会绕半天。
我也是不太熟悉,但是经过这道题,看到很多知识点,见多识广了!
继续加油
end.