想了解更多数据结构以及算法题,可以关注微信公众号“数据结构和算法”,每天一题为你精彩解答。
问题描述
给一个函数f(x),返回一个不小于x的最小的2的n次方。描述的比较绕口,举个例子。
f(7)=8
f(9)=16
f(30)=32
f(64)=64
注意:
- x是正整数
- 0
循环解决
我们看到返回的结果都是2的n次方,并且是不小于x的最小的2的n次方。如果把2的n次方转换成二进制我们就会发现,只有一个位上是1,其他位上全部是0,随便举几个数字看一下
所以最简单的一种方式就是通过循环来计算,先用x和1比较,如果大于1,那么1就往左移一位,继续比较……,一直这样下去,直到不小于为止,原理比较简单,来看下代码
public int lowPower(int x) { int n = 1; while (x > n) n <<= 1; return n;}
代码非常简洁,基本没什么难度
位运算解决
再来看下位运算,我们随便举个例子,比如53,他的二进制位如下,如果要找到不小于53的最小的2的n次方,我们只需要把53的二进制位中最左边的1往左移一位,其他的全部变为0即可。
所以一种最简单的方式就是通过移位运算,把53最左边的1全部往右边铺开,就变成了00111111,然后再加1就变成了了01000000。最后来看下代码
public int lowPower(int x) { //这里把最左边的1全部往右边铺开 x |= x >>> 1; x |= x >>> 2; x |= x >>> 4; x |= x >>> 8; x |= x >>> 16; //最后再加1返回 return x + 1;}
但是这里有个小问题就是,如果x本来就是2的n次方,比如x是16,运算的结果就会变成32,与我们实际要求不符。所以这里我们可以先让x-1,然后再进行运算,所以正确答案应该是下面这样
public int lowPower(int x) { //这里把最左边的1全部往右边铺开 //x--; x -= 1; x |= x >>> 1; x |= x >>> 2; x |= x >>> 4; x |= x >>> 8; x |= x >>> 16; //最后再加1返回 return x + 1;}
或者也可以改成这样,当然没有上面的代码简洁
public int lowPower(int x) { if (x == 1) return 1; //这里把最左边的1全部往右边铺开 x -= 1; x |= x >>> 1; x |= x >>> 2; x |= x >>> 4; x |= x >>> 8; x |= x >>> 16; //执行完下面这行代码,x相当于把 //后面的1全部减掉了,只留下最前 //面的那个1,也是2的n次方, //只不过这个是小于原来x的最大的 //2的n次方 x -= x >> 1; //然后我们再把它往左移一位,就变 //成了不小于原来x的最小的2的n次方 return x << 1;}
总结
如果大家经常看源码可能对这个算法比较了解,这是HashMap中专门计算数组长度的,我们知道HashMap是数组加链表的结构,数组的大小就是2的n次方,无论你传入的大小是多少,他都会通过上面的计算。