位运算学习总结(java实现)

位运算学习总结

位运算

位运算不仅仅是二进制数字的移动,还包括逻辑运算。常见逻辑运算就是与运算、或运算、异或运算、同或运算等等。首先最基础的就是数字的移动:
比如数字5,二进制数字是101(2),分别左移和右移一位结果:

1      int data = 5;
2      int rightShift = data>>1; //右移一位
3      int leftShift = data<<1;  //左移一位
4      System.out.println("右移结果:" + rightShift);
5      System.out.println("左移结果:" + leftShift);

运行结果:右移后二进制变为10(2) , 左移一位后就是1010(2)。
在这里插入图片描述
逻辑运算,与运算即有0就为0,或运算就是有1就是1,,以5(101)和12(1100)为例:
在这里插入图片描述

1   int data1 = 5;
2   int data2 = 12;
3   int andData = data1 & data2;
4   int orData = data1 | data2;
5   System.out.println("与运算结果:" + andData);
6   System.out.println("或运算结果:" + orData);

应用:移位运算是高效的运算,可以代替乘除,一个数字每左移一位即数字乘以2,每右移一位即数字除以2,那么举例:

    2乘以8,2左移三位即可,相当于2乘以8 :  2<<3.
    8除以8,8右移三位即可,相当于8除以8 :  8>>3

异或运算:这是算法题中常见的。异或就是相同为false,不同为true,0与任何数异或都是那个数本身,自己和自己异或结果是0。与之对应的就是同或运算,相同为true,不同为false。
在这里插入图片描述

1   int data1 = 5;
2   int data2 = 12;
3   int XORData = data1 ^ data2;
4   System.out.println("异或运算结果:" + XORData);

非运算,是对自己的运算,每个位取反即可,0位变为1,1位变为0.

算法题

LeetCode上有很多典型的关于位运算的题目。最基础的一道也是出现频率高的就是找出数组中唯一出现一次的数。

136题,只出现一次的数字;给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

根据异或性质,两个相同数异或结果是0,所以将数组所有数异或,结果就是唯一出现一次的数。

 1	class Solution {
 2    public int singleNumber(int[] nums) {
 3        int len = nums.length;
 4        int res = 0;
 5        for (int i = 0 ; i < len ; i ++) {
 6            res ^= nums[i];
 7        }
 8        return res;
 9    }
10 }

137. 只出现一次的数字 II:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现一次元素。
遍历每个位,数组所有数相加后,当前位的值要么是3的倍数,要么是0或者1,所以每个位遍历一遍后算出每个位置,再结合每个位结果。

 1 class Solution {
 2    public int singleNumber(int[] nums) {
 3        int res = 0;
 4        for (int i = 0 ; i < 32 ; i ++) {
 5            int sum = 0;
 6            for (int num : nums) 
 7                sum += (num>>i) & 1;
 8            res ^= (sum%3 <<i);
 9        }
10        return res;
11    }
12}

260. 只出现一次的数字 III:给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。找出只出现一次的那两个元素。
所有数异或后结果就是那两个数异或的结果,结果位值是1就表示这两个数当前位不一样,所以可以随机选一个位值是1的位,进行分组,将两个元素分在两个组内,两个组内所有结果异或,那么异或值就是只出现一次的那个。

 1 class Solution {
 2    public int[] singleNumber(int[] nums) {
 3        int len = nums.length;
 4        int XORData = 0;
 5        for (int num : nums)
 6            XORData ^= num;
 7        int index = 1;
 8        //找最右边为位值为1的那个位
 9        while ((XORData & index) == 0) {
10            index = index<<1;
11        }
12        //分组异或
13        int res1 = 0; int res2 = 0;
14        for (int num : nums) {
15            if ((num & index) == index) res1 ^= num;
16            if ((num & index) != index) res2 ^= num;
17        }
18        return new int[]{res1 , res2};
19    }
20}

