首页 > 试题广场 >

返回第k个排列

[编程题]返回第k个排列
  • 热度指数:10822 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
集合[1,2,3,…,n]一共有n!种不同的排列
按字典序列出所有的排列并且给这些排列标上序号
我们就会得到以下的序列(以n=3为例)
  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"
现在给出n和k,请返回第k个排列
注意:n在1到9之间

示例1

输入

3,1

输出

"123"
public String getPermutation(int n, int k) {
        String res="";
        ArrayList<Integer> list =new ArrayList<>();
        for(int i=0;i<n;i++)
            list.add(i+1);
        int[] f = new int[n];
        f[0]=1;
        k--;
        for(int i=1;i<n;i++) f[i]=f[i-1]*i;
        for(int i=n;i>=1;i--){
            int j=k/f[i-1];
            k%=f[i-1];
            res+=list.get(j);
            list.remove(j);
        }
        System.out.println(res);
        return res;
    }
发表于 2017-12-25 23:06:02 回复(0)
如何找出第16个(按字典序的){1,2,3,4,5}的全排列?
 
1. 首先用16-1得到15
 
2. 用15去除4! 得到0余15
 
3. 用15去除3! 得到2余3
 
4. 用3去除2! 得到1余1
 
5. 用1去除1! 得到1余0
 
有0个数比它小的数是1,所以第一位是1
 
有2个数比它小的数是3,但1已经在之前出现过了所以是4
 
有1个数比它小的数是2,但1已经在之前出现过了所以是3
 
有1个数比它小的数是2,但1,3,4都出现过了所以是5
 
最后一个数只能是2

class Solution {
public:
    string getPermutation(int n, int k) {
        string s(n, '0');
        string result;
        for(int i=0; i<n; i++)
            s[i] += i+1;
       
        return kth_permutation(s, k);
    }
private:
    int factorial(int n){
        int result = 1;
        for(int i=1; i<=n; i++)
            result *= i;
        return result;
    }
   
    string kth_permutation(const string& seq, int k){
        const int n = seq.size();
        string S(seq);
        string result;
       
        int base = factorial(n-1);
        k--;
        for(int i=n-1; i>0; k %= base, base /= i, i--){
            auto a = next(S.begin(), k/base);
            result.push_back(*a);
            S.erase(a);
        }
        result.push_back(S[0]);
        return result;
    }
};

发表于 2016-06-28 14:37:22 回复(3)

最开始用暴力全排序的方法,直接超时,但是感觉在本地测试,速度没那么慢,毕竟1<n<=9;
暴力不行只能另想办法。
第一个数一定是123...n,所以第一位以1开头的数有(n-1)!种可能,
所以第二位以某个数开头有(n-2)!中可能
同理...
所以我们求每一位上的值是多少?
第一位是多少呢?
k/(n-1)!
第二位是多少呢?
k=k%(n-1)!
k/(n-2)!
同理一直算到最后一位数。
下面给出代码

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class Solution {
    public  String getPermutation(int n, int k) {
        k--;//我们算法中是k从0开始,题意从1开始,所以我们要k--;
        int num[]=new int[n];
        int cnt=1;
        for(int i=0;i<n;i++)
        {
            num[i]=i+1;//初始化{1,2,3,...,n}
            cnt*=(i+1);//求n!
        }
        char[] cs=new char[n];//保存每一位结果
        for(int i=0;i<n;i++)
        {
            cnt/=(n-i);
            int selected=k/cnt;
            cs[i]=(char) ('0'+num[selected]);
            for(int j=selected;j<n-1-i;j++)
            {
                num[j]=num[j+1];
            }
            k%=cnt;
        }
        return new String(cs);
    }



}
发表于 2017-04-26 11:12:08 回复(0)

全排列暴力递归法也是可以的啊!

public class PermutationSequence {

  int count;
  String result;

  public String getPermutation(int n, int k) {
    char[] arr = new char[n];
    for (int i = 0; i < n; i++)
      arr[i] = (char) ('1' + i);
    permutation(arr, 0, k);
    return result;
  }

