LeetCode笔记——60第K个排列

题目:

给出集合 [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,  n!]。

示例 1:

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

示例 2:

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

思路:直接看了网上大神的代码。按照程序可以一步步推导出结果,但是原理还是不太懂。。。。。。这个代码我看了下好像是类似于康拓展开。康拓展开是一个全排列的自然数的双射,实质是计算当前排列在所有由小到大全排列中的顺序。逆康拓展开可以求出全排列中给定顺序的排列。

以下是摘自百度百科的一个例子:

 

 

在(1,2,3,4,5)的全排列中求出顺序为61的组合(即34152)。由上述的计算过程可以容易的逆推回来,具体过程如下:

用 61 / 4! = 2余13,说明     说明比首位小的数有2个,所以首位为3。

用 13 / 3! = 2余1,说明,说明在第二位之后小于第二位的数有2个,但是因为3已经被放在首位,所以第二位为4。

用 1 / 2! = 0余1,说明,说明在第三位之后没有小于第三位的数,所以第三位为1。

用 1 / 1! = 1余0,说明,说明在第二位之后小于第四位的数有1个,所以第四位为5。

最后一位自然就是剩下的数2。

通过以上分析,所求排列组合为 34152

代码:

class Solution {
   public String getPermutation(int n, int k) {
        int[] factorial = new int[n];
        
        //因式分解需要的基数  即每一步应该除去的阶乘
        for (int i = 0; i < n; i++) {
            if (i == 0) {
                factorial[i] = 1;
                continue;
            }
            factorial[i] = factorial[i - 1] * (i);
        }
       
       StringBuilder res = new StringBuilder(); %存放最后结果
        boolean[] used = new boolean[n];  %用来标记数据,以便确定哪一个数已经被使用过了
        int i = n - 1;
        while (i >= 0) {
            int digit = (k - 1) / factorial[i];//变换关系k-1  用来确定每一位的数,小于该数的数字的个数为digit
            res.append(findKth(used, digit));//先取最高位的值  确定是哪一个数,排除已经出现过的
            k -= digit * factorial[i--];  %确定下一个数
        }
 
        return res.toString();
    }
    //再次强调下,数组是用的地址,而我们传递的对象就是普通的参数
    public int findKth(boolean[] used, int digit) {
        int res = -1;
        while (digit >= 0) {
            if (!used[++res]) { //从小到大的去取值,同时进行标记
                digit--;
            }
        }
        used[res] = true;
        return res + 1;//从0-4,变为1-5
 
    }
}

 

执行最快的代码:

最快的代码还是康拓展开的思想,只不过细节的处理不同。

class Solution {
    public String getPermutation(int n, int k) {
        
		StringBuilder sb=new StringBuilder();
		int num=k;
		int factorial=calFactorial(n);

		StringBuilder s=new StringBuilder();
		for(int i=1;i<=n;i++){
			s.append(i);
		}

		for(int i=n;i>=1;i--){
		
			factorial/=i;
			int index=(num-1)/factorial;
			String tmp=""+s.charAt(index);
			sb.append(tmp);
			num-=index*factorial;
			s=s.deleteCharAt(index);
		}
		return sb.toString();
    }

	public int calFactorial(int n){
		int ans=1;
		for(int i=1;i<=n;i++){
			ans*=i;
		}
		return ans;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值