力扣1672位运算详解 找出两个数字a和b中最大的那一个。不得使用if-else或其他比较运算符

示例:

输入: a = 1, b = 2
输出: 2

一种非常巧妙的位运算方法解决该题 由于有些复杂 单独写下来

首先我们要考虑两种情况 a 和 b之间的加减为不会造成溢出 

所以分为溢出 不溢出

而溢出和不溢出要分为a b同号 a b异号的情况

还有就是两者同号 都为正 都为负的情况

多种情况 看我慢慢道来  代码如下 若看的懂则跳过就好啦

class Solution {

  public int maximum(int a, int b) {

        int k = b - a >>> 31;
   
        int aSign = a >>> 31, bSign = b >>> 31;

        int diff = aSign ^ bSign;

        k = k & (diff ^ 1) | bSign & diff;

        return a * k + b * (k ^ 1);
    }

}

接下里我就详细说明下 

首先是简单的情况

不溢出

当 先通过int k = b - a >>> 31; 无符号右移 来算出最高位 其中负数返回1 正数返回0

①当b为正数 a为负数时

        此时k=0
        int aSign = a >>> 31, bSign = b >>> 31;

       算出aSign=1   bSign=0

       接着通过异或运算   int diff = aSign ^ bSign;  这是为了区分同号还是异号 同号为0 异号为1

        得到diff为1

      k = k & (diff ^ 1) | bSign & diff=0 & (1^1)|0&1=0   这里有一点要提 就是符号运算的优先级问题 可以看下表

 

 

此时我们知道 与 异或 或 是按这个顺序来的 千万不要以为是从左往右的顺序哦!

最后return a * k + b * (k ^ 1)=a*0+b*1=b  最终返回b  这是第一种情况

②当a为正数 b为负数时

int k = b - a >>> 31  b-a为负

此时k=1
        int aSign = a >>> 31, bSign = b >>> 31;

       算出aSign=0   bSign=1

       接着通过异或运算   int diff = aSign ^ bSign;  

       得到diff为1

      k = k & (diff ^ 1) | bSign & diff=1 & (1^1)|1&1=1

     最后return a * k + b * (k ^ 1)=a*1+b*0=a  最终返回a  

③当ab为正 且a>b时

int k = b - a >>> 31  b-a为负

此时k=1
        int aSign = a >>> 31, bSign = b >>> 31;

       算出aSign=0   bSign=0

       接着通过异或运算   int diff = aSign ^ bSign;  

       得到diff为0

      k = k & (diff ^ 1) | bSign & diff=1 & (1^0) | 0 & 0=1

     最后return a * k + b * (k ^ 1)=a*1+b*0=a  最终返回a  

④当ab为正 且a<b时

int k = b - a >>> 31  b-a为正

此时k=0
        int aSign = a >>> 31, bSign = b >>> 31;

       算出aSign=0   bSign=0

       接着通过异或运算   int diff = aSign ^ bSign;  

       得到diff为0

      k = k & (diff ^ 1) | bSign & diff=0 & (1^0) | 0 & 0=0

     最后return a * k + b * (k ^ 1)=a*0+b*1=a  最终返回b

⑤当ab为负 且a<b时

int k = b - a >>> 31  b-a为正

此时k=0
        int aSign = a >>> 31, bSign = b >>> 31;

       算出aSign=1   bSign=1

       接着通过异或运算   int diff = aSign ^ bSign;  

       得到diff为0

      k = k & (diff ^ 1) | bSign & diff=0 & (0^1) | 1 & 0=0

     最后return a * k + b * (k ^ 1)=a*0+b*1=a  最终返回b

⑥当ab为负 且a>b时

int k = b - a >>> 31  b-a为负

此时k=1
        int aSign = a >>> 31, bSign = b >>> 31;

       算出aSign=1   bSign=1

       接着通过异或运算   int diff = aSign ^ bSign;  

       得到diff为0

      k = k & (diff ^ 1) | bSign & diff=1 & (0^1) | 1 & 0=1

     最后return a * k + b * (k ^ 1)=a*1+b*0=a  最终返回a

溢出时

① 负溢出的情况

也就是b为负数 a为正数

k=b-a>>>31  这样不要担心会报错 当负溢出右移31位是返回0  正溢出返回1

所以k=0

 int aSign = a >>> 31, bSign = b >>> 31;

       算出aSign=0  bSign=1

       接着通过异或运算   int diff = aSign ^ bSign;  

        得到diff为1

      k = k & (diff ^ 1) | bSign & diff=0 & (1^1) | 1 & 1=1

      最后return a * k + b * (k ^ 1)=a*1+b*0=a 最终返回a

②正溢出的情况

也就是a为正数 b为负数   k=1

  int aSign = a >>> 31, bSign = b >>> 31;

  算出aSign=1  bSign=0

 接着通过异或运算   int diff = aSign ^ bSign;  

 得到diff为1

      k = k & (diff ^ 1) | bSign & diff=1 & (1^1) | 0 & 1=0

      最后return a * k + b * (k ^ 1)=a*0+b*1=a 最终返回b

 

以上就是所有的情况了  这个算法是真的牛逼!!! 关键是没有用到if else 所以要将那么多情况考虑进去真的很难

 

ps:如有笔误 请联系我O(∩_∩)O

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值