- 暴力法:求出數組的所有排列,然後求其第
k
個排列即可- 遞歸回溯:
在遞歸的過程中進行剪枝,若在這條路徑中不會出現答案則進行剪枝,跳過即可
;
/**
*
* 使用力扣第46題:全排列,即使用回溯搜索思想,依次得到全排列,輸出所求的第k個全排列
* 但事實上,我們不必求出所有全排列,基於以下幾點考慮:
* 1、我們知道所求排列一定在葉子結點處得到。事實上,進入每一個分支的時候,我們都可以通過遞歸的層數,直接計算這一分支可以得到的葉子結點的個數;
這是因爲:進入一個分支的時候,我們可以根據已經選定的數的個數,進而確定還未選定的數的個數,然後計算階乘,就知道這一個分支的葉子結點有多少個。
2、如果 kk 大於這一個分支將要產生的葉子結點數,直接跳過這個分支,這個操作叫“剪枝”;
3、如果 kk 小於等於這一個分支將要產生的葉子結點數,那說明所求的全排列一定在這一個分支將要產生的葉子結點裏,需要遞歸求解;
4、計算階乘的時候,你可以使用循環計算,特別注意:0!=10!=1,它表示了沒有數可選的時候,即表示到達葉子結點了,排列數只剩下 11 個;
*
*/
package BDyNamicProgramming;
import DString.Problem3;
import java.util.ArrayList;
import java.util.List;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/4/26 0026 14:36
*/
public class Problem60 {
//採用暴力法:全排列:不剪枝
//超出時間限制
// public String getPermutation(int n, int k) {
//
// List<Integer> path = new ArrayList<>();
// List<List<Integer>> rs = new ArrayList<>();
// boolean[] used = new boolean[n];
//
// dfs(n,path,rs,0,used);
//
// List<Integer> r = rs.get(k-1);
// StringBuilder sb = new StringBuilder();
// for(Integer i:r) sb.append(i+"");
// return sb.toString();
//
//
// }
//
// public void dfs(int n, List<Integer> path,List<List<Integer>> rs,int start,boolean[] used){
//
// if(path.size()==n){
// rs.add(new ArrayList<>(path));
// return;
// }
//
// for(int i=0;i<n;i++){
//
// if(used[i]) continue;
// used[i]=true;
// path.add(i+1);
// dfs(n,path,rs,i+1,used);
// used[i]=false;
// path.remove((Object)(i+1));
// }
//
// }
/**
*
* 使用力扣第46題:全排列,即使用回溯搜索思想,依次得到全排列,輸出所求的第k個全排列
* 但事實上,我們不必求出所有全排列,基於以下幾點考慮:
* 1、我們知道所求排列一定在葉子結點處得到。事實上,進入每一個分支的時候,我們都可以通過遞歸的層數,直接計算這一分支可以得到的葉子結點的個數;
這是因爲:進入一個分支的時候,我們可以根據已經選定的數的個數,進而確定還未選定的數的個數,然後計算階乘,就知道這一個分支的葉子結點有多少個。
2、如果 kk 大於這一個分支將要產生的葉子結點數,直接跳過這個分支,這個操作叫“剪枝”;
3、如果 kk 小於等於這一個分支將要產生的葉子結點數,那說明所求的全排列一定在這一個分支將要產生的葉子結點裏,需要遞歸求解;
4、計算階乘的時候,你可以使用循環計算,特別注意:0!=10!=1,它表示了沒有數可選的時候,即表示到達葉子結點了,排列數只剩下 11 個;
*
*/
public String getPermutation(int n, int k){
//記錄數字是否適用過
boolean[] used = new boolean[n+1];
//階乘數組
int[] factorial = new int[n+1];
//從根節點到葉子節點的路徑
List<Integer> path = new ArrayList<>();
// 計算階乘數組
factorial = new int[n + 1];
factorial[0] = 1;
for (int i = 1; i <= n; i++) {
factorial[i] = factorial[i - 1] * i;
}
dfs(0,n,k,used,path,factorial);
StringBuilder sb = new StringBuilder();
for(Integer i:path) sb.append(i+"");
return sb.toString();
}
/**
*
* @param index 在這一步之前已經選擇了好幾個數字,其值恰好等於這一步需要確定的索引值
*/
public void dfs(int index,int n,int k,boolean[] used,List<Integer> path,int[] factorial){
if(index==n) return;
//還未確定數字全排列個數,第一次進入的時候是n-1
int cnt = factorial[n-1-index];
for(int i=1;i<=n;i++){
//排列,已經用過跳過
if(used[i]) continue;
//當前所有可能的排列下不滿足,則跳過,對k進行更新
if(cnt<k) {
k=k-cnt;
continue;
}
//將數據添加到路徑中
path.add(i);
//標記當前數據已經使用過了
used[i]=true;
dfs(index+1,n,k,used,path,factorial);
}
}
public int fun(int n){
if(n==1||n==0) return 1;
return n*fun(n-1);
}
public static void main(String[] args) {
Problem60 problem60 = new Problem60();
// System.out.println(problem60.getPermutation1(3,3));
}
}