序列的第一个元素是1
即A[0]=1
A[i+1]
可以是2A[i]
或A[i]+1
。
我们必须找到最短的序列。
例如:
If N is 18
A[0]=1, A[1]=2,4,8,9,18
所以我的代码基本上就是。
int count = 0;
for (int i = N, i != 1;){
if (i % 2 == 0) {
i /= 2;
++count;
} else{
--i;
++count;
}
return count;
正如您所看到的,此算法非常简单,并且将返回最短的长度。但是,最差的时间复杂度是O(N)
有没有办法让它O(logN)
?
答案 0 :(得分:2)
您的算法已经是O(log(N))。要看到这一点,您的算法可以像这样重写:
int count = 0;
for (int i = N; i != 1;) {
if (i % 2 == 1) {
--i
++count;
}
i /= 2;
++count;
}
return count;
对于每个位,您要么除以2,要么减去1并除以2。由于每位操作的次数不依赖于N的大小,因此时间为O(log(N))。
如果你考虑二进制。乘以2是将位左移一位。添加一个是将最右边的位设置为1。因此,只需读取N的二进制值即可找到操作序列。
N = 18是二进制的10010,所以我们有
1 = starting value: 1
0 = multiply by 2 : 2
0 = multiply by 2 : 4
1 = multiply by 2 and add one: 8,9
0 = multiply by 2 : 18
解决方案不一定是唯一的,但它始终至少与任何其他解决方案一样短。要了解原因,您只需要观察连续两次添加相当于将第1位添加到第1位并将第0位归零:
xxx01 -> xxx10
但是你可以通过先添加一个来获得相同的结果,所以连续两次添加一个没有任何好处。因此,最佳操作序列包括重复乘以2并且可选地在每次乘法之间加1。