  private void permutation(char[] arr, int index, int cc) {
    //至于什么时候输出,要考虑清楚
    //考虑:只有所有位置上的字符都确认即index到达末尾,才算是排好一种情况
    if (index == arr.length) {
      count++;
      // System.out.println(String.valueOf(arr));
      if (count == cc)
        result = String.valueOf(arr);
    }
    //现在index之前的字符都已就位,把之后的每个字符都交换到index这个位置
    for (int k = index; k < arr.length; k++) {
      //尝试交换
      swap1(arr, index, k);
      //交换之后index这个位置就定好了,接下来的事就是递归去解决index+1位置开始的全排列就好了
      permutation(arr, index + 1, cc);
      // 前面我们尝试交换,把全排列求出来了,交换时只尝试了一个字符,因此for循环继续之前要换回来,继续尝试交换下一个字符
      swap2(arr, index, k);
    }
  }

  private void swap1(char[] arr, int index, int k) {
    char tmp = arr[k];
    //用插入法不改变后续元素的大小顺序
    for (int i = k; i > index; i--) {
      arr[i] = arr[i - 1];
    }
    // arr[k] = arr[index];
    arr[index] = tmp;
  }

  private void swap2(char[] arr, int index, int k) {
    char tmp = arr[index];
    //用插入法不改变后续元素的大小顺序
    for (int i = index; i < k; i++) {
      arr[i] = arr[i + 1];
    }
    arr[k] = tmp;
  }

  public static void main(String[] args) {
    Instant now = Instant.now();
    System.out.println(new PermutationSequence().getPermutation(9,362880));
    System.out.println(Instant.now().toEpochMilli()-now.toEpochMilli());
  }
}
发表于 2017-12-10 18:53:49 回复(0)

string getPermutation(int n, int k) {
        // 计算n-1位数字共有多少种组合,便可以求出来第k个组合是以哪个数字开头
        // 之后将第一位数字去除,再计算n-2位数字共有多少种组合,...

        // 预先将n!(n = 1 ~ n - 1)存起来
        vector<int> rec(n, 1);
        for (int i = 2; i < n; i ++) rec[i] = i * rec[i - 1];
        vector<int> data(n, 0);
        for (int i = 0; i < n; i ++) data[i] = i + 1;

        string res = "";
        for (int i = 1; i < n && k >= 1; i ++ ) {
            // 若当前k可以整除n-i位数字的全排列个数时,digit就是商;
            // 当无法整除时,需要再额外+1,才是最终的digit
            int tmp = k / rec[n - i];
            int digit = k % rec[n - i] == 0 ? tmp : tmp + 1;
            int num = data[digit - 1];
            data.erase(remove(data.begin(), data.end(), num), data.end());
            // 前面已经有了t = (digit - 1) * rec[n - i]个数字,需要再去计算余下的第(k - t)个数字
            k = k - (digit - 1) * rec[n - i];
            res += to_string(num);
        }
        
        // k=1时,表明后面的几位按照顺序来就可以了,所以将data中剩余的都加进去
        for (auto d: data) {
            res += to_string(d);
        }

        return res;
    }

发表于 2019-06-14 16:48:30 回复(0)
利用回溯法的框架做了一遍
跟subsets那题类似
主要trick在于:
1. res 记录要返回的结果
2. 用一个数组num[n]记录1~n的数字,如果数字 i 已经被记录,则把对应的num[i-1]记录为0
3. 用count记录目前已遍历的组合数,当count==k时推出循环,返回res
4. t表示还有t个数要添加,t==0时,一个组合完成(str内有n个数)
public class Solution {
    String res = "";
    int count = 0;
    public String getPermutation(int n, int k) {
        int[] num = new int[n];
        for(int i=1; i<=n; i++){
            num[i-1] = i;
        }
        backTrack(num, k, n, "");
        return res;
    }
    void backTrack(int[] n, int k, int t, String str) {
        if(t == 0) {count ++; res = ""+str; return;} //t==0时,一个组合完成
        for(int i=0; i<n.length; i++){
            if(n[i] == 0) continue;
            str += n[i];
            n[i] = 0;
            backTrack(n, k, t-1, str);
            if(count == k) break;
            n[i] = i+1;
            str = str.substring(0, str.length()-1);
        }
        
    }
    
}


