目录

  • 前言
  • 运算符
  • 异或
  • 题目(一)
  • 解析
  • 题目(二)
  • 解析


前言

    咱先唠唠嗑,上来就是知识点,多少有些枯燥无味了。当然,可以选择跳过。
    话说为什么我们要去用位运算呢,这不是计算机该做的吗?存在即合理:
            1.因为相同的运算,位运算实现比数学运算符实现速度要快,计算机底层就是二进制位运的算,而使用数学运算符,还要将其转换成位运算,这其中就存在一定的开销。虽然速度相差不大,但是当数据量越大时,形成的差距也会越大。搞算法嘛,无非就是想让程序尽可能快的解决问题。
            2.位运算可以更方便的利用二进制。说这个可能有些伙伴会有点懵逼,没关系,稍后的讲解中会领略到位运算的奇妙。

运算符

Java中的位运算符:
    1.位与:&
    2.位或:|
    3.位非:~
    4.异或:^
    5.左移:<<
    6.右移:>>
    7.无符号右移:>>>
当然还有与之对应的&=,|=等等

值得注意的是

    1.无符号右移:>>>在c++中是不存在的,无符号右移左端直接补零,而普通的右移左端补符号位。
    2.位运算只能给整数做运算,float和double是不行的。
    3.对于btye,short,int这三种类型,i<<k等价于i<<(k%32),伙伴们可以去试试,而对于long类型而言,i<<k等价于i<<(k%64),这是java规定的。

常见操作
       这里介绍一些常用的小技巧,可以稍微记一下。但是为了控制篇幅,这里就不进行过程推导了(其实也就4和5需要演算一下才好理解,其他都比较简单)

a<<1				a*2
a<<1|1				a*2+1
a>>1				a/2
a&((~a)+1)			只保留a的二进制中最低位的1
a&(a-1)				将a的二进制中最后一位1置零(可以用于判断a是否是2的n次方)
a>>b&1				得到a的二进制中右边第b+1位
a&1					判断奇偶,奇数为1,偶数为0
a^=b;b^=a;a^=b;		交换a,b。注意a和b不能指向同一个内存单元,比如对数组中的同一个元素自我交换



异或

在算法题中,用的最多的莫过于“异或”了,所以下文的重点就是异或运算符。
异或的运算规则

1^1=0
0^0=0
1^0=1
0^1=1
总的来说相同得0,不同得1

首先我们来看看异或的运算律:

1.归零律:a^a=0
2.交换律:a^b=b ^a
3.恒等律:a^0=a
4.结合律:a^b ^c=a ^(b ^c)=(a ^b) ^c
5.自反律:a^b ^a=b
6.d=a^b ^c 可以推出a=d ^b ^c

推导起来很简单,大家可以试一下。

上面讲了这么多,还是要实践一波才得劲呀。

题目(一)

题目:
给定一个数组,数组元素中仅存在一个数字出现了奇数次,其他数字均出现了偶数次,求这个出现了奇数次的数字。

例如:
a=[1,1,6,6,7,9,7]
很明显,所求答案为9,只有9出现了一次


不知道伙伴们有没有思路,如果没有思路的话,给点小提示:
异或的【结合律,归零律,恒等率】



解析

直接对数组的所有元素做异或运算便可得出答案

为什么呢?
举个栗子:a=[1,1,6,6,7,9,7]

1.首先结合律结合律:所有异或的顺序对结果是没有影响的。那么也就相当于依次异或[1,1,6,6,7,7,9]。


2.然后归零律:所以a^a=0。那么结果会变成[0,0,0,9],相等的归零了


3.最后恒等律:所以a^0=a。那么结果就变成了[9]


总结来说:出现偶数次的数字最后都会归零,只有奇数次的数字会剩下一个,那么最后的结果就是那个出现了奇数次数的数字了。

public static int getAlone(int[] nums){
    int res=0;
    for (int i : nums) {
        res^=i;
    }
    return res;
}



题目(二)

有了上题的铺垫,是否骚微感觉到了位运算的一丝丝神奇呢?再来亿题助助兴吧,估计头发不保。再来一题吧,Come on。

题目
给定一个数组,数组元素中仅存在【两个】数字出现了奇数次,其他数字均出现了偶数次,求这两个出现了奇数次的数字。
ps:跟上一题有那么点类似

例如:

a=[1,1,2,2,9,5,6,6,5,4]

答案便是9和4,只有这哥俩出现了奇数次。



不知阁下有思路否,没思路看题解嘛。^_ ^。

ps:如果想了半天还没有思路的话,直接看题解嘛,何必为难自己。很多算法是那些大神花了几个月甚至几年才研究出来的,你要是去自己死磕,估计你的秀发很快就要离你而去了。

java 高八位第八位 java 位运算_位运算



题解有点长,不过其实很简单。一定要耐心看完。

解析

首先假设所求的两个数分别为a和b


第一步:对所有数组元素做异或运算,的出来的结果就是temp=a^ b。
这点大家应该没问题吧。这里是上一题一样的思路,偶数归零了,最后就剩下a^ b了。


第二步:求出temp的二进制中最后一位1。
求这个有什么用呢?待我细细道来。
首先a和b是两个不同的数,异或运算是相同得0,不同得1。假设temp的二进制中第k位为1,就表示a和b的第k位二进制是不相同的。
那么我们就可以把数组中的数字分为两个部分:

1.二进制第k位为1的数
2.二进制第k位为0的数

而a和b会被分别划分进这两个不同的部分,为什么呢,因为我们做划分就是拿a和b不同之处做的划分,所以a和b一定不会在同一个部分中。


第三步:对其中的一部分的所有数字做异或运算。
既然我们将其划分为了两个部分,那对其中的一部分的所有数字做异或运算,就可以得出所求答案中的一个数字了,假设是a(也可以是b,这都不重要)


第四步:求出结果b
说到这里,大家肯定知道该怎么做了,直接对另一部分的所有数字做异或运算就可以得出b了。
当然没毛病啦,但是有个更简单的方法【自反律:a^ b^ a=b】。
既然上面求出来的temp=a^ b,那么b = a^ b^ a = temp^ a.
这样是不是更简单呢。

public static int[] getTwoAlone(int []nums){
    int temp=0,a=0,k;
    for (int i : nums) {
        temp^=i;
    }
    k=temp&((~temp)+1);
    for (int i : nums) {
        if((i&k)!=0){
            a^=i;
        }
    }
    return new int[]{a,a^temp};
}

看完之后是不是觉得有手就行。