leecode 解题总结:60. Permutation Sequence

#include <iostream>
#include <stdio.h>
#include <vector>
#include <sstream>
using namespace std;
/*
问题:
Total Accepted: 74373
Total Submissions: 272450
Difficulty: Medium
Contributors: Admin
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 (ie, for n = 3):

"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

分析:
这道题目实际之前做过。之前一道题目是给定一个排列,返回下一个序列。
现在这道题目没有给定一个排列。如果我们是以第一个排列为基准,那么
时间复杂度其实就是O(n!)。
给定一个排列,下一个排列是略微大于该排列的,比如132下一个排列是213
1】那么需要找到最高位的较大的数字替换,2】并把最高位后面的部分调整为最小的数字
设数组为A
步骤1】:寻找最高位较大的数字,从后向前,找到第一个顺序的即可,即找到A[i] > A[i+1]中的位置i来替代A[i]
步骤2】:下面需要寻找替代A[i]的数字,该数字必须是>A[i]的数字中最小的一个
需要从A[len ... 1]中从后向前寻找第一个大于A[i]的元素,记为A[j],然后swap(A[i],A[j])
步骤3】:将A[i]之后的A[i+1 ... len]元素需要变成最小的数字,此时只需要对A[i+1 ... len]逆置,因为此时的
         A[i+1 ... len]已经是降序排列的,逆置后变成升序,必然是最小的

输入:
3(n) 4(k)
4 14
输出:
231
3142

关键:
1 果然超时。用另外一种方法:以{1,2,3,4}举例,共有24中排列。
其中1开头的排列6种,2排列的6种,...
因此加入给定k=14,从编程角度下标从0开始,则k-1=13,则必定在以3开头的里面,减去1,2开头排列,应该是在去除3之后的
{1,2,4}组成的排列中的第2个。 index = 13 / (4-1)! = 1,对应下标为2,剩余k = 13 - index * (4-1)! = 1,【选择了3,剩余{1,2,4}】
而{1,2,4}重复上述操作,共产生3!=6种排列,其中1开头的占有2个排列。
因此index = 1 / (4-2)! = 0,【选择了1,剩余{2,4}】
剩余k=1 - index * (4-2)! = 1,
    index = k / (4-3)! = 1,【选择了4,剩余{2}】
	k = 1 - index * (4-3)! = 0,
	index = 0,【选择了2,剩余空{}】
	最终3142

2 nums.erase(nums.begin() + index , nums.begin() + index + 1);//注意删除的时候,后面的位置取不到
*/

class Solution {
public:
	//获取排列str下面一个排列
	string getNext(string str)
	{
		if(str.empty())
		{
			return "";
		}
		int len = str.length();
		bool isFind = false;
		int i;
		for(i = len - 2 ; i >= 0 ; i--)
		{
			//从后向前寻找第一个顺序的数
			if(str.at(i) < str.at(i+1))
			{
				isFind = true;
				break;
			}
		}
		//如果没有找到,说明是逆序的,直接返回
		if(!isFind)
		{
			return str;
		}
		//寻找替代A[i]的元素,从后向前找到第一次大于A[i]的元素A[j]
		int j;
		for(j = len - 1; j >= 0 ; j--)
		{
			if(str.at(j) > str.at(i))
			{
				break;
			}
		}

		//交换元素A[i],A[j]
		char ch = str.at(i);
		str.at(i) = str.at(j);
		str.at(j) = ch;

		//逆置A[i+1...len-1]
		reverse(str.begin() + i + 1 , str.end());
		return str;
	}

    string getPermutation2(int n, int k) {
		if(n <= 0 || k <= 0)
		{
			return "";
		}
		stringstream stream;
		for(int i = 1 ; i <= n ; i++)
		{
			stream << i;
		}
		string first = stream.str();
		if(k == 1)
		{
			return first;
		}
		string result;
		for(int i = 2 ; i <= k ; i++)
		{
			result = getNext(first);
			first = result;
		}
		return result;
    }

    string getPermutation(int n, int k) {
		if(n <= 0 || k <= 0)
		{
			return "";
		}
		vector<int> nums;
		for(int i = 1 ; i <= n ; i++)
		{
			nums.push_back(i);
		}
		k--;
		vector<int> fact(n + 1 , 1);
		for(int i = 1 ; i <= n; i++ )
		{
			fact.at(i) = i * fact.at(i-1);
		}
		int index;
		stringstream stream;
		for(int i = 1 ; i <= n ; i++)
		{
			index = k / fact.at(n-i);//计算下标
			stream << nums.at(index);
			//删除已经选中的数字
			nums.erase(nums.begin() + index , nums.begin() + index + 1);//注意删除的时候,后面的位置取不到
			k -= index * fact.at(n-i);
		}
		string result = stream.str();
		return result;
	}
};

void process()
{
	 Solution solution;
	 int n;
	 int k;
	 while(cin >> n >> k)
	 {
		 string result = solution.getPermutation(n , k);
		 cout << result << endl;
	 }
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值