0060. Permutation Sequence (M)

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个数中从最小数到最大数依次更替的。令 \(count=\frac{k-1}{cycle}+1\),则排在第一位的数字是这n个数中的第count大,将该数字加入结果序列。每次得到第一位数字后,缩小n的规模求剩下n-1个数中应当排在第一位的数,同时要更新n-1个数中要求的第k个排列 \(k=(k-1)\ \%\ cycle+1\)

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


代码实现

Java

公式计算

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;
    }
}

JavaScript

公式计算

/**
 * @param {number} n
 * @param {number} k
 * @return {string}
 */
var getPermutation = function (n, k) {
  let used = new Array(n)
  let ans = ''

  while (n > 0) {
    let cycle = n === 1 ? 1 : factorial(n - 1)
    let rank = Math.trunc((k - 1) / cycle) + 1
    let count = 0
    for (let i = 0; i < used.length; i++) {
      if (!used[i]) {
        count++
        if (count === rank) {
          ans += i + 1
          used[i] = true
          break
        }
      }
    }
    k = ((k - 1) % cycle) + 1
    n--
  }

  return ans
}

let factorial = function (n) {
  return n === 1 ? n : n * factorial(n - 1)
}

nextPermutation

/**
 * @param {number} n
 * @param {number} k
 * @return {string}
 */
var getPermutation = function (n, k) {
  let count = 1
  let arr = new Array(n).fill(0).map((v, index) => index + 1)
  while (count !== k) {
    nextPermutation(arr)
    count++
  }
  return arr.join('')
}

let nextPermutation = function (arr) {
  let i = arr.length - 2
  while (i >= 0 && arr[i] >= arr[i + 1]) {
    i--
  }
  if (i === -1) {
    return
  }
  let j = arr.length - 1
  while (arr[i] >= arr[j]) {
    j--
  }
  [arr[i], arr[j]] = [arr[j], arr[i]]
  let left = i + 1,
    right = arr.length - 1
  while (left < right) {
    [arr[left++], arr[right--]] = [arr[right], arr[left]]
  }
}
posted @ 2020-07-07 02:57  墨云黑  阅读(179)  评论(0编辑  收藏  举报