发表于 2018-12-04 23:08:45 回复(2)
来个简单、高效的算法。
当n = 3时,有6种排列组合(3 * 2 * 1 )
123   132
213   231
312   321
我们可以每次只找开头的数字,n = 3时,1开头有两个,2开头有两个,3开头有两个,即6 / 3 = 2;
当k = 3时,为213,即第二行第一列,即2开头
接下来这时候就考虑剩下两个数字1、3的排列组合,
这时n = 2(剩下2个数字), k = (k-1)%2 + 1 = 1,即是2开头的第1个数字,
那么下一步就是 n = 2,  k = 1
13
31
可以用一个数字boolean flag[] = new boolean[n+1]标识哪个数字被用过就行
public class Solution {
    public String getPermutation(int n, int k) {
        boolean num[] = new boolean[n+1];
        int total = 1;
        for(int i=1; i<=n; i++) total *= i;
        return getResult(num,total,  n, k );
    }
    public String getResult(boolean num[],  int total, int n, int k){
        if(n == 0) return "";
        total = total / n;                 //相同开头数字有多少种变化 比如123 132两种 ,总共6种  6 / 3 = 2
        int count  = (k - 1) / total + 1;  //开头是余下n个数字的第几个数字(相当于第几行)  
        int next_k = (k - 1) % total + 1;  //相当于第几列                                                                                 
        int i = 1;                                                                    
        while(count > 0){  //找第count个没用过的数字
            if(!num[i]) count--;                         i++;
        }
        i--;
        num[i] = true; // 数字i用完,设为true
        return String.valueOf(i)+getResult(num, total, n-1, next_k);         }
}

发表于 2018-07-27 22:40:44 回复(0)
class Solution {
public:
    int numb;
    vector<int> num;
    void gethelp(int n)
    {       
        num.clear();
        numb=1;
        for(int i=1;i<=n;i++)
        {
            numb*=i;
            num.push_back(i);            
        }       
    }
    string getPermutation(int n, int k) 
    {
        gethelp(n);
        string out="";
        k--;
        for(int i=0;i<n;i++)
        {
            numb/=(n-i);
            int tp=k/numb;
            out+=char('0'+num[tp]);
            for(int j=tp;j<n;j++)
               num[j]=num[j+1];
            k%=numb;
        }

        return out;
    }
};
发表于 2018-06-16 10:47:25 回复(0)
class Solution {
public:
    string getPermutation(int n, int k) {
        int N=1;
        for(int i=2;i<=n;++i)N*=i;
        --k;
        k=k%N;
        string str="";
        for(int i=1;i<=n;++i)str+=i+'0';
        for(int i=0;i<k;++i)
        {
            m_next_permutate(str);
        }
        return str;
    }
   
    void m_next_permutate(string& str)
    {
        int n=str.length();
        int i=n-1;
        while(i>0 && str[i-1]>str[i])--i;
        if(i!=0)
        {
        --i;
        int j;
        for(j=n-1;j>i;--j)
        {
            if(str[j]>str[i])break;
        }
        char ch=str[i];str[i]=str[j];str[j]=ch;
        reverse(str.begin()+i+1,str.end());
        }
        else
        {
            reverse(str.begin(),str.end());
        }
    }
};
发表于 2017-07-14 11:18:27 回复(0)
import java.util.ArrayList;

public class Solution {
   
   public String getPermutation(int n, int k) {
        ArrayList<String> dict = new ArrayList<>();
        for (int i = 1; i <= n; i++) {
            dict.add(Integer.toString(i));
        }

        return find(dict, k - 1, n);
    }

    public String find(ArrayList<String> dict, int k, int n) {
        if (n == 1) return dict.get(0);

        int num = count(n - 1);
        int index = k / num;
        int offset = k % num;

        String s = dict.get(index);
        dict.remove(index);
        k = offset;

        return s + find(dict, k, n - 1);
    }


