全1函数介绍:
class Solution {
public:
int getOne(int n) {
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n;
}
};
这个算法只适用于正整数或无符号整数
这个算法的原理就是把一个数的补码的最高位1不断向右复制
例:
以无符号整数2147483648为例
他的补码为:
1000 0000 0000 0000 0000 0000 0000 0000
n|=n>>1;
10000000 00000000 00000000 00000000 (2147483648)
| 01000000 00000000 00000000 00000000 (2147483648>>1)
---------------------------------------
11000000 00000000 00000000 00000000 (n|n>>1)
n|=n>>2;
11000000 00000000 00000000 00000000 (更新第一次后的n)
| 00110000 00000000 00000000 00000000 (n>>2)
-----------------------------------------
11110000 00000000 00000000 00000000 (n|n>>2)
以此类推,每一次都会复制一次原先的1到后面的位上,而一个数的二进制位数一共是logn个
而这个算法是在二进制位上进行的复制操作,每一次操作过后1都会翻倍占据。
因此,这个算法的时间复杂度是以2为底的O(loglog(n))但是基本可以视为O1的时间复杂度
在数值上,这个算法所计算出来的值是2的(log(n)+1)次幂-1
但对于负数来说,结果一定是-1
利用这个数值上的特性,使用内置函数log2可以实现以一个真O1时间复杂度的代码
int getOne(int n) {
return (1 << ((int)log2(n) + 1)) - 1;
}
但是请注意,从理论上讲,位运算一般都比浮点运算要快,因为位运算是直接作用在数的二进制表示形式上。尽管log2是高效的,但是log2涉及浮点运算,而浮点运算更复杂,涉及到位表示的解析、指数和尾数的运算等,因此,尽管从时间复杂度上来说使用log2的实现略胜一筹,但实际情况下,这个实现的性能可能与单纯使用位运算的实现平分秋色。
实例:
输出大于等于n的最小的2的幂
输入格式:n>0,n为整数。
int pow2uper(int n) {
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n++;
}
针对这个解决,一共有两种情况需要处理:
①n不是2的幂,n--不会改变这个数的二进制位数,所以经过全1化之后,n会变成2的(log(n)+1)次幂-1,然后再加1就得到大于等于n的最小的2的幂
②n是2的幂,结果就是n本身,n--先降位,然后全一化n会变成2的(log(n))次幂,然后再加1得到结果。