题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如,输入数组 {3, 32, 321},则打印出这 3 个数字能排出的最小数字 321323。
思路 1:全排列
最直接的思路是:求出数组中所有数字的全排列,然后找出最小的数字。这样的做法着实简单,但是时间复杂度为 O(n!),较高,我们需要找到更快的算法。
思路 2:大小比较
我们可以尝试举例来寻找思路,{3, 32, 321} 可以排列出 332321, 332132, 323321, 323213, 321332 和 321323。将结果从小到大排序是 321323, 321332, 323213, 323321, 332132, 332321。这里我们可以发现把 321 排在前面会使得结果变小,把 3 排在前面会使得结果变大。
我们可以定义一组比较规则,来比较数组中数字的“大小”。规则如下:对于 m 和 n,如果 mn < nm,那么 m < n;如果 mn = nm,那么 m = n;如果 mn > nm,那么 m > n。使用这组规则可以将数组中的数字进行从小到大排序,然后按照顺序拼接成一个数字,这个数字就是我们要找的结果。
这里存在一个隐晦的问题,在比较的时候,两个 int 数字拼接起来是一个大数问题。因此,我们可以使用字符串将这两个数字拼接,由于 mn 和 nm 的位数相同,我们便可以直接使用字符串比较大小。
我们总结一下这个思路:首先定义一组规则,来比较数组中数字的“大小”,然后使用这组规则可以将数组中的数字进行从小到大排序,然后按照顺序拼接成数字。
由于排序操作需要 $O(n\,log\,n)$ 的时间复杂度,其他的操作均小于它,因此这个算法的时间复杂度是 $O(n\,log\,n)$。
这个算法涉及到自己定义的比较规则,往往需要证明这个规则的有效性与这个算法的有效性。换句话说,也就是需要证明规则是自反的、对称的和传递的;该算法确实能够找到最小的数字。这个部分比较长,就省略拉。
实现
1 | import java.util.*; |