真正的装逼都是不经意间的,比如说,当你的代码中出现了位运算
位运算在算法当中的使用
Part 1
检验一个数是否为2的幂次,(当一个数的二进制中只有一个1时,就是2的幂次)
假设现在有一个整数为n,想要判断它是否为2的幂次,我们可以
// 判断是2的幂次
func isPowerOfTwo(n int){
if n & (n - 1) == 0 {
fmt.Printf("%d是二的幂次\n", n)
}else {
fmt.Printf("%d不是二的幂次\n", n)
}
}
如果n是2的幂次的话,n & (n-1) 可以将n中的唯一个1给去掉,所以,可以通过结果是否为0来进行判断
Part2
给出一组整型数据,这些数据中,其中有一个数只出现了一次,其他数都出现了两次,请找出只出现了一次的那个数。
n ^ n = 0. n ^ 0 = n
那么,我们只需要遍历数组,将其中的每一个元素进行异或操作即可
func isDuplicatedArr(arr [7]int) int{
first := arr[0]
for idx, v := range arr {
if idx == 0 {
continue
}
first = first ^ v
}
return first
}
part3
计算2的n次幂
func thePowerOfTwo(n int) int{
var a int = 1
return a << n
}
part4
用异或操作实现两个数的交换
// 交换两个数
func swapTwoNumber(a *int,b *int){
*a ^= *b
*b ^= *a
*a ^= *b
}
part5
位操作判断奇偶性
// 判断奇数
func isOddNum(n int) bool {
if n&1 == 0 {
// 不是奇数
return false
}else {
// 不是偶数
return true
}
}
part6
// 位操作交换符号 正数交换为负数 负数变为正数
func reverseV(n int) int{
return ^n + 1
}
part7
// 位操作求绝对值
func abs(a int) int {
// 整数右移31位为0 负数右移31位为1
i := a >> 31
if i == 0 {
// 正数
return a
}else {
// 负数取反
return ^a + 1
}
}
part8
// 统计二进制中1出现的个数
func countOneInBin(n int) int {
count := 0
for{
n = n & (n-1)
count ++
if n == 0 {
break
}
}
return count
}
// 每调用一次n&(n-1),n的二进制中1的个数就会少一个
part9
hash表的散列当中也是经常可以见到位运算的存在, 比如说我们jdk中的hashmap,还有netty源码中自己实现的fastThreadLocalThread中的ThreadLocalMap底层数组进行散列时也是这样写的
我所标蓝的哪里就是hashmap进行键散列的逻辑
这样写的话,不仅效率比取模运算要好一些,你分布的均衡性完全由你的hashcode的后几位决定,这是因为n为tab的长度,hashmap的长度有一个规则,默认为16,之后的每一次resize时,他的值都是2的幂次,使得(n - 1)的二进制表示为几个连续的1,这样,分布的均匀性完全取决于你的hashcode的后几位,而一般我们的hashcode是没有规律的,所以,效率较好的实现了hash表的散列.
以上就是目前的关于位运算比较常用的一些技巧,等以后如果发现了新的话,还会持续更新下去