题目地址:
https://www.lintcode.com/problem/largest-number/description
给定一个数组,要求将这些数全部拼接起来,问能拼出的最大数是多少。返回那个数的字符串形式即可。
首先,如果给定数字构成的两个字符串
和
,那么很容易判断
和
形成的数字哪个更大。我们对字符串规定一个序,如果上面两个形成的数字前者大,那么就规定
,如果后者大则不等号反向,如果一样大则规定两个字符串相等。
我们首先证明,这确实是个偏序关系:自反性和反对称性显然;对于传递性,如果
并且
,我们要证明
。以下所有式子,我们直接以
来表示字符串
代表的数字,令
代表
的长度。那么问题转化为:已知:
要证明:
由已知得:
所以
即
整理即得。
显然任意两个字符串都是可比较的,所以这个偏序关系是个全序关系。而题目给定的数字肯定只有有限个,所以可以证明这个关系也是个良序关系,所以就可以进行排序了。
接着只需证明排好序后一个接着一个拼接起来所得的数就是最大数。可以用反证法。假设 ,但存在一种拼接使得 ,但 的位置在 的后面的拼接方式能得到更大的数,由于显然数字 (因为让不等号左边的 不停向后挪动一直挪到尾部,再将 不停向前挪动一直挪到头部,就得到了不等号右边。然而,每次挪动都会使得数字变小或不变,绝不可能使得数字变大),这就矛盾了。证明完毕。
代码如下:
import java.util.Arrays;
public class Solution {
/**
* @param nums: A list of non negative integers
* @return: A string
*/
public String largestNumber(int[] nums) {
// write your code here
String[] numbers = new String[nums.length];
for (int i = 0; i < nums.length; i++) {
numbers[i] = String.valueOf(nums[i]);
}
Arrays.sort(numbers, (n1, n2) -> (n2 + n1).compareTo(n1 + n2));
StringBuilder sb = new StringBuilder();
for (String number : numbers) {
sb.append(number);
}
// 删掉开头的0
int i = 0;
while (i < sb.length() && sb.charAt(i) == '0') {
i++;
}
// 如果sb全是0,那显然返回0;否则返回前置0去掉后的子串
if (i == sb.length()) {
return "0";
} else {
return sb.substring(i);
}
}
}
时间复杂度 ,空间 。