    public int count(int x) {
        if (x == 1) return 1;
        return x * count(x - 1);
    }
}
编辑于 2017-06-29 16:17:34 回复(0)
暴力我交了一次  超时了  
然后在网上发现这个办法   第一位有n种情况  每一种情况后面对应(n-1)!种可能 
那么第一位选择整个数组中的哪一位  就为k/(n-1)! 
至于为什么k--  是因为当k==1  但是此时又两个数的时候1/(2-1)!==1  但是其实应该要的是0  所以k-1解决了这个边界问题
public class Solution {
    public String getPermutation(int n, int k) {
        if(k<=0 || n<0){
			 return "";
		 }
		 //求n的阶乘
		 int factorial  =1;
		 StringBuffer buffer = new StringBuffer();//用来存储1--n
		 StringBuffer result = new StringBuffer();
		 for(int i=1;i<=n;i++){
			 factorial *=i;
			 buffer.append(i+"");
		 }
		 k--;
		 for(int i=0;i<n;i++){//对于每一位
			 factorial = factorial/(n-i);
			 int index = k/factorial;
			 result.append(buffer.charAt(index));
			 //移除这个index
			 buffer.deleteCharAt(index);
			 k = k%factorial;
		 }
		return result.toString();    
    }
}

发表于 2016-03-17 13:54:17 回复(0)
class Solution {
public:
    string getPermutation(int n, int k) {
        vector<int> num(n,0);
        string str = "";
        for(int i = 0; i < n; ++ i) num[i] = i+1;
        for(int i = 0; i < k-1; ++ i)
            next_permutation(num.begin(),num.end());
        for(int i = 0; i < n; ++ i) str += num[i]+'0';
        return str;
    }
};

发表于 2018-02-20 15:38:43 回复(0)
class Solution {
public:     int N = 0;     vector<int> d;     string result = "";     int book[15] = {0};
    string getPermutation(int n, int k) {
        DFS(0, n, k);
        return result;
    }
    void DFS(int step, int n, int k)
    {
        int i;
        if(step == n)
        {
            N++;
            if(N == k)
                for(int i=0;i<d.size();i++)
                    result += d[i] + '0';         }else{             for(int i=1;i<=n;i++)             {                 if(book[i] == 0)                 {                     d.push_back(i);                     book[i] = 1;                     DFS(step+1, n, k);                     book[i] = 0;                     d.pop_back();                 }             }         }     }
};

发表于 2017-09-22 08:09:21 回复(0)

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    static int count = 0;
    static String res = "";

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int k = in.nextInt();
        System.out.println(getPermutation(n, k));
    }

    public static String getPermutation(int n, int k) {
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 1; i <= n; i++) {
            list.add(i);
        }
        backTrack(list, k, n, "");
        return res;
    }

    static void backTrack(List<Integer> list, int k, int num, String str) {
        if (num == 0) {
            count++;
            res = "" + str;
            return;
        }
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals(0)) {
                continue;
            }
            str += list.get(i);
            list.set(i, 0);
            backTrack(list, k, num - 1, str);
            if (count == k) {
                break;
            }
            list.set(i, i+1);
            str = str.substring(0, str.length() - 1);
        }

    }
}

还是回溯大法好
编辑于 2023-02-13 12:20:41 回复(0)
import java.util.*;


public class Solution {
    /**
     * 
     * @param n int整型 
     * @param k int整型 
     * @return string字符串
     */
    public String getPermutation (int n, int k) {
        // write code here    
        List<Integer> nArray = new ArrayList(); 
        for(int i=1; i<=n; i++){
            nArray.add(i);
        }
        StringBuffer sb = new StringBuffer();
        sb = calc(n,k,nArray,sb); 
        return sb.toString();
    }
    
    public StringBuffer calc(int n,int k,List<Integer> arr, StringBuffer sb){
         if(arr.size()==1){
            return sb.append("" + arr.get(0));
        }
        int nums = 1;
        for(int i=1; i< n; i++){
            nums *= i;
        }
        int noIndex = k%nums==0?(k-1)/nums:k/nums;
        int no = arr.get(noIndex);
        sb.append(String.valueOf(no));
        arr.remove(noIndex);
        k = k%nums == 0 ? nums : k%nums;
        return calc(n-1,k,arr,sb);
    }
}
发表于 2023-02-12 15:15:46 回复(0)
class Solution {
public:
    string getPermutation(int n, int k) {
        int div,mod;
        int d=1;
        for(int i=1;i<n;++i) d*=i;
        string strInitial="",strRes="";
        for(int i=1;i<=n;++i){
            strInitial+=('0'+i);
        }
        --k;--n;
        while(k){
            div=k/d;mod=k%d;
            strRes+=strInitial[div];
            strInitial.erase(strInitial.begin()+div);
            k=mod;
            d/=n;
            --n;
        }
        strRes+=strInitial;
        return strRes;
    }
};
发表于 2021-10-25 20:19:01 回复(0)

