目录
题目
给你一个正整数
n
,开始时,它放在桌面上。在 天内,每天都要执行下述步骤:
- 对于出现在桌面上的每个数字
x
,找出符合1 <= i <= n
且满足x % i == 1
的所有数字i
。- 然后,将这些数字放在桌面上。
返回在 天之后,出现在桌面上的 不同 整数的数目。
注意:
- 一旦数字放在桌面上,则会一直保留直到结束。
%
表示取余运算。例如,14 % 3
等于2
。
示例 1:
输入:n = 5 输出:4 解释:最开始,5 在桌面上。 第二天,2 和 4 也出现在桌面上,因为 5 % 2 == 1 且 5 % 4 == 1 。 再过一天 3 也出现在桌面上,因为 4 % 3 == 1 。 在十亿天结束时,桌面上的不同数字有 2 、3 、4 、5 。
示例 2:
输入:n = 3 输出:2 解释: 因为 3 % 2 == 1 ,2 也出现在桌面上。 在十亿天结束时,桌面上的不同数字只有两个:2 和 3 。
提示:
1 <= n <= 100
题目分析
先来分析一下题目的意思:
题目给定了一个正整数n,作为桌面上最先存在的数字。
题目给定了一个天数:,可以理解为循环的次数。
题目也指明了循环体执行的内容:对于桌面上的每一个数字x,找出一个i满足条件:x % i == 1,i∈[1,n],并将找到的这个i放入桌面。
最后要求返回所有循环结束后,桌面上不同数字的个数。
这边需要注意的是,返回的是桌面上不同数字的个数,也就是说如果将桌面上的这些数放入一个集合中,返回的是集合中元素的个数(因为集合中的元素具有不可重复性)。
tip:返回的不是仅出现一次的数字的个数(题目看快了很容易误以为是这个要求)。
解题思路1——暴力破解法
分析完题目后,题目的意思是很简单的,解题流程大致如下:
但是,这样一来,循环次数固定是次,每次可能会产生若干个符合条件的数字放在桌面上,因此代表桌面的数组array的容量和循环次数是处于一个数量级的,这么一来,加上每次循环还要完整遍历一次数组,这时空复杂度想想就恐怖。
因此它虽然可以得到答案,但是这么做必然会产生时间超限或者空间超限的问题。
解题思路2—— 解决暴力破解法下空间复杂度太高的问题
既然暴力破解法下空间复杂度十分的恐怖,那么必然存在一种方法来降低空间复杂度。
此时想到,返回值等价于将桌面看作一个集合时集合中元素的个数,那么集合中元素具有不可重复性的特点,因此,每次将符合条件的数字放入桌面时,首先判断一下桌面中是否已经存在一摸一样的数字,如果存在便不放入桌面,如果不存在再放入桌面,这样一来,便可以很精确地让桌面符合集合的特性,最后返回桌面中元素的个数即可,因为这样一来桌面上的数字必然是互不相等的。
但是,此时并不知道最后集合的容量是多少,因此这么做的话需要借助一些容量可变的容器来存放数字,或者每次放入的时候进行一次数组扩容。
即便这样,空间复杂度也比暴力破解法下降低了不少。
程序的大致流程如下:
解题思路3——解决暴力破解法下时间复杂度过高的问题
接着进行分析。
上面的思路中,虽然降低了空间复杂度的量级,但是并没有对时间复杂度产生太大的影响,因此必须对题目进行再次分析,以寻找降低时间复杂度的方法。
在上面的思路中,可以发现,如果每次都完整遍历集合时,是存在重复操作的,因为n的值由题目中给出,是一个固定值,因此每一轮循环中,i的范围也是固定的,这就导致每一次遍历集合中在上一轮循环中已经访问的元素时,会得到一样的结果,而这些结果必然不会被放入集合中,因此使用在之前循环中已经访问过的数进行计算必然不会对集合产生任何影响,反而会变成存在了大量的无用计算。因此,在一轮循环中,真正需要进行计算的是集合中没有被访问过的元素。
这样一来,就可以给集合的每一个元素设定一个访问标记,每一轮循环只对集合中未被访问过的元素进行计算,当集合中所有的元素都被访问过时,便结束循环,返回集合中元素的个数。
做到这里,整个算法的时间复杂度和空间复杂度已经较暴力破解法大幅下降了。
解题思路4——时空复杂度为O(1)的算法
接着分析下去,题目中有这样一个表达式:
x % i == 1
那么这个表达式便等价于:
(x - 1) % i == 0
即x-1这个数字可以被i整除,也就是说,x-1必然是i的整数倍,即x必然为i的整数倍+1。
也就是这样:
x = factor * i + 1 其中,factor∈N,i∈[1,n]
那么,当factor = 0,i = 1时,x可以获得最小值1。
但是,就从上面的等式来看,x貌似是没有上限的,但是深入分析一下x:
x的初始值为n,每一次添加进桌面的元素为符合条件的i,而i∈[1,n],因此x的最小值为1,最大值为n。
因此,x存在一个最大值n。
通过上面的分析,可以十分肯定地确定x的范围:x∈[1,n]。
这个结论很有用,但是现在这个结论是没有办法解题的,因为这个范围十分的准确但并不精确。
对于n=1而言,无论循环多少次,1%1永远只会得到一个0(i=1永远不会满足条件),因此对于n=1而言,x永远只有一个元素1。
对于n=2而言,无论循环多少次,2%1永远只会得到一个0(i=1永远不会满足条件),因此对于n=2而言,x永远不可能为1。
对于n=3而言,无论循环多少次,3%1永远只会得到一个0(i=1永远不会满足条件),因此对于n=3而言,x永远不可能为1。
......
对于任意一个值大于1的n而言,无论循环多少次,n%1永远只会得到一个0(i=1永远不会满足条件),因此对于任意一个大于1的n而言,x永远不可能为1。
因此,精确地表示x的范围为:
那么,当n=1时,桌面上不同整数的数目只有1个。当n≥2时,桌面上不同整数的数目最多有n-1个。
注意这边使用的是最多,因为此时对于n≥2时的情况,并不能直接获得答案。
下面来分析一下这个结论:
当x=2时,不可能获得任何符合条件的i值。
当x=3时,获得的符合条件的i值为2。(3 % 2 = 1)
当x=4时,获得的符合条件的i值为3。(4 % 3 = 1)
当x=5时,获得的符合条件的i值为4和2。(5 % 4 = 1)(5 % 2 = 1)
...
这样看,好像看不出来什么,那么接下来换一种分析方式:
当x=2时,不可能获得任何符合条件的值。
当x=3时,必定存在一个i=2。
当x=4时,必定存在一个i=3。
当x=5时,必定存在一个i=4。
...
当x=n时,必定存在一个i=x-1。
那么,这个结论很有用,使用这个结论,可以接着向下分析:
对于任意一个x=n(n≥3),必然存在i=x-1。(因为n=2时是不存在任何有效的i的)
对于上面得到的n-1,必然由于n-1≠n而可以被添加入集合,因此必然有x=n-1,而此时必然可以得到一个i=x-1=n-2。
而对于上面得到的n-2,必然由于n-2≠n-1≠n而可以被添加入集合,因此必然有x=n-2,而此时必然可以得到一个i=x-1=n-3。
下面以此类推,可以得到:x={n} => x={n,n-1} => x={n,n-1,n-2} => x={n,n-1,n-2,n-3} => x={n,n-1,n-2,n-3,n-4} => ... => x={n,n-1,n-2,n-3,n-4,...,5} => x={n,n-1,n-2,n-3,n-4,...,5,4
} => x={n,n-1,n-2,n-3,n-4,...,5,4,3} => x={n,n-1,n-2,n-3,n-4,...,5,4,3,2}
那么,此时可以确定,当n≥2时,集合中的元素个数必定为n-1个。
综上所述:
到这一步,这一题的最佳解法便是这一个分段函数,这个结论经过缜密的推导而得出。
因此,这一种解法的时空复杂度为O(1)。
总结
这是力扣第330场周赛的第一题,若是可以快速发现此题中存在的数学关系,便可以在很短的时间内写出正确的题解。若是使用暴力破解法,那么便会存在时空超限的问题。
因此对算法的设计很重要。