421 数组中两个数的最大异或值

给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 。找到 ai 和aj 最大的异或 (XOR) 运算结果,其中0 ≤ i, j < n 。你能在O(n)的时间解决这个问题吗?

最大的异或 最大就是最大数的二进制表示的最大数,比如25 = 11001(2),最大只可能是11111(2),不能再大了。那么就按照最大二进制数按位遍历。就是这个位可不可能是1。

 1	class Solution {
 2    	public int findMaximumXOR(int[] nums) {
 3        	int len = nums.length;
 4        	int maxData = nums[0];
 5        	for (int num : nums) maxData = Math.max(maxData , num);
 6        	int stringLen = Integer.toBinaryString(maxData).length();
 7
 8        	HashSet<Integer> set = new HashSet<>(16);
 9        	int maxXor = 0 , curXor = 0;
10        	//遍历最长二进制数字
11        	for (int i = stringLen - 1 ; i >= 0 ; i--) {
12            	//让位一个下一个最大值
13            	maxXor <<= 1;
14            	//假设当前是最大的,最右一位掷1,就是看数组中前stringLen-i位是否有两个值异或等于curXor
15            	curXor = maxXor | 1;
16            	set.clear();
17            	for (int num : nums) set.add(num>>i);
18            	//是否存在p1^p2 == curXor ,也就是 p1 = curXor^p2是否存在
19            	for (int num : nums) {
20                	if (set.contains((num>>i)^curXor)) {
21                    	maxXor = curXor;
22                    	break;
23                	}  
24            	}
25        	}
26        	return maxXor;
27    	}
28	}

268 缺失数字:给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。

[0 , n]所有数据加上数组中所有数据异或,那么结果就是只出现一次的值,也就是[0 , n]中没有出现在序列中的数。

1	class Solution {
2    	public int missingNumber(int[] nums) {
3        	int len = nums.length;
4        	for (int i = 0 ; i < nums.length ; i++) {
5            	len ^= (i^nums[i]);
6        	}
7        	return len;
8    	}
9	}   

非算法题

正是因为位运算的高效性,在一些技术大佬写的代码或者源码中都能看见位运算,这些小细节也体现了编程者的技术功底和细节严谨。很值得自己学习。

    求2的余数:n & 1

    求2的幂次方的余数: n%a = n & b - 1 

HashTable源码中的位运算

空间初始化

1	//默认的初始化容量为16,必须是2的n次幂
2	static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
3	//最大容量为 2^30
4	static final int MAXIMUM_CAPACITY = 1 << 30;

tableSizeFor()函数: 将传入的初始化大小值转化为大于这个数的最小的2的幂方数

1	static final int tableSizeFor(int cap) {
2    	int n = cap - 1;
3    	n |= n >>> 1;
4    	n |= n >>> 2;
5    	n |= n >>> 4;
6    	n |= n >>> 8;
7    	n |= n >>> 16;
8    	return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
9	}

hash()计算原理:哈希值h和右移16位后作异或运算,尽量保持高16位特征,减少哈希碰撞。异或是因为让0和1分布均匀,从而哈希值分布均匀,也是为了减少哈希碰撞。

1	static final int hash(Object key) {
2    	int h;
3    	return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16	);
4	}

put()函数取模:与运算取模

1	//这是 put 方法中用来根据hash()值寻找在数组中的下标的逻辑,
2	//n为数组长度, hash为调用 hash()方法混合处理之后的hash值。
3	i = (n - 1) & hash

& 和 && 的区别:

    &运算两种用法: (1)按位与;(2)逻辑与

    &&运算符是短路与运算。两者都是要求运算符左右两端的布尔值都是true整个表达式才是true,但两者是有区别的。&&被称为短路运算是因为如果&&左边表达式值是false。右边表达式会直接被短路,不会进行运算。

    逻辑或运算(|) 和短路或运算符(||)差别也是如此。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值