想了解更多数据结构以及算法题,可以关注微信公众号“数据结构和算法”,每天一题为你精彩解答。也可以扫描下面的二维码关注
问题描述
给一个函数f(x),返回一个不小于x的最小的2的n次方。描述的比较绕口,举个例子。
f(7)=8
f(9)=16
f(30)=32
f(64)=64
注意:
- x是正整数
- 0<x<Integer.MAX_VALUE
循环解决
我们看到返回的结果都是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次方,无论你传入的大小是多少,他都会通过上面的计算。