输入一个数字n,如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数,写出一个函数。
初次看到这个题的时候,觉得不是越快减完越好么??每次奇数都减去1不就可以?
图样啊~
仔细想想又不对,能除以2的时候是最爽的,而且最好能一直除下去。。那么问题来了,怎么样在遇到奇数的时候,判断我是要加1还是减1,才能够让后面通畅无阻呢。。。
每次遇到2的x次方的时候,尽量靠近它? 要是发现它差的不是1,是3,5...呢。。那不是后悔前面的步数应该早点铺垫。。。在网上搜解答的时候,大家都是通过递归,还有存储计算结果的思路去解。隐隐约约总是觉得这事有点不科学。。。
——————想看答案在下面—————
直到今早在开(滑板)车的时候突然想通了!哈!!
之前一直在想能被2整除的数字,这到底长的像什么玩意呢?0,2,4,6,8。。。看不出头绪,有人可能会说就是偶数啊(废话,我是说2和4长得不像,6比他们更帅,666)
但是我们如果换在二进制的世界里面,他们是长这样的:
0 - 0
2 - 10
4 - 100
6 - 110
8 - 1000
来两个奇数看看?
7 - 111
9 - 1001
是不是?发现了他们的亲属关系?偶数都是以0结尾的。那么在被除以2的时候发生了什么事情?比如
8 --- 1000
4 --- 100
2 --- 10
1 --- 1
看见了么,0被一个个吃掉了。。你如果还记得一点高中数学,这个叫做相位右移,二进制相位右移等价于除以2,就好像十进制我们砍掉最后一个0相当于除以10(是不是很有道理啊)
所以接下来的事情就好办,假如遇到了一个二进制的奇数,你会选择加1还是减1?
10001111
加1 : 10010000 (爽了,前面有4个0可以连吃过去)
减1 : 10001110 (哎,吃完一个0前面还有一个1,再后面还有两个,神烦)
为了让接下来的路更好走果断选择加1!
如果是 110100001 呢,当然减1好了,现在不消灭1,往后也得消灭,出来混总是要还的。。。
所以最佳的策略就是,遇到奇数时,如果下一位是一个1,则选择加1把前面的道路推平,如果前面是0,则减1,不要自找麻烦。
js的实现:
const fn = (n, steps=[]) => {
if(n==1){
console.log("消灭n的步骤为 ", steps);
return steps.length;
};
if(n==3){
//3的二进制是11,到达10的最快方式是减去1
steps.push("-1");
return fn(n-1, steps);
}
let binaryArray = parseInt(n, 10).toString(2).split("");
if(!parseInt(binaryArray.pop())){ //取出最后一位数字,如果为0则执行除以2
steps.push("/2");
return fn(n/2, steps);
}else if(parseInt(binaryArray.pop())){ //取出倒数第二位数字,如果为1,则执行加1,否则减1
steps.push("+1");
return fn(n+1, steps);
}else{
steps.push("-1");
return fn(n-1, steps);
}
}
网友评论