LeetCode刷题总结 --- 位运算奇技淫巧

12 篇文章 0 订阅
8 篇文章 1 订阅

位运算 (TODO)

导言

  1. 以下代码都存放于 我的GitHub仓库 ,如果小伙伴觉得有用,请给我颗星星哈。
  2. 以下代码都是提交过的,正确性可以保证。

1. 位运算性质

  • 「异或」运算
    • 满足交换律: x ^ y == y ^ x
    • 满足结合律: (x ^ y) ^ z == x ^ (y ^ z)
    • 自身异或为 0: x ^ x == 0
    • 任何数和 0 异或都为自身: x ^ 0 == x
  • 「与」运算
    • 满足交换律: x & y == y & x
    • 满足结合律: (x & y) & z == x & (y & z)
    • 自身相与为自身: x & x == x
    • 任何数和 0 相与都为 0: x & 0 == 0
  • 「或」运算
    • 满足交换律: x | y == y | x
    • 满足结合律: (x | y) | z == x | (y | z)
    • 自身相或为自身: x | x == x
    • 任何数和 0 相或都为自身: x | 0 == x

2. 小技巧

  • 获取 整数x 的最低位 1 对应的值: x & (-x)

  • 获取 整数x 的最高位 1 对应的值:

    // 适用于64位的整数 (最高能获取第 62 位的"1" )
    func getHighestOne(a int) int {
        for i := 1; i <= 32; i *= 2 {
            a |= a >> uint8(i)
        }
        return (a + 1) >> 1
    }
    
  • 去除 整数x 的最低位 1 后的值: x & (x-1)

  • 实现 整数x整数y 的不进位加法: x ^ y

  • 实现 01的相互转换:

    • 0 ^ 1 == 1
    • 1 ^ 1 == 0
  • 关于取模

    • 获取 整数x2后的值: x & 1
    • 获取 整数x4后的值: x & 3
    • 获取 整数x8后的值: x & 7

3. 进阶

3.1 判断 整数x 是否为 2 的幂

func isPowerOfTwo(x int) bool {
    return x > 0 && x&(x-1) == 0
}

3.2 判断 整数x 是否为 4 的幂

// 只适用于32位整数
func isPowerOfFour(x int) bool {
    return isPowerOfTwo(x) && x & 0x55555555 != 0
}

// 适用于32、64位整数,但不适用于32位系统
func isPowerOfFour(x int) bool {
    return isPowerOfTwo(x) && x & 0x5555555555555555 != 0
}

// 适用于32、64位整数,适用于32、64位系统
func isPowerOfFour(x int) bool {
    return isPowerOfTwo(x) && x%3 != 0
}

func isPowerOfTwo(x int) bool {
    return x > 0 && x&(x-1) == 0
}

3.3 利用位运算实现 整数x整数y的交换。

// 这里只是形参的交换,如果要实现实参的交换,那么可以传入指针。
func swap(x,y int) {
    x = x ^ y
    y = x ^ y
    x = x ^ y
}

3.4 利用位运算获取 整数x 有多少个 1

func hammingWeight(x int) int {
    count := 0
    for x != 0 {
        count++
        x = x & (x-1) 
    }
    return count
}

3.5 计算 整数x整数y 的有多少个不同的二进制位。

func hammingDistance(x,y int) int {
    return hammingWeight(x^y)
}

func hammingWeight(x int) int {
    count := 0
    for x != 0 {
        count++
        x = x & (x-1) 
    }
    return count
}

3.6 利用位运算实现不用 + 做加法。

func getSum(a int, b int) int {
    sum, carry := a^b, (a&b)<<1
    if carry == 0 {
        return sum
    }
    return getSum(sum, carry)
}

3.7 位运算还可以实现的bitmap

/*
	现在假设现在有 personCount 人(标号[0, personCount-1]),他们每个人最多拥有30类物品(标号[0,29]),每类物品数量范围为[0,10^6]。
	现在给出他们分发物品的情况,每一种情况用一个长度为 3 的切片表示。
	如 [0, 5, 10],表示第 0 个人,被分发了 10 个第 5 类物品。
	如 [5, 10, 1000],表示第 5 个人,被分发了 1000 个第 10 类物品。
	现在给出人物编号 x、y,我想知道第 x 个人和第 y 个人 他们有多少类相同的物品、多少类不同的物品、以及他们一共有多少类物品。

	以上输入都在合法范围内。
*/

// 解题代码 (未经验证,只是为了说明位运算的功能,如果有错误,欢迎大家指出哈)
// personCount: 一共有多少人
// situations: 包含很多种拥有情况
// x,y : 两个人的标号
func solve(personCount int, situations [][]int, x, y int) (sameKinds, unsameKinds, allKinds int) {
	personOwn := make([]int, personCount) // 用于表示每个人的物品拥有情况(不关心拥有多少个,0个表示没有,否则表示有)
	// 		这里使用 32 位整数表示人们拥有物品的情况:
	// 		假如 personOwn[i] = 7,  这表示第 i 个人拥有第0、第1、第2类物品。
	// 		解释:	对于32位整数来说,7的二进制是:  00000000000000000000000000000111,从右到左算,标1的位置表示拥有该类物品,否则为不拥有。
	// 		假如 personOwn[i] = 9,  这表示第i个人拥有第0、第3类物品。
	// 		解释:	对于32位整数来说,9的二进制是:  00000000000000000000000000001001,从右到左算,标1的位置表示拥有该类物品,否则为不拥有。

    // 物品分配
	for i := 0; i < len(situations); i++ {
		if situations[i][2] != 0 {
			personOwn[situations[i][0]] &= 1 << uint8(situations[i][1])
		}
	}
	
	// 相同的物品表示 2 个人该二进制位上都是1,所以使用 & 运算,再通过计算结果有多少个 1,就能知道有多少类相同的物品。
	sameKinds = countOne(personOwn[x] & personOwn[y])

	// 不同的物品表示 2 个人该二进制位上是不同的,所以使用 ^ 运算,再通过计算结果有多少个 1,就能知道有多少类不同的物品。
	unsameKinds = countOne(personOwn[x] ^ personOwn[y])

	// 对于一共有多少类物品,两个人在该二进制位上有一个 1 就可以了,再通过计算结果有多少个 1,就能知道他们一共有多少类物品。
	allKinds = countOne(personOwn[x] | personOwn[y])

	// 例子:
	// 如 		      personOwn[x] == 00000000000000000000000000000111
	//	              personOwn[y] == 00000000000000000000000000001001

	// 则  personOwn[x] & personOwn[y] == 00000000000000000000000000000001	  	表示x,y共同拥有第0类物品,所以它们只有1类相同物品
	//     personOwn[x] ^ personOwn[y] == 00000000000000000000000000001110		表示x,y第1、2、3类物品拥有情况不同,所以它们有3类不同物品
	//     personOwn[x] | personOwn[y] == 00000000000000000000000000001111		表示x,y一共拥有第0、1、2、3物品,所以它们一共有4类物品。

	return
}

// 计算 a 有多少个1
func countOne(a int) int {
	count := 0
	for a != 0 {
		count++
		a = a & (a - 1)
	}
	return count
}

3.8 快速幂

// 快速幂,计算a^n,时间复杂度O(logn)
func quickPow(a, n int) int {
	sum := 1
	for n != 0 {
		if n&1 == 1 {
			sum *= a
		}
		a = a * a
		n >>= 1
	}
	return sum
}

4. 注意

  • 以上位运算基于补码。

5. 练习题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值