位运算专题(JAVA)

位运算

JAVA位运算符有:左移( << )、右移( >> ) 、无符号右移( >>> ) 、位与( & ) 、位或( | )、位非( ~ )、位异或( ^ ),除了位非( ~ )是一元操作符外,其它的都是二元操作符。

关于补码:

计算机中存整数n是用补码存的:如果n为正数,则原码 = 补码 = 反码;如果n为负数,则补码 = 反码 + 1

正数右移,高位用0补,负数右移,高位用1补,当负数使用无符号右移时,用0进行补位(自然而然的,就由负数变成了正数了)

左移一位相当于乘以2,右移一位相当于除以2

  • 负数的右移:需要保持数为负数,所以操作是对负数的二进制位左边补1。如果一直右移,最终会变成-1,即(-1)>>1是-1。 2.

  • 负数的左移:和整数左移一样,在负数的二进制位右边补0,一个数在左移的过程中会有正有负的情况,所以切记负数左移不会特殊处理符号位。如果一直左移,最终会变成0。

我的记忆方法就是:

位与&100,位或|101,位异或^同0异1

习题

1.不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

  • 经过思考我们可以得出:如果不考虑进位,a+b的结果就是a^b的结果。
  • 进位就是&运算的结果但是需要左移一位,因为进位影响下一位的运算。
  • 如果进位不为0,那么a+b的结果就等于无进位和+进位,此时把无进位和作为a,进位作为b,继续计算。
  • 如果进位为0,则说明此时a+b的结果就等于无进位和,返回无进位和即可。
public int Add(int num1,int num2) {
        while (num1 != 0){
            int temp = num1 ^ num2;
            num1 = (num1 & num2) << 1;//进位
            num2 = temp;//无进位和
        }
        return num2;
    }

2.二进制中1的个数

输入一个整数 n ,输出该数32位二进制表示中1的个数。其中负数用补码表示。

思路:

​ 如果整数不等于0,那么该整数的二进制表示中至少有1位是1。先假设这个数的最右边一位是1,那么该数减去1后,最右边一位变成了0,其他位不变。再假设最后一位不是1而是0,而最右边的1在第m位,那么该数减去1,第m位变成0,m右边的位变成1,m之前的位不变。

​ 上面两种情况总结,一个整数减去1,都是把最右边的1变成0,如果它后面还有0,那么0变成1。那么我们把一个整数减去1,与该整数做位与运算,相当于把最右边的1变成了0,比如1100与1011做位与运算,得到1000。那么一个整数中有多少个1就可以做多少次这样的运算。

public int NumberOf1(int n) {
        int count=0;
        while(n!=0){
            count +=1;
            n = (n-1)&n;
        }
        return count;
    }

3.数值的整数次方

实现函数 double Power(double base, int exponent),求base的exponent次方。**代码:**考虑base=0/exponent=0/exponent<0的情况 。

注意:

1.保证base和exponent不同时为0。

2.不得使用库函数,同时不需要考虑大数问题

3.有特殊判题,不用考虑小数点后面0的位数。

算法思路:

  • 任何数的0次方都是1,0的任何次方都是0;
  • a(-n)与an是互为倒数的关系,所以给负数次方的设置一个flag,最后翻一下即可。
public double Power(double base, int exponent) {
        if(exponent==0)
            return 1;
        if(base==0)
            return 0;
        int flag=1;
        double res=1;
        if(exponent<0){
            flag =-1;
            exponent = -exponent;
        }
        while(exponent!=0){
            res = res*base;
            exponent -= 1;
        }
        if(flag==-1){
            res = 1/res;
        }
        return res;
  }

4.数组中只出现一次的两个数字

一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。输出时按非降序排列。

思路1:利用哈希表

public int[] FindNumsAppearOnce (int[] array) {
        // write code here
        // 用于返回结果
        int[] res = new int[2];
        // 创建一个哈希表
        HashMap<Integer,Object> set = new HashMap<>();
        for(int i = 0; i < array.length; i++){
            // 如果已经被当作key了,那就直接remove掉
            if(set.containsKey(array[i])){
                set.remove(array[i],null);
            }else{
                // 否则就添加进去
                set.put(array[i],null);
            }
        }
        int i = 0;
        // 最后拿出来放进返回结果的数组中进行返回
        for(Integer num:set.keySet()){
            res[i++] = num;
        }
        return res;
    }

思路2:位运算

利用异或运算的同0异1

详细思路:

  • 本题有两个只出现一次的数a和b,我们按异或方法最终只能得到a异或b的值,就需要思考一下这两个数异或的结果有何特点:我们可以发现,首先这两个数一定不同,故异或结果一定不为0,那么a异或b的结果中一定有一位为1
  • 假设是第x位为1(从右往左找起),那么就说明了a和b的二进制的第x位是不同,根据这一特点,我们可以将数组分为两个集合,即第x位为1的数和第x位为0的数,两部分的分别的异或和即为a和b的值
  • 然后比较大小、返回结果即可。
public int[] FindNumsAppearOnce (int[] array) {

        // 先将全部数进行异或运算,找出这两个不同的数相异或的结果
        int tmp = 0;
        for(int num: array){
            tmp ^= num;
        }
        // 找到那个可以充当分组去进行与运算的数(也就是找到两个数不同的那一位)
        // 从最低位开始找起
        int mask = 1;
        while((tmp & mask) == 0){
            mask <<= 1;
        }
        // 进行分组,分成两组,转换为两组 求出现一次的数字 去求
        int a = 0;
        int b = 0;
        for(int num:array){
            if((num & mask) == 0){
                a ^= num;
            }else{
                b ^= num;
            }
        }
        // 因为题目要求小的数放前面,所以这一做个判断
        if(a > b){
            int c = a;
            a = b;
            b = c;
        }
        return new int[]{a,b};
    }

5.求1+2+3+…+n

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)

思路一:使用递归

//若用递归:
if (n == 1) return n;
        return n + Sum_Solution(n-1);
//换成位运算即可
public int Sum_Solution(int n) {
        boolean flag=(n==1)||((n+=Sum_Solution(n-1))>0);
        return n;
    }

思路二:使用数学公式(右移相当于除以2)

public int Sum_Solution(int n) {
        int sum = 0;//初始化结果
        sum = (int)(n + Math.pow(n,2)) >> 1;//通过右移一位来代替除以2,注意要强转一下
        return sum;
    }

今日推歌

—《双人行》 司南

谁在吟唱
扶琴曲调
油纸伞上细雨如银毫
一拢白衣笑的心荡漾
仍是余音袅袅
小女自是难忘
在水一方

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星回昭以烂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值