1.二维数组排序
public static void main(String[] args) {
int arr[][]=new int[][]{{5,8,1},{3,9,4},{9,10,1}};
int sum[][]=new int[][]{{5,8,1},{3,9,4},{9,10,1}};
Arrays.sort(arr,(a,b)->b[1]-a[1]);
//通过对数组每一行的第二列元素大小进行降序排列,从而对二维数组进行重新排列(对比较器的重写)
for (int i = 0; i <arr.length ; i++) {
for (int j = 0; j <arr[0].length ; j++) {
System.out.println(arr[i][j]);
}
}
}
}
二维数组有大用处,可以同时存放数值,和数值映射的值,类似与哈希表,但搜索蛮方便(treemap)也能做到,它可以通过键值进行排序。建立一个n列2行的二维数组,第一行存放key值,第二行存放value,可以用归并排序(sort)进行排序,其中归并排序属于稳定排序,即值相同的元素的相对位置不发生变动。TreeMap也可以对键和值进行排序,查找更加方便,但缺点就是key不能冲突
如下题
public static int[] sortJumbled(int[] mapping, int[] nums) {
int arr[][]=new int[nums.length][2];//2行nums.length列
for (int i = 0; i <nums.length ; i++) {
String temp="";
String word=String.valueOf(nums[i]);
for (int j = 0; j <word.length() ; j++) {
//这里用的笨方法,用字符串拼接来做映射
String temp_1=String.valueOf(word.charAt(j));
int count=Integer.parseInt(temp_1);
temp=temp+String.valueOf(mapping[count]);
}
arr[i][0]=Integer.parseInt(temp);//第一行保存映射数值key
arr[i][1]=nums[i];//第二行保存value
}
Arrays.sort(arr,(a,b)->a[0]-b[0]);//排序,根据key来进行升序排序(sort默认的)
int arr_1[]=new int[nums.length];
for (int i = 0; i <arr_1.length ; i++) {
arr_1[i]=arr[i][1];
}
return arr_1;
}
比较器可以应用在二维数组排序上,同时也可以应用在优先队列上
PriorityQueue<Integer> q = new PriorityQueue<>((a,b)->b-a);
这里是将优先队列里面的元素进行降序排列,即往里面放值会进行自动的排列
比较器
重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2;
如果返回0,表示相等;
返回负整数,表示o1小于o2。
2.TreeMap
2.1TreeMap根据键排序
TreeMap相比普通Map,可以起到一个排序的作用,无论是对键或者值。相比用二维数组模拟,TreeMap的查找速度更快,下面是对TreeMap的应用。
比较器覆写,对KEY的绝对值进行升序排序
TreeMap<Integer, Integer> counter = new TreeMap<>((o1, o2) -> {
if (Math.abs(o1) == Math.abs(o2)) {
return o1 - o2;
}
return Math.abs(o1) - Math.abs(o2);
});
一下是对上例的测试
public class Solution {
public static void main(String[] args) {
// key-距离 0 的距离,value - 当前数字的出现次数,升序排列
TreeMap<Integer, Integer> counter = new TreeMap<>((o1, o2) -> {
if (Math.abs(o1) == Math.abs(o2)) {
return o1 - o2;
}
return Math.abs(o1) - Math.abs(o2);
});
counter.put(-1, 0);
counter.put(-2, 0);
counter.put(1, 0);
counter.put(2, 0);
counter.put(3, 0);
for (Map.Entry<Integer, Integer> entry : counter.entrySet()) {
System.out.println(entry.getKey());
}
}
}
控制台的输出结果
需要排序键的题目,TREEMAP当仁不让!排序速度快,查找速度远超模拟的二维数组。
因为负数和正数需要分类讨论,负数得按降序排列,正数得按升序排列,这里treemap就巧妙的利用比较器实现离0的距离实现升序排列,避免了分类讨论的繁杂情况
class Solution {
public boolean canReorderDoubled(int[] arr) {
// key-距离 0 的距离,value - 当前数字的出现次数,升序排列
TreeMap<Integer, Integer> counter = new TreeMap<>((o1, o2) -> {
//这里的if判断不能省,否则会缺失相反数中的一部分
if (Math.abs(o1) == Math.abs(o2)) {
return o1 - o2;
}
return Math.abs(o1) - Math.abs(o2);
});
// 统计 arr 数组中的每个数字的出现次数,若之前不在counter中置1,已有则置oldCnt+ newCnt,速度要比counter.put(num,map.getOrDefault(num,0)+1)要快一些
for (int num : arr) {
counter.merge(num, 1, (oldCnt, newCnt) -> (oldCnt + newCnt));
}
// 如果 0 的次数不为偶数,则直接返回 false
final int ZERO = 0;
if ((counter.getOrDefault(ZERO, 0) & 1) == 1) return false;
//遍历counter的键值
for (Map.Entry<Integer, Integer> enrty : counter.entrySet()) {
// 当前数字及其出现次数
int num = enrty.getKey(), cnt = enrty.getValue();
// 当前数字已经消耗完了,就不再匹配 2*num
if (cnt == 0) continue;
// 如果 2*num 的次数小于 num 的次数,则无法构成题目要求的数对
int numDouble = 2 * num;
int cntDouble = counter.getOrDefault(numDouble, 0);
if (cntDouble < cnt) return false;
// 将 2*num 的次数抵消掉 num 的次数
counter.put(numDouble, cntDouble - cnt);
}
return true;
}
}
根据键排序,TreeMap可以直接实现,但根据值排序,并未找到TreeMap自带的功能
2.2根据值排序
可以将map放入List中,利用Collections.sort,覆写内置的比较器,实现对值的一个排序,下面是对值排序的一个测试
public static void main(String[] args) {
Map<String,Integer> map=new HashMap<>();
map.put("say",9);
map.put("hash",10);
map.put("you",3); System.out.println("排序前");
for (String s : map.keySet()) {
System.out.println(s+":"+map.get(s));
}
//通过ArrayList构造函数将map.entrySet()转换成list,即将map放到List中进行排序,这样就可对value值进行排序
List<Map.Entry<String,Integer>> list= new ArrayList<>(map.entrySet());
//通过比较器进行比较排序
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> entry1, Map.Entry<String, Integer> entry2) {
//返回值为1,代表前面>后面,-1相反,0表示相等
return entry2.getValue().compareTo(entry1.getValue());
}
});
//将排序后的结果打印出来
System.out.println("排序后");
for (Map.Entry<String, Integer> entry : list) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
}
控制台输出如下图所示,对值进行了一个降序排列
练习如下,见力扣的题目
这里便是对值进行一个降序排序,再将其遍历输出K个即可
代码如下所示
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer>map=new HashMap<>();
int res[]=new int[k];
for (int i = 0; i <nums.length ; i++) {
map.put(nums[i],map.getOrDefault(nums[i],1)+1);
}
//利用List对map的值进行排序
List<Map.Entry<Integer,Integer>>list=new ArrayList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> integerIntegerEntry, Map.Entry<Integer, Integer> t1) {
return t1.getValue()-integerIntegerEntry.getValue();
}
});
for (int i=0;i<k;i++) {
res[i]=list.get(i).getKey();
}
return res;
}
}
3.自定义数组比较器
重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2;
如果返回0,表示相等;
返回负整数,表示o1小于o2。
如题,需要自己重写比较器进行排序
Arrays.sort默认对数组元素进行升序排序
代码如下,有注释
public static String[] reorderLogFiles(String[] logs) {
Arrays.sort(logs, new Comparator<String>() {
@Override
public int compare(String log1, String log2) {
String id1, id2, c1, c2;
boolean isNum1 = false, isNum2 = false;
int n1 = log1.length(), n2 = log2.length(), t = 0;
while(t < n1 && log1.charAt(t) != ' ') {
t ++;
}
id1 = log1.substring(0, t);
c1 = log1.substring(t, n1);
//isNum1 表示它是数字型还是字母型,false是字母型,true为数字型
isNum1 = log1.charAt(t + 1) >= '0' && log1.charAt(t + 1) <= '9';
t = 0;
while(t < n2 && log2.charAt(t) != ' ') {
t ++;
}
id2 = log2.substring(0, t);
c2 = log2.substring(t, n2);
//isNum2 表示它是数字型还是字母型,false是字母型,true为数字型
isNum2 = log2.charAt(t + 1) >= '0' && log2.charAt(t + 1) <= '9';
if(isNum1 != isNum2) {
//数字型,排后面
if(isNum1) {
return 1;
}
//字母型,排前面
else {
return -1;
}
}
//如果都是数字型,保留原来的顺序
else if(isNum1) {
return 0;
}
//如果是字母型
else {
//内容相同时,比较标识符
if(c1.equals(c2)) {
return id1.compareTo(id2);
}
//内容不同时,比较内容
else {
return c1.compareTo(c2);
}
}
}
});
return logs;
}
4、字符串形式数字比较大小(重写比较器)
有的时候,比如说像“13231315464654”如此大的字符串,想转换成数字来比较大小,非常容易溢出,而逐个去比较字符大小呢,复杂度非常的高,很痛苦,可见力扣2343题。
这里我转换成数字进行比较器重写,不行,字符串长度太长,直接溢出,不能单纯的进行转换。
暴力解法(只是适用于字符串长度一致,不用考虑其他问题)
重写比较器,采用compareto这个API,二维数组,首位记录下标位置,下一位记录截取后的字符串,进行排序。
public static int[] smallestTrimmedNumbers(String[] nums, int[][] queries) {
int res[]=new int[queries.length];
for (int i = 0; i <queries.length ; i++) {
String arr[][]=new String[nums.length][2];
int cut=queries[i][1];
for (int j = 0; j <nums.length ; j++) {
String a=nums[j].substring(nums[j].length()-cut,nums[j].length());
String temp=a;
arr[j][0]=String.valueOf(j);
arr[j][1]=temp;
}
Arrays.sort(arr,(a,b)->(a[1].compareTo(b[1]));
int count=queries[i][0];
for (int j = 0; j <arr.length ; j++) {
count--;
//达到指定位置
if(count==0){
res[i]=Integer.valueOf(arr[j][0]);
break;
}
}
}
return res;
}
基数排序(待更新)