1、题目描述
【JZ32】输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
知识点:数组,全排列,排序,贪心
难度:☆
2、解题思路
2.1 暴力计算
核心思想:拼接出所有可能的组合,然后从这些组合中选择最小的。
于是任务变成两个:1、拼接所有可能的组合;2、找出最小值。
拼接所有可能的组合,即全排列。全排列可以使用递归的方法,数组的每一个元素都当放一次在首位,然后数组剩下的元素继续全排列。当数组只有一个元素,那么全排列就是它本身。
初始步骤如下:
1、定义两个指针 start 和 end,数组的 [start, end] 范围内的元素是待全排列的元素;
2、初始时:start = 0,end = array.length - 1,即一开始需要全排列的元素是所有元素;
3、接下来开始递归:
3.1 递归函数的功能:求解 array 的 [start, end] 区间元素的全排列;
3.2 递归结束条件:start == end;
3.3 不断缩减 [start, end] 区间大小,即:每一次递归,start++。
把所有的全排列都保存在 ArrayList<String>
中,然后从中找出最小的。
2.2 贪心算法
贪心算法是局部最优解,我们知道,局部最优解不一定能得到全局最优解,但是,也不一定得不到全局最优解。
对于本题这种情况,是适合使用贪心算法,贪心算法得出的解就是最优解,至于为什么,可以查看相应的数学证明,查看什么情况下贪心算法的解等于最优解。
对于两个字符串 a 和 b,如果 a + b < b + a,那么 a + b 就是最小值,即我们偏向于把 a 放在前边。
基于这种情况,我们首先把 array 数组从小到大排列,然后从小到大两个元素依次比较。
步骤如下:
1、把 array 从小到大排列;
2、定义一个 String 类型的变量 result,初始值为:String.valueOf(array[0])
;
3、从第 1 个元素开始,遍历 array,如果 result + String.valueOf(numbers[i]) < String.valueOf(numbers[i]) + result,则 result = result + String.valueOf(numbers[i]),反之 result = String.valueOf(numbers[i]) + result。
4、遍历完毕,返回 reuslt。
3、解题代码
3.1 暴力计算
package pers.klb.jzoffer.hard;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @program: JzOffer2021
* @description: 把数组排成最小的数
* @author: Meumax
* @create: 2020-07-25 09:59
**/
public class PrintMinNumber {
public String PrintMinNumber(int[] numbers) {
if (numbers.length == 0) return "";
ArrayList<String> list = new ArrayList<>();
permutation(numbers, 0, numbers.length - 1, list);
String min = list.get(0);
for (int i = 1; i < list.size(); i++) {
if (list.get(i).compareTo(min) < 0) {
min = list.get(i);
}
}
return min;
}
private void permutation(int[] array, int start, int end, ArrayList<String> list) {
if (start == end) {
String s = "";
for (int num : array) {
s += String.valueOf(num);
}
list.add(s);
} else {
for (int i = start; i <= end; i++) {
swap(array, start, i); // array[start] 和 array[i] 交换,即让每一个元素都当一次首元素
permutation(array, start + 1, end, list);
swap(array, start, i); // 恢复 array
}
}
}
private void swap(int[] array, int index1, int index2) {
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
}
3.2 贪心算法
package pers.klb.jzoffer.hard;
import java.util.Arrays;
/**
* @program: JzOffer2021
* @description: 把数组排成最小的数
* @author: Meumax
* @create: 2020-07-25 09:59
**/
public class PrintMinNumber {
public String PrintMinNumber(int[] numbers) {
if (numbers.length == 0) return "";
Arrays.sort(numbers); // 对数组从小到大排序
String result = String.valueOf(numbers[0]);
for (int i = 1; i < numbers.length; i++) {
result = joint(result, String.valueOf(numbers[i]));
}
return result;
}
private String joint(String s1, String s2) {
return (s1 + s2).compareTo(s2 + s1) < 0 ? (s1 + s2) : (s2 + s1);
}
}
4、解题心得
本题难度适中,考察贪心算法的使用,对于某些情况下,贪心算法的解就是最优解,但是什么情况下成立,得查阅详细的数学证明。