笔试题73. LeetCode OJ (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;
	}
};

结果如下:

时间: 2024-10-19 04:05:22

笔试题73. LeetCode OJ (60)的相关文章

笔试题28. LeetCode OJ (15)

这个题确实比较复杂,我刚刚开始的思路是先将数组排序,然后从左向右遍历,然后用两个变量lpos,rpos分别指向left+1 和 nums.size()-1,然后求三者的和,若和sum < 0 则让lpos加1,若sum>0则让rpos减1.想法不错,可是现实很残酷.这样 的解很容易错过真实解,我测试了很多遍,总有测试用例无法通过.其中还有一个时间复杂度太高了也没通过测试.这个题大家 可以自己去实现试试,真的错误点太多了,最后还是采用了比较老实的办法,再一步一步分析,求的解如下: class S

笔试题52. LeetCode OJ (39)

如果还记的话或者写过LeetCode的人会知道,这是nsum,已经不再是之前的twosum,threesum,foursum了.之前是都是使用 i 层循环解题的,那么现在n层循环理论上是可以解题的(其实应该不行),但是n是不确定了,而且是可变的,其变化范围是[1,n] 说道这里其实想说明的是,我们要换种思路去解题了.我在看到这个题的时候想到的思路是: 我们从小到大一个一个将最小的数字重复不断的加入到 vector 中,若vector现有的值的和比 target 大则将 vector 集合中的元素

笔试题85. LeetCode OJ (71)

              Simplify Path     这个题是给出一个字符串的相对路径,让我们返回绝对路径,让我们在Linux下自己去动手操作确实不难,可是用程序做起来的话确实比较麻烦. 我的解题思路是找好'/'和'.'之间的关系,找好他们之间的组合关系后这个题就容易一些.在路径中'.'和'/'的组合正常的可能只有'.'和'..'两种.'/'是起修饰作用的,说明当前目录下可能还存在目录. 既然我们要写这个题的话,那么肯定会出现各种组合的情况,其实我们只要处理好两种正常的情况就差不多了,

笔试题36. LeetCode OJ (23)

合并K个排序链表,没错.我的思路是分别从 K 个链表的中找出最小的一个值,然后依次插入链表,最后遍历完所有链表就好了,也没想中的那么难呀,循环遍历的思路如下: (先说明一下,它是不合格的) 主要思路是: 1.首先从所有排序链表里找一个最小val节点作为头结点 2.依次循环在各个链表里面找到最小节点摘下来尾插,循环结束的条件是当链表的数目为0或者为1的时候结束(可以通过lists[i]==NULL来判断lists[i]代表的链表是否结束了),还编写了一个函数用来找最小元素下标的,每次返回的list

笔试题30. LeetCode OJ (17)

这个题目有意思,和生活接近,题目的意思一看就知道,应该是字符串的全排列吧.但是需要注意的是有几数字字符是没有对应的字符串的,比如'0'和'1',所以我们的输入字符串中若有他们,则将它们过滤掉.这个题我的思路是递归,因为这种类型的题目递归思路清晰明了,若要使用循环,则会形成n层循环,所以循环的思想应该被淘汰掉.我在做这个题的时候喜欢先把"多余"的东西先给它解决掉,比如'0'和'1'这两个数字,我先遍历一边digits数组,将'0'和'1'过滤掉,这样在后面的递归中可以少考虑一些东西,自认

笔试题74. LeetCode OJ (61)

    Rotate List    这个题的意思旋转链表,更具体点的意思右移链表,移出去的节点放到头部前面,结合着题目给出的例子还是很好理解的. 这个题的主要思路是:摘取从末尾到头的k个节点,然后将他们放到头部. 需要注意的是,上面说的k并不一定等于传入的k的值,因为这个k很可能比链表的长度还大.所以我主要思路是:遍历一遍链表,找到链表的长度n,然后k%=n(这时候k<n,我们更喜欢的是此时k=0),这样就可以找出实际需要移动的节点的个数,然后将链表的最后k个节点放到链表的前面就行了.大概方法

笔试题72. LeetCode OJ (59)

Spiral Matrix II 看上图就能知道这个提示要干什么的,给定一个 n 值,按照螺旋数组的存储特点将 1~N^2 存放到螺旋数组中. 思路:使用一个计数器(引用的方式使用),然后按照螺旋数组的方式去遍历的特点依次将该计数器的值赋给数组相应的位置,遍历完成后就是上述的样子了了,需要注意一下几点. 1.我们需要实现分配空间(讲vector的大小给定),否则肯定会崩溃的... 2.还是需要注意螺旋数组中一下常见问题,如重复问题,越界问题等等 代码如下: class Solution { pu

笔试题33. LeetCode OJ (20)

看到这个题的时候我们是否会记起点什么呢?是不是很熟悉的感觉呢,没错就是括号匹配问题.我们知道后会立马想起一个数据结构---栈. (1).我们需要借助一个栈来保存括号的左边部分找到右边的部分时,找出栈顶元素,若两者匹配,则删除栈顶元素,继续下一轮遍历. (2).如果当前元素是括号的右边部分,但是却不合栈顶元素匹配,则说明整个匹配失败. (3).需要注意的是,栈空的情况.最后是通过栈是否为空来判断是否全部匹配,但是若找到右边括号的时候,栈是空的,那么我们也是可以直接返回false的. 代码如下: c

笔试题32. LeetCode OJ (19)

终于碰见了和数据结构相关的题目了,这也意味着复杂度在增加.来看看这道题吧,删除链表的倒数第k个节点,我们有必要先找出链表的长度然后再确定倒数第k个节点吗?我觉得这是没必要的.因为我们的方法比这样更巧,听起来高大尚一些.解题思路以及注意事项如下: 1.我采用的是两个指针,一快一慢,快的先走k+1步,然后快指针和慢指针一起走,直到快指针走到尾 2.为什么要快指针要走k+1步呢?我们仔细想想一下,我们是通过慢指针来决定要删除的节点的,如果慢指针刚刚好指在要删除的节点上,那么恭喜你,你需要再遍历一遍链表