力扣——位运算(之一)

1. 位运算简介

位运算是对二进制数的一种操作,主要以下几种,

(一元)取反(!)、按位或(||)、按位异或(^)、按位与(&&)。

(二元)移位:将一个二进制数中的每一位都向指定方向移动指定位,溢出的部分被舍弃,空缺的部分补充一定值。

这里对移位运算符进行一定的代码解释,方便以后处理这里题目时候翻看:

public static void main(String[] args) {
    int a=4;
    System.out.println(a+" 向左和右移动:");
    System.out.println(a<<2);//a<<2为:a*(2^2),对应二进制数左移两位,缺位正数补0
    System.out.println(a>>2);//a>>2为a/(2^2),对应二进制数右移两位,缺位正数补0
    
    int b=-4;
    System.out.println(b+" 向左和右移动:");
    System.out.println(b<<2);//对于负数而言,缺位补1
    System.out.println(b>>2);
    
    //对于>>>,只对32位和64位有意义
    System.out.println(a+" 和 "+b+"无符号右移:");
    System.out.println(a>>>2);//对于这种符号来说,同样是右移,但是缺位补0,所谓的无符号右移,
    System.out.print(b>>>2);// 所以这里的负数一般会变成正数
}

对应的输出为:在这里插入图片描述

基础知识想了解更多,可以参考:https://www.cnblogs.com/SunArmy/p/9837348.html

2. 在题目中的应用

2.1 最大数值

2.1.1 题目分析

不能使用各种比较运算符来找到a和b中最大的那个。

如果我们能得到a-b的符号位,就可以通过以下函数形式来返回满足条件的数:

//如果a-b>=0,设i=0;否则i=1,这样就可以返回正确的值
return i*b+(i-1)*a;

我们注意到对于二进制数,其首位会显示对应的正负(对于64位long或者32位int型来说),这样我们可以对a-b使用右移的操作,但这里要注意溢出的问题,例如
a = 2 32 − 1 , b = − 2 32 , a − b = 2 33 − 1 ; / / 这 个 时 候 a − b 就 会 超 过 32 位 a=2^{32}-1,b=-2^{32},a-b=2^{33}-1;//这个时候a-b就会超过32位 a=2321,b=232,ab=2331;//ab32
所以我们可以选择long类型来解决这个问题。这样我们可以通过无符号右移63位来获得符号位。

2.1.2 代码

public static void main(String[] args) {
   int a=3,b=6;
   int i=(int)(((long)a-(long)b)>>>63);
   System.out.println(i);
   System.out.print("a和b中最大值为:"+(a*(1-i)+b*i));
}

对应输出为:
在这里插入图片描述

2.2 求1+2+…+n

2.2.1 问题分析

解决这道题可以有两种思路:1.短路运算 2.位运算模拟高斯公式

A&&B是具有短路效果的,也就是如果A不成立,B不会执行,所以可以利用这一个特点来进行递归,另外java不认为数字可以充当逻辑值,同时需要有boolean来充当承载,也就是:

public int sumNums(int n) {
     boolean b = (n > 0) && ((n += sumNums(n - 1)) > 0);
        return n;
    }

当n==0时,函数递归就可以结束,开始不断返回值。

我们同时知道:
1 + 2 + 3 + ⋅ ⋅ ⋅ + n = n ∗ ( n + 1 ) 2 1+2+3+···+n=\frac{n*(n+1)}{2} 1+2+3++n=2n(n+1)
我们可以通过将n+1拆解成2的n次幂数之和,再和n相乘,我们给出示例代码如下:

引用自https://leetcode-cn.com/problems/qiu-12n-lcof/solution/

public int sumNums(int n) {
        int n1 = (n & -(n + 1 >> 0 & 1)) << 0;
        int n2 = (n & -(n + 1 >> 1 & 1)) << 1;
        int n3 = (n & -(n + 1 >> 2 & 1)) << 2;
        int n4 = (n & -(n + 1 >> 3 & 1)) << 3;
        int n5 = (n & -(n + 1 >> 4 & 1)) << 4;
        int n6 = (n & -(n + 1 >> 5 & 1)) << 5;
        int n7 = (n & -(n + 1 >> 6 & 1)) << 6;
        int n8 = (n & -(n + 1 >> 7 & 1)) << 7;
        int n9 = (n & -(n + 1 >> 8 & 1)) << 8;
        int n10 = (n & -(n + 1 >> 9 & 1)) << 9;
        int n11 = (n & -(n + 1 >> 10 & 1)) << 10;
        int n12 = (n & -(n + 1 >> 11 & 1)) << 11;
        int n13 = (n & -(n + 1 >> 12 & 1)) << 12;
        int n14 = (n & -(n + 1 >> 13 & 1)) << 13;
        return ( n1+n2 + n3 + n4 + n5 + n6 + n7 + n8 + n9 + n10 + n11 + n12 + n13 + n14) >> 1;
    }

这里1<=n<=10000。所以只用拆解成13次幂足以。

2.2.2 代码分析

这里我们以n=3举例,二进制为11,

//n+1为二进制的100,也就是4,现在需要判断n+1在0-13位上是否有值,所以
n+1>>k&1;//0<=k<=13,1为00000···001,所以这样就可以通过得到的是1还是0来判断k位上是否有值

(n & -(n + 1 >> k & 1))//因为任何整数和-1进行与运算都会得到本身,当n+1在某一位存在值的
//时候,n会得到它本身;不存在值的时候,会得到0
//然后对这个值进行左移,也就是模拟乘积的过程。

写成数学形式就是:
n ∗ ( n + 1 ) 2 = n ∗ ( f 0 ∗ 2 0 + f 1 ∗ 2 1 + ⋅ ⋅ ⋅ + f k ∗ 2 k ) 2 ( 其 中 f k = 0 o r 1 ) \frac{n*(n+1)}{2}=\frac{n*(f_0*2^0+f_1*2^1+···+f_k*2^k)}{2}(其中f_k=0or1) 2n(n+1)=2n(f020+f121++fk2k)(fk=0or1)

欢迎来我的博客看看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值