题目
Given a list of non negative integers, arrange them such that they form the largest number.
Example 1:
Input: [10,2]
Output: “210”
Example 2:
Input: [3,30,34,5,9]
Output: “9534330”
Note: The result may be very large, so you need to return a string instead of an integer.
思路
考虑自定义一个comparator去比较大小,“大”的概念就是数字出现在答案的前面部分。
多写几个例子可以发现,例如12和1212,123和123123123这种循环的都是相等的,谁放前面都是同一个数,这就给了我们一个比较基本的想法,以长度短的为一个周期一位一位去比较。
例如34和345,把34看成34|5,5比3大所以345大,验证34534>34345,也可以看到大小差确实发生在5和3这位上;同理34和34|2,就是34大,验证34342>34234。
最复杂的是刚才比较下来是相等的,但其实不相等因为位数不同,举几个例子。
8308和830
8308 > 830 因为 8308830 > 8308308,我们发现产生差异的是8 > 3那位。
12和121
132 > 1321 因为1321321 > 1321132,产生差异的是3 > 1那位。
332和3323
3323 > 332,因为3323323 < 3323332,产生差异的是3 > 2那位。
334和3343
334 > 3343,因为3343343 > 3343334,产生差异的是4 > 3那位。
这时观察发现产生差异的是长度较短的数从左往右比较第i位和第i+1位的大小决定的,结合例子可以方便理解他们的大小和对应比较的位数的关系。
代码
class Solution {
public String largestNumber(int[] nums) {
Integer[] temp = Arrays.stream(nums)
.boxed()
.sorted(
(o1, o2) -> {
String s1 = Integer.toString(o1);
String s2 = Integer.toString(o2);
if (s1.length() == s2.length()) return Integer.compare(o1, o2);
if (s1.length() < s2.length()) {
for (int i = 0; i < s2.length(); i++) {
if (s1.charAt(i % s1.length()) < s2.charAt(i)) {
return -1;
} else if (s1.charAt(i % s1.length()) > s2.charAt(i)) {
return 1;
}
}
for (int i = 0; i < s1.length() - 1; i++) {
if (s1.charAt(i) > s1.charAt(i + 1)) {
return -1;
}
}
return 1;
} else {
for (int i = 0; i < s1.length(); i++) {
if (s1.charAt(i) < s2.charAt(i % s2.length())) {
return -1;
} else if (s1.charAt(i) > s2.charAt(i % s2.length())) {
return 1;
}
}
for (int i = 0; i < s2.length() - 1; i++) {
if (s2.charAt(i) > s2.charAt(i + 1)) {
return -1;
}
}
return -1;
}
})
.toArray(Integer[]::new);
StringBuilder sb = new StringBuilder();
int i = temp.length - 1;
while (i >= 0 && temp[i] == 0) {
i--;
}
for (; i >= 0; i--) {
sb.append(temp[i]);
}
return sb.toString().equals("") ? "0" : sb.toString();
}
}
优化
代码中用到了转成Integer类后使用数组自带排序的方法,显然直接自己写一个mergeSort会更好。
查看Sample答案,还有一个很好的想法是比较的时候并不适用String而是计算出两个数a,b的位数,然后a10进行b的位数次+b和b10进行a的位数次+a两个数直接比较,参考代码如下。
class Solution {
public String largestNumber(int[] nums) {
sort(nums, 0, nums.length);
// If the first element is 0, the result itself is 0
if (nums[0] == 0) return "0";
StringBuilder result = new StringBuilder();
for (int num : nums) result.append(num);
return result.toString();
}
// We can use string comparator
// (b + a).compareTo(a + b)
// However, concatenating strings and comparing takes more time
// So, use the above logic but using number
private boolean compare(int a, int b) {
long aLong = a * 10, bLong = b * 10;
int x = a, y = b;
while ((x /= 10) > 0) bLong *= 10;
while ((y /= 10) > 0) aLong *= 10;
return (aLong + b) > (bLong + a);
}
// The array is of primitive, so we cannot pass comparator
// This is a compact version of merge sort
private void sort(int[] nums, int l, int r) {
if (l >= r - 1) return ;
int m = l + (r - l) / 2;
sort(nums, l, m);
sort(nums, m, r);
merge(nums, l, m, r);
}
private void merge(int[] nums, int l, int m, int r) {
int[] aux = new int[r - l];
for (int i = l, j = m, k = 0; i < m || j < r;) {
if (i < m && (j == r || compare(nums[i], nums[j]))) aux[k++] = nums[i++];
else if (j < r) aux[k++] = nums[j++];
}
for (int i = 0; l < r; ++i) nums[l++] = aux[i];
}
}