LeeCode 231 - daily01

有为有不为,知足知不足
锐气藏于胸,和气浮于面
才气见于事,义气施于人
— 丰子恺 ​​​

业精于勤…


题目描述:

给定一个整数,编写一个函数来判断它是否是 2 的幂次方。

Leecode 231 该题在小米公司微软公司都曾被面过。

但是该题在面试过程中,真的不容易想到最优解。不过算法是一步一步演进的,不可能一步到位。

1. 普通解法

public boolean isPowerOfTwo(int n) {
        if(n <= 0) return false;
        while(n %2 == 0){
            n = n >> 1;
        }
        return n == 1;
    }

很显然,时间复杂度为O(logN)


2. 最优解

 public boolean isPowerOfTwo(int n) {
        if (n <= 0) return false;
        return (n & (n - 1)) == 0;
    }

关键点在于:(n & (n-1) )

 public boolean isPowerOfTwo(int n) {
    if (n == 0) return false;
    return (n & (-n)) == n;

关键点在于:(n & (-n) )

3.为何最优?

对于刷题有一定经验的人,见到2立刻就会联想到位运算

二进制中除了最右边的的1,其它位置上的1都表示2.

n原码反码补码
1101
2100111
3011100101
4100011100
5101010011
3.1 知识储备
  1. 负整数是以补码形式存储的;
  2. 补码 = 反码 + 1 ;
  3. 反码 = 原码按位取反;

PS:对于原码,反码,补码不太清楚的,请自行学习了解。

思路回到这道题上,判断一个数是否是2的幂,这就需要我们找一下规律啦。在现实的面试中,面试官是会给我们一定的思考时间的。

n原码
11
210
4100
81000

我们可以发现:如果是2的幂,那么其二进制表示中有且仅有一个1,有0的话后面会跟一些0

换个写法,我们会发现更多的秘密。

n原码
100000001
200000010
400000100
800001000

这样更符号在计算机中的存储规律,同时我们也发现:1的位置总是出现在最右边

那么题目就可以转化为求最右边的1

这里想一下原码,补码之间的相同点其实就是最右边的1.

n原码补码反码原码&补码
100000001111111111111111000000001
200000010111111101111110100000010
300000011111111011111110000000010
400000100111111001111101100000100
800001000111110001111011100001000

注意观察: n & (-n) 就会保留最右边的1,同时将其它位设置为0

PS : 这里列举出3,就是为了 最后的公式:n & (-n ) == n

最方便的其实是看这个:

n原码补码
111
21010
4100100
810001000
  1. 反码 = 原码按位取反;
  2. 补码 = 反码 + 1 ;
  3. n & (-n) 会保留最右边的1,同时将其它位设置为0
  4. 我们还可以发现:2的幂的数原码 和 补码 一样

3.2 延伸解法

根据上面的条件,我们肯定会猜想:

能不能把最右边的1给消灭掉?

事在人为嘛,因为二进制减一可以将最右边的1变成0,且右边的0变为1

n原码(二进制)n-1的二进制n & (n-1)
1000000010000000000000000
2000000100000000100000000
3000000110000001000000010
4000001000000001100000000
8000010000000011100000000

我们都知道:

2的幂的数1在最右边,后面跟的全是0.

这样的话,刚好跟上面的操作是互斥操作。

我们可以就可以采用 n & (n-1) == 0 来作为判断条件。

4. 总结一下

  1. 与2相关的算法题,可以优先考虑位运算
  2. 负整数在计算机中以补码形式表示;
  3. n & (-n) 可以获取到二进制中最右边的 1,且其它位设置为 0
  4. n-1 可以将最右边的 1 设置为 0,并且将右边其它位设置为 1
  5. n & (n-1) 可以将最右边的 1 设置为 0

Good Luck ~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值