在解决全排列问题之前,先讲解一个它的子问题,输出给定数字比它大的下一个数字,为了方便,我们把输入和输出数字用数组表示
给出一组数,输出它的下一个排列
假设给定数组[1,2,3,5,4] 比它大的下一个数字是[1,2,4,3,5]
思想:
假设出入数组为arr
1.我们从后向前遍历,找到arr[i]>arr[i-1],并且返回i,如果没有找到返回0;
2.用index接收返回的下标,判断index是否等于0,如果等于0,返回null,否则在区间 arr.length-1 和 index 区间找出比arr[index-1]小的值,两者交换,并退出循环;
3.在对 index和arr.length-1组成的区间 进行逆序,
4.返回数组arr,数组arr表示当前数组的下一个排列
实现图如下:
实现代码如下:
//找到下一个排列
public static int[] findNearestNumber(int[] arr){
int index = returnIndex(arr);
if(index == 0) return null;
for(int i = arr.length-1;i>=index;i--){
if(arr[index-1]<arr[i]){
int tmp = arr[index-1];
arr[index-1] = arr[i];
arr[i] = tmp;
break;
}
}
reverseArr(arr,index,arr.length-1);
return arr;
}
//逆序
public static void reverseArr(int[] arr,int start,int end){
while(start<=end){
int tmp = arr[start];
arr[start++] = arr[end];
arr[end--] = tmp;
}
}
//从后向前遍历找出arr[i]<arr[i-1]的下标i
public static int returnIndex(int[] arr){
for (int i = arr.length-1; i >0 ; i--) {
if(arr[i]>arr[i-1]){
return i;
}
}
return 0;
}
这就是求一个数组的下一个排列数组,是不是很简单?
下面这个全排列的例子该如何实现呢?
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
通过上面的例子,我们可以求出一个数组的比它大的下一个数组,那么我们要输出给定某数组的全部排列就很简单了,在解决此题之前,我们首先的判断该数组总共有多少排列组合,然后在进行操作,先对该数组按升序进行排序,也就是最小值a,我们可以求a的下一个排列b,紧接着我们可以求b的下一个排列c...直到该排列为空,我们就求出了该数组的全部排列组合。
代码如下:
public List<List<Integer>> permute(int[] nums) {
Arrays.sort(nums);
int length = nums.length;
List<List<Integer>> list = new ArrayList<List<Integer>>();
int n = getN(length);
for(int i = 0;i<n;i++){
List<Integer> list1 = new ArrayList<Integer>();
//将原数组加入集合中
if(i == 0){
List<Integer> list2 = new ArrayList<Integer>();
for(int j:nums){
list2.add(j);
}
list.add(list2);
}
//求得nums下一个排列
nums = findNearestNumber(nums);
//如果返回数组为null表示已经排列完成,结束循环
if(nums == null) break;
//将数组元素加入集合中
for(int j:nums){
list1.add(j);
}
//将集合list1加入集合list中
list.add(list1);
}
return list;
}
//获取总共的排列组合数
public static int getN(int length){
if(length == 1){
return length;
}
return length*getN(length-1);
}
//排列
public static int[] findNearestNumber(int[] arr){
int index = returnIndex(arr);
if(index == 0) return null;
for(int i = arr.length-1;i>=index;i--){
if(arr[index-1]<arr[i]){
int tmp = arr[index-1];
arr[index-1] = arr[i];
arr[i] = tmp;
break;
}
}
reverseArr(arr,index,arr.length-1);
return arr;
}
//逆序
public static void reverseArr(int[] arr,int start,int end){
while(start<=end){
int tmp = arr[start];
arr[start++] = arr[end];
arr[end--] = tmp;
}
}
//从后向前遍历找出arr[i]<arr[i-1]的下标i
public static int returnIndex(int[] arr){
for (int i = arr.length-1; i >0 ; i--) {
if(arr[i]>arr[i-1]){
return i;
}
}
return 0;
}
如果给定的数组有重复的数字,那么我们该如何解决呢?
其实上面的代码也适用于数组有重复数字,这就是采用非递归的好处。
下面是采用递归实现代码:
这个代码实现给定的数组中没有重复数字的
//递归实现 调用时n是0
public static void printSort(int[] num, int n) {
if(n >= num.length-1){
System.out.println(Arrays.toString(num));
}
else{
for(int i = n;i < num.length;i++) {
swap(num,i,n);
printSort(num,n+1);
swap(num,i,n);
}
}
}
//交换指定下标元素
public static void swap(int[] num, int i, int n) {
int flag = num[i];
num[i] = num[n];
num[n] = flag;
}
如果有重复的元素,用递归该如何实现,其实很简单,我们只要让第一次出现该数作为开头
代码如下:
//调用时n是0
public static void printSort(int[] num, int n) {
List<Integer> list = new ArrayList<Integer>();
if(n >= num.length-1){
System.out.println(Arrays.toString(num));
}
else{
for(int i = n;i < num.length;i++) {
//筛选重复的数
if(!list.contains(num[i])){
list.add(num[i]);
swap(num,i,n);
printSort(num,n+1);
swap(num,i,n);
}
}
}
}
public static void swap(int[] num, int i, int n) {
int flag = num[i];
num[i] = num[n];
num[n] = flag;
}