leetcode *60. 第k个排列

【题目】*60. 第k个排列

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

"123"
"132"
"213"
"231"
"312"
"321"

给定 n 和 k,返回第 k 个排列。

说明:

给定 n 的范围是 [1, 9]。
给定 k 的范围是[1,  n!]。

示例 1:

输入: n = 3, k = 3
输出: "213"

示例 2:

输入: n = 4, k = 9
输出: "2314"

【解题思路1】数学

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
    
    
    public String getPermutation(int n, int k) {
    
    
        int[] factorial = new int[n];
        factorial[0] = 1;
        for (int i = 1; i < n; ++i) {
    
    
            factorial[i] = factorial[i - 1] * i;
        }

        --k;
        StringBuffer ans = new StringBuffer();
        int[] valid = new int[n + 1];
        Arrays.fill(valid, 1);
        for (int i = 1; i <= n; ++i) {
    
    
            int order = k / factorial[n - i] + 1;
            for (int j = 1; j <= n; ++j) {
    
    
                order -= valid[j];
                if (order == 0) {
    
    
                    ans.append(j);
                    valid[j] = 0;
                    break;
                }
            }
            k %= factorial[n - i];
        }
        return ans.toString();
    }
}

【解题思路2】全排列

所求排列 一定在叶子结点处得到,进入每一个分支,可以根据已经选定的数的个数,进而计算还未选定的数的个数,然后计算阶乘,就知道这一个分支的 叶子结点 的个数:

  • 如果 k 大于这一个分支将要产生的叶子结点数,直接跳过这个分支,这个操作叫「剪枝」;
  • 如果 k 小于等于这一个分支将要产生的叶子结点数,那说明所求的全排列一定在这一个分支将要产生的叶子结点里,需要递归求解。
class Solution {
    
    
    boolean[] used; //记录数字是否使用过
    int[] factorial; //阶乘数组

    public String getPermutation(int n, int k) {
    
    
        calculateFactorial(n);

        // 查找全排列需要的布尔数组
        used = new boolean[n + 1];
        Arrays.fill(used, false);

        StringBuilder path = new StringBuilder();
        dfs(0, path, n, k);
        return path.toString();
    }


    /**
     * @param index 在这一步之前已经选择了几个数字,其值恰好等于这一步需要确定的下标位置
     * @param path
     */
    public void dfs(int index, StringBuilder path, int n, int k) {
    
    
        if (index == n) {
    
    
            return;
        }

        // 计算还未确定的数字的全排列的个数,第 1 次进入的时候是 n - 1
        int cnt = factorial[n - 1 - index];
        for (int i = 1; i <= n; i++) {
    
    
            if (used[i]) {
    
    
                continue;
            }
            if (cnt < k) {
    
    
                k -= cnt;
                continue;
            }
            path.append(i);
            used[i] = true;
            dfs(index + 1, path, n, k);
            // 注意 1:没有回溯(状态重置)的必要

            // 注意 2:这里要加 return,后面的数没有必要遍历去尝试了
            return;
        }
    }

    // 计算阶乘数组
    public void calculateFactorial(int n) {
    
    
        factorial = new int[n + 1];
        factorial[0] = 1;
        for (int i = 1; i <= n; i++) {
    
    
            factorial[i] = factorial[i - 1] * i;
        }
    }

}

猜你喜欢

转载自blog.csdn.net/XunCiy/article/details/108415849
今日推荐