60.Permutation Sequence (M)

给定n和k,返回字典序排序的第k个n个数的排列序列。当n个数的排列完成后,才会对第一个数进行更替,周期为(n-1)!,第一位数字按从小到大依次更替。通过计算找到第k个排列的首位数字,并逐步缩小规模求解剩余部分。

Permutation Sequence (M)

The set [1,2,3,...,*n*] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order, we get the following sequence for n = 3:

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

Given n and k, return the kth permutation sequence.

Note:

  • Given n will be between 1 and 9 inclusive.
  • Given k will be between 1 and n! inclusive.

Example 1:

Input: n = 3, k = 3
Output: "213"

Example 2:

Input: n = 3, k = 3
Output: "213"

题意

按字典序输出一个递增序列的第k个排列。

思路

对于n个数的排列,只有当后面n-1个数完成一次全排列后,才会对第一个数进行更替,即更换周期cycle为(n-1)!,且第一个数字是按照n个数中从最小数到最大数依次更替的。令 c o u n t = k − 1 c y c l e + 1 count=\frac{k-1}{cycle}+1 count=cyclek1+1,则排在第一位的数字是这n个数中的第count大,将该数字加入结果序列。每次得到第一位数字后,缩小n的规模求剩下n-1个数中应当排在第一位的数,同时要更新n-1个数中要求的第k个排列 k = ( k − 1 )   %   c y c l e + 1 k=(k-1)\ \%\ cycle+1 k=(k1) % cycle+1

笨办法:也可以结合 31. Next Permutation,从递增序列开始依次计算下一个排列。


代码实现 - 直接计算

class Solution {
    public String getPermutation(int n, int k) {
        StringBuilder ans = new StringBuilder();
        boolean[] used = new boolean[n];		//标记剩下的数字

        while (n >= 1) {
            // 注意n=1时实际周期为0,将其周期设为1便于合并处理
            int cycle = n == 1 ? 1 : factorial(n - 1);
            int count = (k - 1) / cycle + 1;
            
            // 找到剩余数字中的第count大,并将其加入结果序列
            for (int i = 0; i < used.length; i++) {
                if (!used[i]) {
                    if (count == 1) {
                        ans.append(i + 1);
                        used[i] = true;
                        break;
                    }
                    count--;
                }
            }
            
            // 缩小待排列序列的规模
            n--;
            k = (k - 1) % cycle + 1;
        }

        return ans.toString();
    }

    private int factorial(int n) {
        int ans = 1;
        while (n != 1) {
            ans *= n--;
        }
        return ans;
    }
}

代码实现 - nextPermutation

class Solution {
    public String getPermutation(int n, int k) {
        char[] a = new char[n];
        for (int i = 0; i < n; i++) {
            a[i] = (char) (i + 1 + '0');
        }
        int count = 1;
        while (count < k) {
            nextPermutation(a);
            count++;
        }
        return String.valueOf(a);
    }

    // 依照当前排列计算下一个排列
    private void nextPermutation(char[] a) {
        int i = a.length - 2;
        while (i >= 0 && a[i] >= a[i + 1]) {
            i--;
        }
        // i==-1说明整个数组是逆序排列,已经是字典序中的最后一个排列
        if (i == -1) {
            return;
        }
        int j = a.length - 1;
        while (a[j] <= a[i]) {
            j--;
        }
        swap(a, i, j);
        reverse(a, i + 1, a.length - 1);
    }

    private void reverse(char[] a, int left, int right) {
        while (left < right) {
            swap(a, left++, right--);
        }
    }

    private void swap(char[] a, int i, int j) {
        char temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值