题目原型:
Given a collection of numbers, return all possible permutations.
For example,
[1,2,3]
have the following permutations:
[1,2,3]
, [1,3,2]
, [2,1,3]
, [2,3,1]
, [3,1,2]
, and [3,2,1]
.
基本思路:
(1).递归解法:
给定一个字符串,求出这些字符的全排列。面对这种复杂的问题,我们分解成小得问题来考虑。比如,面对一个字符串,我们把它分成两个部分:第一部分是它的第一个字符,第二部分是后面剩下的所有字符。那么我们要求字符串的全排列,可以分成两步:
1,首先求出所有可能在第一个位置出现的字符(可以把第一个字符和后面的所有的字符交换所得)。
2,确定了第一个字符以后,再对后面的所有字符进行全排列。(对剩下的字符全排列就相当于一个递归的过程,又从第一步开始处理)
因为给的集合,元素可能是无序的,所以首先进行排序,在这里使用快排:
// 快速排序
public void sort(int[] num,int low , int high)
{
int pivot = -1;
if(low<high)
{
pivot = partition(low, high, num);
sort(num, low, pivot-1);
sort(num, pivot+1, high);
}
}
public int partition(int low, int high, int[] num)
{
int reference = num[low];
while(low<high)
{
while(low<high&&num[high]>=reference)
high--;
num[low] = num[high];
while(low<high&&num[low]<=reference)
low++;
num[high] = num[low];
}
num[low] = reference;
return low;
}
然后递归求序列:
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();//全局变量
//recursion全排列的递归实现(没有重复数字)
public ArrayList<ArrayList<Integer>> permuteByRecursion(int[] num)
{
//排序
sort(num, 0, num.length-1);
allRange(num, 0, num.length-1);
return result;
}
//添加
public ArrayList<ArrayList<Integer>> add(ArrayList<ArrayList<Integer>> result,int[] num)
{
ArrayList<Integer> in = new ArrayList<Integer>();
for(int i = 0;i<num.length;i++)
{
in.add(num[i]);
}
result.add(in);
return result;
}
//sur表示当前选取的数,sum表示总共有多少个数
public void allRange(int[] num,int cur,int sum)
{
if(cur==sum)
{
//添加result
add(result, num);
return;
}
else
{
for(int i = cur;i<=sum;i++)//第i个数分别与它后面的数字交换就能得到新的排列
{
swap(num, i, cur);
allRange(num, cur+1, sum);//表示,若sur和sum相邻时输出
swap(num, i, cur);
}
}
}
//交换
public void swap(int[] num ,int i , int j)
{
int temp = num[i];
num[i] = num[j];
num[j] = temp;
}
(2)非递归解法
要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。
如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增字,"20"、"52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220",然后再将替换点后的字符串"6220"颠倒即得到"950226"。
//全排列的(非递归实现)
public ArrayList<ArrayList<Integer>> permute(int[] num)
{
// 排序num中的数,简便起见,采用快速排序
sort(num, 0, num.length-1);
for(int i = 0; i < num.length; i++)
System.out.print(num[i]);
System.out.println("********************************");
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
// 第一个数
ArrayList<Integer> number = new ArrayList<Integer>();
for (int i = 0; i < num.length; i++)
{
number.add(num[i]);
}
result.add(number);
boolean hasFound = true;
int i, j;
while (hasFound)
{
hasFound = false;
// 从后往前找到两个相邻的升序序列,前一个数为替换数
for (i = num.length - 1; i > 0; i--)
{
if (num[i - 1] < num[i])
{
hasFound = true;
break;
}
}
if (hasFound == false)
break;
number = new ArrayList<Integer>();
// 从后往前走,找到第一个比替换书大的数
for (j = num.length - 1; j > i - 1; j--)
{
if (num[j] > num[i - 1])
{
int temp = num[i - 1];
num[i - 1] = num[j];
num[j] = temp;
break;
}
}
// 倒置替换数后面的数
for (j = num.length - 1; i < num.length - 1 && j > i; i++, j--)
{
int temp = num[i];
num[i] = num[j];
num[j] = temp;
}
for (i = 0; i < num.length; i++)
number.add(num[i]);
result.add(number);
}
return result;
}