递归方式实现全排列:图片资源来自于网络(递归顺序类似树的后序遍历)
代码实现
/*****************
* 将字符串的组成元素进行全排列 后输出
* @param str 输入的字符串
* @return 所有的全排序序列
*/
public ArrayList<String> Permutation(String str) {
//输出的结果集
ArrayList<String> result=new ArrayList<>();
//非空判断
if(str==null||str.length()==0)
{
return result;
}
//调用递归全排序算法
allRangeRecu(str.toCharArray(), 0, str.length()-1, result);
//字典序排序
Collections.sort(result);
//返回结果集
return result;
}
/*************
* 对数组中的元素进行全排序 排序序列保存在ArrayList中
* @param array 进行全排序的元素序列
* @param start 序列开始位置
* @param end 序列结束位置
*/
private void allRangeRecu(char[] array,int start,int end,ArrayList<String> result)
{
if(start==end)//如果只剩下一个元素 则固定此序列
{
String curOrder=String.valueOf(array);//保存当前的序列
if(!result.contains(curOrder))//去重 只有当前的字符串不存在结果集中时才添加
{
result.add(curOrder);
}
}
else
{
for(int i=start;i<=end;i++)
{
char temp=array[i];
array[i]=array[start];
array[start]=temp;
allRangeRecu(array, start+1, end, result);
temp=array[i];
array[i]=array[start];
array[start]=temp;
}
}
}
非递归方式实现全排序
非递归求解的过程就是一个找序列的下一序列的过程
这对初始的序列有要求,需要满足是按照升序或者降序排列的。
方法的基本流程为:
- 首先,将所有的元素进行排序(假设初始按照升序排列)。
- 然后,当所有的元素没有完全变成另一种排序方式时,执行循环。
- 从数组尾节点位置向前寻找,找到相邻两个位置中前一位置元素小于后一元素的位置,记录前一位置为leftIndex。如果没找到,则说明序列已经变成降序,查找序列完成。
- 从leftIndex+1开始,向后寻找最后一个大于array[leftIndex]的值,记录此位置为rightIndex。
- 交换两个位置的元素
- 将原数组从leftIndex+1位置开始,转置后面的数组。
- 将转置后的结果添加到结果集中。重新开始循环。
算法过程如图:
最后为什么要进行转置?
为了保证下一个排序是恰好比当前排列大的一个序列。每次两个元素交换后,都会将一个较大的元素移到前面(leftIndex位置)去,而从leftIndex+1的位置向后是一个递减的序列,因为移动到前面去一个大的值,高位变大,低位要调整为最小的(升序排列),才能保证当前的序列恰好比之前的序列大。
代码实现如下:
/***********************
* 使用非递归方式对字符串进行字典序排序
* @param array 需要进行排序的数组
* @param start 数组开始位置
* @param end 数组结束位置
* @param result 排序的结果集
*/
public void allRange(char[] array,int start,int end,ArrayList<String> result)
{
//将数组排序为有序的
Arrays.sort(array);
result.add(String.valueOf(array));
//循环查找所有元素的下一元素
//这是一个从123到321的过程
while(true)
{
int leftIndex=end;//从尾部向前找第一个array[i-1]<array[i]的位置
while(leftIndex>=1&&array[leftIndex-1]>=array[leftIndex])
{
//从右边数第一个小右侧元素的下标加1
leftIndex--;
}
//序列已经变成降序排列时 退出循环
if(leftIndex==0)
{
break;
}
leftIndex--;
int rightIndex=leftIndex+1;//查找右侧交换元素
//从查找到的左侧元素的下一位开始
//找到最后一位大于array[leftIndex]的位置
while(rightIndex<=end&&array[rightIndex]>array[leftIndex])
{
rightIndex++;
}
rightIndex--;
//交换两位置的元素
char temp=array[leftIndex];
array[leftIndex]=array[rightIndex];
array[rightIndex]=temp;
//转置出前缀之外的数组
reverseArray(array, leftIndex+1, end);
//结果添加到数组中
result.add(String.valueOf(array));
}
}
/****************
* 转置数组内指定区域内的元素
* @param array 需要进行转置的数组
* @param start 需要进行转置数组的开始位置
* @param end 结束位置
*/
private void reverseArray(char[] array,int start,int end)
{
if(start>=end)
{
return;
}
while(start<end)
{
char temp=array[start];
array[start]=array[end];
array[end]=temp;
start++;
end--;
}
}