问题
刷到一个算法题需要拼接数字组成一个最大数,基本思想是高位比较,相等比较下一位,然后根据大小先拼接大的,再拼接小的,但是发现当存在多个数字高位相同时面临一个问题,比如示例2中,3,30,34,肯定先拼接34,但是30和3应该拼接为330,先是3再拼30,而不是303,因此转换思路,只需要将两个数线拼接起来,比较一下拼接后的结果哪个大,就把哪个数放前面,就不用逐位比较了,实现很容易如下:
class Solution {
public String largestNumber(int[] nums) {
String[] strs = new String[nums.length];
for(int i=0; i<nums.length; i++){
strs[i] = String.valueOf(nums[i]);
}
Arrays.sort(strs, (a,b)->(b+a).compareTo(a+b));
if(strs[0].equals("0")){
return "0";
}
StringBuilder sb = new StringBuilder();
for(String s : strs){
sb.append(s);
}
return sb.toString();
}
}
但重点不是这个,而是其中对Arrays的sort排序重写的comparator接口:
Arrays.sort(strs, (a,b)->(b+a).compareTo(a+b));
源码如下:
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
我们可以通过自定义Comparator接口进行我们所需要的排序。
实现Comparator接口
先以整数为例,最基础的方式如下:
通过定义一个类实现Comparator接口,重写里面的compare方法,返回o1-o2,o1-o2为升序,o2-o1为降序
class B implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
}
public class test {
public static void main(String[] args) {
Integer[] nums = {3,30,34,5,9};
System.out.println("排序前"+Arrays.toString(nums));
Arrays.sort(nums, new B());
System.out.println("排序后"+Arrays.toString(nums));
}
}
匿名内部类实现
进一步来写,可以将外部定义的类改为匿名内部类,如下所示:
public class test {
public static void main(String[] args) {
Integer[] nums = {3,30,34,5,9};
System.out.println("排序前"+Arrays.toString(nums));
Arrays.sort(nums, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
System.out.println("排序后"+Arrays.toString(nums));
}
}
lambda表达式
再进一步,可以更精简,追求更极致的简洁采用lambda表达式去写
public class test {
public static void main(String[] args) {
Integer[] nums = {3,30,34,5,9};
System.out.println("排序前"+Arrays.toString(nums));
Arrays.sort(nums, (Integer o1, Integer o2) -> {return o1-o2;});
System.out.println("排序后"+Arrays.toString(nums));
}
}
在lambda中甚至变量类型和return都能省略,因此可以写成这样:
Arrays.sort(nums, ( o1, o2) -> o1 - o2 );
String的自定义比较方法
回到最开始的问题,因为要比较的是String对象而不是Integer对象,String不像Integer能够直接用减号“-”去比较大小,而且直接给出比较结果如-1只是将元素进行了逆序排列而已,并没有达到题目要求:
public class test {
public static void main(String[] args) {
Integer[] nums = {3,30,34,5,9};
String[] strs = new String[nums.length];
for(int i=0; i<nums.length; i++){
strs[i] = String.valueOf(nums[i]);
}
System.out.println("排序前"+Arrays.toString(strs));
Arrays.sort(strs, ( o1, o2) -> -1 );
System.out.println("排序后"+Arrays.toString(strs));
}
}
因此针对String类型的数据要采取另一种方式,恰好String类提供了compareTo方法,compareTo方法有两个特点:
- 字符串与对象进行比较。
- 按字典顺序比较两个字符串。
因此它可以很好的根据ASCLL码比较由数字拼接在一起的字符串的大小。
public class test {
public static void main(String[] args) {
Integer[] nums = {3,30,34,5,9};
String[] strs = new String[nums.length];
for(int i=0; i<nums.length; i++){
strs[i] = String.valueOf(nums[i]);
}
System.out.println("排序前"+Arrays.toString(strs));
// Arrays.sort(strs, ( o1, o2) -> -1 );
Arrays.sort(strs, (a, b)->(b+a).compareTo(a+b));
System.out.println("排序后"+Arrays.toString(strs));
}
}
此时可以发现,排序后的String数组的元素按照拼接后能组成最大数的顺序进行了排序。