Java位运算之2的N次幂、整数转换、寻找只出现一次的数

本文详细介绍了Java中的位运算,包括如何判断一个数是否为2的N次幂,计算整数转换所需的位变化次数,以及使用位运算找出数组中只出现一次的数。通过位运算的高效实现,可以快速解决这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java位运算

位运算:只能对整数进行位运算,可加快计算机计算速度。因为所有数在内存中都是以二进制存放,故直接对数的二进制位进行操作非常快,比如乘法、除2操作:通过左右移二进制位即可,无需将数转为十进制后再通过加法完成(乘法本质是由加法实现的)

Java位运算的简单实现:
/* 位运算:加快计算效果,只能对整数进行位运算。因为所有数在内存中都是以二进制存放,故直接对数的二进制位进行操作非常快,
比如乘法、除2操作:通过左右移二进制位即可,无需将数转为十进制后再通过加法完成(乘法本质是由加法实现的)
* */
public class Code1_bitOption {
    /* 创建main函数快捷键: 输入psv即可出现提示 */
    public static void main(String[] args) {
        int a = 7, b = 2;
        System.out.println(a + " & " + b + " = " + (a & b));
        System.out.println(a + " | " + b + " = " + (a | b));
        System.out.println(a + " ^ " + b + " = " + (a ^ b));
        System.out.println("~" + a + " = " + (~a));
        System.out.println(a + " << " + b + " = " + (a << b));
        System.out.println(a + " >> " + b + " = " + (a >> b));      // 带符号右移,最左边高位与符号位保持一致
        System.out.println(a + " >>> " + b + " = " + (a >>> b));    // 无符号右移:最左边高位始终填0
    }
}

输出:

7 & 2 = 2
7 | 2 = 7
7 ^ 2 = 5
~7 = -8
7 << 2 = 28
7 >> 2 = 1
7 >>> 2 = 1
位运算之2的N次幂、整数转换:
  • 问题1:给定一个数,判断是否为2的N次幂
    解:关键技巧 a = a &(a-1)能消除a的最低有效位,即消除最右边的1,变为0。
    若一个数是2的N次幂,那么它的二进制表示有且只有一个1,如4:010;8:100
    故只需判断 a & (a-1)是否为0即可

  • 问题2:整数转换。编写一个函数,确定一共需要改变多少个位,才能将整数A转换为整数B
    解:找出AB的不同位的个数即为需要改变的位数,通过异或操作能将不同位上变为1,然后再统计1个个数即可。
    如何统计一个数的二进制中1的个数?: 使用问题1中的技巧 a = a &(a-1),循环消除a的最右边的1,直到a为0,循环次数即为1的个数.

public class Code2_checkPowerOf2 {
    public static void main(String[] args) {
        // 控制台输出快捷键, sout + Tab/Enter
        System.out.println(checkPowerOfTwo(10));
        System.out.println(checkPowerOfTwo(16));

        System.out.println(minCountOfBitChange(1, 14));
        System.out.println(minCountOfBitChange(2, 3));
    }

    public static boolean checkPowerOfTwo(int a) {
        return (a & (a - 1)) == 0;
    }

    public static int minCountOfBitChange(int a, int b) {
        int temp = a ^ b;
        return bitNumOfOne(temp);
    }

    private static int bitNumOfOne(int x) {
        int res = 0;
        while (x != 0) {
            x &= (x - 1);
            res++;
        }
        return res;
    }
}

输出:

false
true
4
1
位运算寻找只出现一次的数

问题:给定一个数组,数组中只有一个数仅出现过一次,其余数都出现过2次,请找出只出现1次的数
解题思路: 使用异或操作,利用其特性:a^a = a, a^0 = 0,且异或操作满足交换律和结合律!!!

public class Code3_singleNum {
    public static void main(String[] args) {
        int[] nums = new int[]{1, 4, 1, 2, 3, 3, 2};
        System.out.println(singleNum(nums));
    }

    public static int singleNum(int[] nums) {
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            // 相当于将数组的所有数,与0进行异或操作,然后通过交换律与结合律将2次出现的数异或为0,
            // 最后只剩下0与出现1次的数异或,其结果就是该数本身
            res ^= nums[i];
        }
        return res;
    }
}

输出:
4

寻找最近的2次幂((代码请基于jdk8’联网析每一步) (请详细析相关概念和描述,联网析,中文析)) 在定义elements变量时说,其长度总是2次幂,但用户传入的参并不一定符合规则,所以就需要根据用户的输入,找到比它大的最近的2次幂。比如用户输入13,就把它调整为16,输入31,就调整为32,等等。考虑下,我们有什么方法可以实现呢? 来看下ArrayDeque是怎么做的吧: private void allocateElements(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. // Tests "<=" because arrays aren't kept full. if (numElements >= initialCapacity) { initialCapacity = numElements; initialCapacity |= (initialCapacity >>> 1); initialCapacity |= (initialCapacity >>> 2); initialCapacity |= (initialCapacity >>> 4); initialCapacity |= (initialCapacity >>> 8); initialCapacity |= (initialCapacity >>> 16); initialCapacity++; if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements } elements = new Object[initialCapacity]; } 看到这段迷之代码了吗?在HashMap中也有一段类似的实现。但要读懂它,我们需要先掌握以下几个概念: 在java中,int的长度是32位,有符号int可以表示的值范围是 (-2)31 到 231-1,其中最高位是符号位,0表示正,1表示负。 >>>:无符号右移,忽略符号位,空位都以0补齐。 |:位或运算,按位进行或操作,逢1为1。 我们知道,计算机存储任何据都是采用二进制形式,所以一个int值为80的在内存中可能是这样的: 0000 0000 0000 0000 0000 0000 0101 0000 比80大的最近的2次幂是128,其值是这样的: 0000 0000 0000 0000 0000 0000 1000 0000 我们多找几组据就可以发现规律: 每个2次幂用二进制表示时,只有一位为 1,其余位均为 0(不包含符合位) 要找到比一个大的2次幂(在正范围内),只需要将其最高位左移一位(从左往右第一个 1 出现的位置),其余位置 0 即可。 但从实践上讲,没有可行的方法能够进行以上操作,即使通过&操作符可以将某一位置 0 或置 1,也无法确认最高位出现的位置,也就是基于最高位进行操作不可行。 但还有一个很整齐的字可以被我们利用,那就是 2n-1,我们看下128-1=127的表示形式: 0000 0000 0000 0000 0000 0000 0111 1111 把它和80对比一下: 0000 0000 0000 0000 0000 0000 0101 0000 //80 0000 0000 0000 0000 0000 0000 0111 1111 //127 可以发现,我们只要把80从最高位起每一位全置为1,就可以得到离它最近且比它大的 2n-1,最后再执行一次+1操作即可。具体操作步骤为(为了演示,这里使用了很大的字): 原值: 0011 0000 0000 0000 0000 0000 0000 0010 无符号右移1位 0001 1000 0000 0000 0000 0000 0000 0001 与原值|操作: 0011 1000 0000 0000 0000 0000 0000 0011 可以看到最高2位都是1了,也仅能保证前两位为1,这时就可以直接移动两位 无符号右移2位 0000 1110 0000 0000 0000 0000 000
04-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值