LeetCode60 第k個排列

第k個排列》》》
在這裏插入圖片描述

  • 暴力法:求出數組的所有排列,然後求其第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));
    }


}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章