LeetCode 60

    Permutation Sequence

    这个题是求1~n (n[1~9]) 的数字的全排列的第K个序列。

    一般思路是:使用一个计数器,递归去找全排列序列,找到一个计数器加一,一直到第k个。

但是加若 n = 9 我要找的是第 (9! -1 )个数,那么上述办法的时间是多少,多半会超时的(没试过,但是我敢保证一定会超时的,因为这样的思路不可取),想一想我们只需要一个序列,并不必要把全部的序列都找出来吧。下面我给出一种解题方案,我个人感觉是可取的。

    我们是学过数学的人,要我们求全排列的第 k 个序列,那么我们其实可以推测它的大致分布在那个范围(不是用眼睛看,而是推导出来的)。这个方法是这样的:

    假设 n = 3,那么 1~3 组成的全排列以及相应的第K个序列氛围如下:

 k n[1,3]
          1                  1  2  3
          2           1  3  2
          3            2  1  3
          4           2  3  1
          5           3  1  2
          6           3  2  1

    首先说明一下像这种无重复数字的全排列规律:

    n = 1 时,全排列个数为: 1!

    n = 2 时,全排列个数为: 2!

    n = 3 时,全排列个数为: 3!

       .....

       基于这种规律我们是不是可以大致推测k的范围,推测的方法是去找一个数 m,时 m! > k,这样可以说明k的大致位置,画个图说一下思路吧。


     上面所说的解题方法确实不好描述清楚,因为它是数字规律,我是通过过滤一些位置来不断缩小k的值,当缩小到 k = 1的时候所对应的序列就是我们要找的序列,如果还没看懂的话那就是我没有描述好的问题了。所以我教一个方法:

例如:

当:n = 2

      1 2

      2 1

      那么我们交换1和2的位置(交换第一位和第二位的位置),就相当于过滤掉一个序列了,k的值也就减小 1

当:n = 3

      1 2 3

      1 3 2

      2 1 3

      2 3 1

      3 1 2

      3 2 1

    所以当我们交换1和2的位置,我们就过滤了2个序列,相应的k的值也就减少了2,问题缩小为在两个数的全排列中寻找第k个序列

   当我们交换1与3的位置,我们就过滤掉了4个序列,相应的k的值也就减少了 4,问题也缩小为在两个数的全排列中寻找第k个虚列

   .... 所以这样就可以推导到 n = 4,5,6,7...,问题也就会不断缩小,最终缩小为1,问题解决,代码如下,可参考代码理解。

class Solution {
public:
	string getPermutation(int n, int k)
	{ //1~n 的全排列的 第k个数
		string ret;
		ret.clear();
		if (n == 1 && k != 1 || k > factorial(n))
		{
			return ret;
		}

		vector<int> v;
		v.clear();
		for (int i = 1; i <= n; ++i)
		{
			v.push_back(i);
		}

		int begin = 0;
		int pos = 0;

		while (k > 0)
		{
			begin = 1;
			int offset = 0;
			int nums = 0;

			while ((nums = factorial(begin)) < k)
			{ //k分布在第begin位之后
				++ begin;
			}
			nums /= begin;
			pos = n - begin;  //第pos位可能需要交换

			if (begin > n+1)
			{ //不存在,其实是越界了,意思是k比 n!还大,不存在
				return ret;
			}

			int temp = 0;
			int jump = 0;  //去找
			while (temp+nums < k)
			{ //去找需要和那一位交换位置
				temp += nums;
				++jump; //jump指明了该位置的相对距离
			}

			if (jump > 0)
			{
				swap(v[pos], v[pos+jump]);
				//交换位置后需要对后面的序列排序
				sort(v.begin() + pos + 1, v.end());
			}
            //交换后,k的相对位置发生变化
			k -= jump*nums;
		
			if (k <= 1)
			{ //这种情况说明此时就是要找的序列
				for (int i=0;i<n;++i)
				{
					ret.push_back(v[i] + '0');
				}
				return ret;
			}
		}
		return ret;
	}

	int factorial(int n)
	{ //求阶乘
		int ret = 1;
		for (int i = 2; i <= n; ++i)
		{
			ret *= i;
		}

		return ret;
	}	
};
结果如下:


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值