位运算的奇思妙想

真正的装逼都是不经意间的,比如说,当你的代码中出现了位运算

位运算在算法当中的使用

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底层数组进行散列时也是这样写的

BcV6ST.png

我所标蓝的哪里就是hashmap进行键散列的逻辑
这样写的话,不仅效率比取模运算要好一些,你分布的均衡性完全由你的hashcode的后几位决定,这是因为n为tab的长度,hashmap的长度有一个规则,默认为16,之后的每一次resize时,他的值都是2的幂次,使得(n - 1)的二进制表示为几个连续的1,这样,分布的均匀性完全取决于你的hashcode的后几位,而一般我们的hashcode是没有规律的,所以,效率较好的实现了hash表的散列.

以上就是目前的关于位运算比较常用的一些技巧,等以后如果发现了新的话,还会持续更新下去

猜你喜欢

转载自blog.csdn.net/weixin_44880685/article/details/113934518