遞歸方式實現全排列:圖片資源來自於網絡(遞歸順序類似樹的後序遍歷)
代碼實現
/*****************
* 將字符串的組成元素進行全排列 後輸出
* @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--;
}
}