C++求排列有三种方法:

  1. 利用库函数next_permutation
  2. 基于交换
  3. 基于回溯

下面用库函数next_permutation求:

//
// Created by jt on 2020/9/26.
//
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    /**
     *
     * @param n int整型
     * @param k int整型
     * @return string字符串
     */
    string getPermutation(int n, int k) {
        // write code here
        vector<char> vec;
        for (int i = 1; i <= n; ++i) vec.push_back(i+'0');
        while (--k > 0 && next_permutation(vec.begin(), vec.end())) {}
        return string(vec.begin(), vec.end());
    }
};
发表于 2020-09-26 18:30:23 回复(0)
方法1(暴力破解):先进行全排列,然后返回对应的第k-1元素就行了
#
# 
# @param n int整型 
# @param k int整型 
# @return string字符串
#
class Solution:
    def perm(self,s,list1,totallist):
        if len(list1)==0:
            temstr=""
            for i in s:
                temstr+=i
            totallist.append(temstr)
            return
        for i in range(len(list1)):
            self.perm(s+list1[i],list1[:i]+list1[i+1:],totallist)
    def getPermutation(self , n , k ):
        # write code here
        if n==0:
            return ""
        list1=[str(i) for i in range(1,n+1)]
        totallist=[]
        self.perm('',list1,totallist)
        return totallist[k-1]


编辑于 2020-09-21 17:35:27 回复(0)
import java.util.*;


public class Solution {
    /**
     * 回溯法
     * @param n int整型 
     * @param k int整型 
     * @return string字符串
     */
    private  String str = "";
    private  int pos;
    public String getPermutation (int n, int k) {
        if (n <=0 || k <= 0){
            return null;
        }
        pos = k;
        backTrack(n,new ArrayList<>());
        return str;
    }
    
    public  void backTrack(int n, ArrayList<Integer> list){
        if (list.size() == n ){
            pos--;
            if (pos == 0){
                for (Integer integer : list) {
                    str += integer;
                }
            }
        }else if (pos > 0){
            for (int i = 1; i <= n ; i++) {
                if (!list.contains(i)){
                    list.add(i);
                    backTrack(n,list);
                    list.remove(list.size() - 1);
                }
            }
        }
    }
}


发表于 2020-09-19 17:19:04 回复(0)
class Solution {
public:
    string getPermutation(int n, int k) {
        vector<int> factorial(n+1,0);//记录1~n的阶乘
        factorial[0]=1; //特别注意的是0的阶乘是1,也就是说当到了最后一层时,可供选择的方案是1而不是0,这点一定要注意
        for(int i=1;i<n+1;i++) {
            factorial[i]=factorial[i-1]*i;
        }
        set<int> ST;//记录哪些数以及在递归路径上了
        string s="";//
        int deep=1;//记录处在递归树的第几层
        Recursive(deep,k,n,s,factorial,ST);
        return s;
    }
    void Recursive(int deep,int k,int n,string &s,vector<int> &factorial,set<int> &ST) {
        if(s.length()==n) {
            return ;
        }
        for(int i=1;i<n+1;i++) {
            if(ST.find(i)!=ST.end()) continue;
            ST.insert(i);
            if(factorial[n-deep]>=k) s=s+to_string(i);//当某层的单个分支大于等于剩余的k,就将这一层存入
            if(factorial[n-deep]<k) {//当某层的单个分支小于剩余的k,就切换到下一个分支,注意切换到同一层其他分支时要,注意将当前分支的根节点从ST中移除ST
                k-=factorial[n-deep];
                ST.erase(ST.find(i));
                continue; 
            }
            Recursive(deep+1,k,n,s,factorial,ST); //当某层的单个分支大于等于剩余的k,进入下一层
        }
    }
};

发表于 2020-06-22 13:30:51 回复(0)