LeetCode 第k个排列(康托展开)

本文详细解析了求解给定整数n和k时,如何找出集合[1,2,...,n]中第k个排列的算法。通过对比回溯法与康托展开法,介绍了两种算法的具体实现过程,探讨了它们的时间复杂度和适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给出集合 [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"

思路分析:请先查阅 LeetCode 全排列
本算法采取回溯法进行搜索,使用一个变量记录当前搜索到的一个排列,当搜到的位所需要的时候停止搜索,返回排列结果。

class Solution {
public:
	vector<bool> useFlag;//使用标记容器
    int cnt;//标记个数
	bool permuteDFS(string &tempRes, int steps, int n, int k){
		if (steps == n){//已经搜索完了一趟
			++cnt;
            if (cnt == k){//找到了第k个
                return true;
            }
		}
		else {
			for (int index = 1; index <= n; ++index){//每次都进行穷举
				if (useFlag[index] == false){//没使用过
					tempRes += ('0' + index);
					useFlag[index] = true;//标记使用
                    if (permuteDFS(tempRes, steps + 1, n, k)){//进行下一步寻找
                        return true;//如果搜索到了,停止搜索
                    }
					useFlag[index] = false;//重点。标记回未使用
					tempRes = string(tempRes, 0, tempRes.size() - 1);
				}
			}
		}
        return false;
	}
    string getPermutation(int n, int k) {
        useFlag = vector<bool>(n + 1, false);
        cnt = 0;
        string tempRes = "";
        permuteDFS(tempRes, 0, n, k);//回溯法搜索
        return tempRes;
    }
};

在这里插入图片描述
虽说是回溯法,但是当n,k比较大时,时间复杂度非常大。
经百度发现这是个非常经典的问题,数学术语为“康托展开”。请参考百度 康拓展开
具体实现代码

class Solution {
public:
	string getPermutation(int n, int k) {
		vector<int> numFactorial = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };//用于储存i的阶乘
		vector<bool> useFlag(n + 1, false);//标记i是否使用过
		string result = "";
		k -= 1;//需要减去自身
		for (int i = n - 1; i >= 0; --i){//从高位到低位进行寻找
			int temp = k / numFactorial[i];//商
			k = k % numFactorial[i];//更新为余数
			int cnt = 0;
			//在还未使用的数中,找第temp大的数
			for (int j = 1; j <= n; ++j){
				if (useFlag[j] == false){//未使用,计数
					++cnt;
				}
				if (cnt > temp){//找到了
					result += to_string(j);
					useFlag[j] = true;
					break;
				}
			}
		}
		return result;
	}
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值