惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
T
Threat Research - Cisco Blogs
N
News and Events Feed by Topic
Hacker News: Ask HN
Hacker News: Ask HN
Project Zero
Project Zero
Application and Cybersecurity Blog
Application and Cybersecurity Blog
博客园 - 叶小钗
Security Latest
Security Latest
Spread Privacy
Spread Privacy
aimingoo的专栏
aimingoo的专栏
N
News and Events Feed by Topic
Webroot Blog
Webroot Blog
U
Unit 42
Cyberwarzone
Cyberwarzone
小众软件
小众软件
Scott Helme
Scott Helme
Engineering at Meta
Engineering at Meta
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
A
About on SuperTechFans
爱范儿
爱范儿
S
Schneier on Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Schneier on Security
Schneier on Security
Latest news
Latest news
GbyAI
GbyAI
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园_首页
Blog — PlanetScale
Blog — PlanetScale
PCI Perspectives
PCI Perspectives
Jina AI
Jina AI
AI
AI
NISL@THU
NISL@THU
I
Intezer
G
GRAHAM CLULEY
B
Blog
S
Secure Thoughts
IT之家
IT之家
宝玉的分享
宝玉的分享
Recent Announcements
Recent Announcements
Y
Y Combinator Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
有赞技术团队
有赞技术团队
V2EX - 技术
V2EX - 技术
Recorded Future
Recorded Future
Hacker News - Newest:
Hacker News - Newest: "LLM"

蛮荆

如何获取更多的免费服务器 Kubernetes 调度器队列 - 设计与实现 Kubernetes 调度器 - 核心流程 Kubernetes Networking Model & CNI Kubernetes 控制器管理总结 Kubernetes CronJob 设计与实现 Kubernetes Job 设计与实现 Kubernetes HPA 设计与实现 Kubernetes Deployment 滚动更新实现原理 Kubernetes GC 设计与实现 Kubernetes Pod 驱逐 - 设计与实现 Kubernetes Daemonset 设计与实现 Kubernetes ReplicaSet 设计与实现 Kubernetes EndPoint 设计与实现 Kubernetes Informer 设计与实现 降本增效之应用优化 (三) 日志存储与检索 Kubernetes Pod 设计与实现 - 创建流程 Kubernetes 探针设计与实现 Unix 编程艺术名句摘录 Kubernetes - CRI 概述 Golang 编译速度为什么这么快? Kubernetes Pod 设计与实现 - Pause 容器 Kubernetes - kube-proxy 代理模式工程优化 Kubernetes 应用最佳实践 - 优雅关闭长连接 Kubernetes Service 类型和会话亲和性 Kubernetes 为什么需要 Ingress Kubernetes 架构 - 控制平面和数据平面 降本增效之应用优化 (二) 大报表 Go 语言如何获取 CPU 利用率 降本增效之应用优化 (一) Redis 业务规则引擎演变过程简述 微服务中的熔断算法 漏桶算法和令牌桶算法 jsonparser 为什么比标准库的 encoding/json 快 10 倍 ? zap 高性能设计与实现 HTTP Router 算法演进 fastcache 高性能设计与实现 Web 常见的三个安全问题 ants Code Reading Go 线程安全 map 方案选型 布隆过滤器 死锁、活锁、饥饿、自旋锁 sync.Pool Code Reading Go 内存管理概述 Go netpoll Code Reading goroutine 泄漏与检测 time/Timer Code Reading GMP Scheduler Code Reading Go channel 的 15 条规则和底层实现 为什么 Linux “一切皆文件” context.Context Code Reading runtime/HACKING.md Goland 最佳实践 互联网开发与金庸武学 为什么 Redis 6.0 引入多线程模型? Kubernetes 应用最佳实践 - 金丝雀发布 容器中如何正确配置 GOMAXPROCS ? singleflight Code Reading sync.Map Code Reading sync.Cond Code Reading sync.WaitGroup Code Reading sync.RWMutex Code Reading sync.Mutex Code Reading sync.Once Code Reading Go 无锁编程 sync/atomic Code Reading goroutine 交替打印奇偶数 GODEBUG Go 并发模式 Go 汇编 UUID 通用技术选型 Kubernetes 应用最佳实践 - 水平自动伸缩 Go 高性能 Tips fasthttp 为什么比标准库 net/http 快 10 倍 ? 技术文章配图指南 ChatGPT 初体验 Docker 网络原理概览 iptables 的五表五链 Kubernetes 应用最佳实践 - 亲和性和污点容忍度 Go 的反射与三大定律 Docker 官方提供的最佳实践 Go 语言内置的设计模式 HTTP1 到 HTTP3 的工程优化 Kubernetes 应用最佳实践 - Sidecar 模式 Kubernetes 应用最佳实践 - init 容器和钩子函数 为什么 recover 必须在 defer 中调用? 为什么 defer 的执行顺序和注册顺序不同? Go map 设计与实现 Go 切片扩容底层实现 Go 语言中的零拷贝 Go Delve 云原生和边缘计算简介 Kubernetes Pod 服务质量等级 Kubernetes 应用最佳实践 - 探针 Kubernetes 应用最佳实践 - 资源请求和限制 CDN 原理 Kubernetes 应用最佳实践 - 开篇 缓存策略和模式 Go 内存模型 Kubernetes 核心概念
LeetCode 刷题必懂位运算
2022-02-02 · via 蛮荆

2022-02-02 算法 LeetCode

位运算

AND

表示将两个二进制数按位进行 运算,只有当两个对应的位都为 1 时,结果位才为 1,其他情况结果位为 0, Go 语言中使用运算符 &

0000 0100 # 4 的二进制表示 
0000 0111 # 7 的二进制表示
0000 0100 # 结果 = 4
// Go 语言表示
println(4 & 7)

OR

表示将两个二进制数按位进行 运算,只要两个对应的位任意一个为 1,结果位为 1,两个位都为 0 时,结果位才为 0, Go 语言中使用运算符 |

0000 0100 # 4 的二进制表示 
0000 0111 # 7 的二进制表示
0000 0111 # 结果 = 7
// Go 语言表示
println(4 | 7)

XOR

表示将两个二进制数按位进行 异或 运算,只有两个对应的位不相等时,结果位为 1,其他情况结果位为 0, Go 语言中使用运算符 ^

0000 0100 # 4 的二进制表示 
0000 0111 # 7 的二进制表示
0000 0011 # 结果 = 3
// Go 语言表示
println(4 ^ 7)

Complement

表示将二进制数按位进行 取反 运算,也就是每个位从 0 变成 1,1 变成 0, Go 语言中使用运算符 ^

0111 1111 # 127 的二进制表示
1000 0000 # 取反之后 = -128

对于有符号整型,最高位是是符号位,计算机用 补码 表示负数。

补码 = 原码取反 + 1

其中 原码 以负数的绝对值的二进制表示。

下面举两个例子来说明。

  1. 计算负数 -1 的二进制表示
# 计算 -1 的原码
# 原码以负数的绝对值 (1) 的二进制表示
# 所以 -1 的原码 = 00000001

# 将原码进行取反运算

00000001  # -1 原码
11111110  # 取反结果

# 在取反的结果上加 1

11111110 # 取反结果
11111111 # -1 的二进制表示
// Go 语言表示
println((^1) + 1)
  1. 计算 -128 的二进制表示
# 计算 -128 的原码
# 原码以负数的绝对值 (128) 的二进制表示
# 所以 -128 的原码 = 10000000

# 将原码进行取反运算

10000000  # -128 原码
01111111  # 取反结果

# 在取反的结果上加 1

01111111 # 取反结果
10000000 # -128 的二进制表示
// Go 语言表示
println((^128) + 1)

溢出

如果计算结果产生溢出,那么最高位溢出的部分会被省略掉。

11111111 # -1 的二进制表示
00000001 #  1 的二进制表示
00000000 #  0
10000000 # -128 的二进制表示
01111111 #  127 的二进制表示
11111111 # -1 

位移

<< 表示左移,>> 表示右移。

数字 8 右移 2 位结果为 2。

0000 1000 # 8 的二进制表示
0000 0010 # 2

数字 8 左移 2 位结果为 32。

0000 1000 # 8 的二进制表示
0010 0000 # 32

将数字左移 1 位等于将该数字乘以 2, 因为在二进制中,左移 1 位就是将每个位向左移动 1 位,而每 1 位的位置都是 2 的幂,反过来说,将数字右移 1 位等于将该数字除以 2。

// Go 语言表示
println(8 >> 2) // 2
println(8 << 2) // 32

练习

1. 判断数字 K 第 n 位是否为 1

解题思路:

  1. 构造一个数字 X,该数字除了第 n 位为 1,其他位都为 0 (利用左移)
  2. 将数字 K 与数字 X 进行 & 运算并判断结果是否大于 0, 如果大于 0, 表示数字 K 的第 n 位为 1, 否则表示数字 K 的第 n 位为 0

例如分别测试数字 7 的第 2 位和第 3 位是否为 1:

# 检测 7 的第 2 位是否为 1
0000 0111 # 7 的二进制表示
0000 0100 # 构造的数字,除了第 2 位,其他位全部为 0
0000 0100 # 结果为 4, 表示 7 的第 2 位等于 1

# 检测 7 的第 3 位是否为 1
0000 0111 # 7 的二进制表示
0000 1000 # 构造的数字,除了第 3 位,其他位全部为 0
0000 0000 # 结果为 0, 表示 7 的第 3 位不等于 1
// Go 语言表示
println(7 & (1 << 2))
println(7 & (1 << 3))

2. 判断数字 K 是否为 2 的整数次幂

解题思路:

首先看一下 2 的整数次幂都有什么特征?

0000 0010 # 2 的二进制表示
0000 0100 # 4 的二进制表示
0000 1000 # 8 的二进制表示
0001 0000 # 16 的二进制表示

特征就是二进制表示中只有 1 位为 1, 其他位全部为 0, 因此只需要构造一个数字 X, X 和 K 正好相反,只有 1 位为 0, 其他位全部为 1, 将数字 K 与数字 X 进行 & 运算并判断结果是否等于 0, 如果等于 0, 表示数字 K 等于 2 的整数次幂, 否则表示数字 K 不等于 2 的整数次幂。

0000 0010 # 2 的二进制表示
0000 0001 # 数字 X, 也就是 1 的二进制表示

0000 0100 # 4 的二进制表示
0000 0011 # 数字 X, 也就是 3 的二进制表示

0000 1000 # 8 的二进制表示
0000 0111 # 数字 X, 也就是 7 的二进制表示

0001 0000 # 16 的二进制表示
0000 1111 # 数字 X, 也就是 15 的二进制表示

从上面的代码中可以看到,X 其实就是 K-1, 在这个基础上再执行 & 运算即可。

0000 1000 # 8 的二进制表示
0000 0111 # 数字 X, 也就是 7 的二进制表示

# 执行 & 运算
0000 1000 # 8 的二进制表示
0000 0111 # 7 的二进制表示
0000 0000 # 0 结果等于 0, 表示 8 等于 2 的整数次幂
// Go 语言表示
println(8&(8-1) == 0)   // true
println(7&(7-1) == 0)   // false

通过上面的梳理过程,可以变化出另外一个小技巧: 将二进制表示中最右边为 1 的位置为 0:

0000 0111 # 7 的二进制表示
0000 0110 # 6 的二进制表示

# 执行 & 运算
0000 0111 # 7 的二进制表示
0000 0110 # 6 的二进制表示
0000 0110 # 6, 最右边为 1 的位已经被重置为 0
// Go 语言表示
println(7&(7-1))

3. 判断数字 k 的二进制需要改变多少位可以变为数字 n

解题思路:

  1. 计算出数字 k 和 n 有多少不同的位 (使用异或运算)
  2. 异或计算时,只有两个对应的位不相等时,结果位为 1,其他情况结果位为 0
  3. 计算结果中位等于 1 的个数,就是数字 k 需要改变的位数量
0000 0111 # 7 的二进制表示
0000 0110 # 6 的二进制表示

# 执行 ^ 异或运算
0000 0111 # 7 的二进制表示
0000 0110 # 6 的二进制表示
0000 0001 # 结果二进制中只有 1 位为 1

所以数字 7 需要改变 1 位可以变为数字 6, 反过来也一样。

// Go 语言表示

func countBits(n int) int {
	count := 0
	for n != 0 {
		count += n & 1 // 如果最低位为1,则计数增加
		n >>= 1        // 右移一位
	}
	return count
}

func main() {
	println(countBits(7 ^ 6)) // 1
}

Go 标准库中的位运算

文章的最后介绍下 Go 语言中和位运算相关的方法。

打印二进制表示

fmt.Printf("%08b\n", 7) // 00000111

计算数字的二进制表示长度

fmt.Println(bits.Len(7))  // 3
fmt.Println(bits.Len(16)) // 5

计算数字的二进制表示中位为 1 的个数

// 7 的二进制表示: 0111, 有 3 位为 1
fmt.Println(bits.OnesCount(7)) // 3

计算数字的二进制表示中位为 0 的个数

// 7 的二进制表示: 1000, 有 3 位为 0
fmt.Println(bits.Len(8) - bits.OnesCount(8)) // 3

反转数字的二进制表示中的所有位

fmt.Printf("%08b\n", 7)                // 00000111
fmt.Printf("%08b\n", bits.Reverse8(7)) // 11100000   

检查数字的二进制表示中位为 0 的数量

var x uint8 = 16
fmt.Printf("%08b\n", x)  // 00010000

// 返回从最高位开始到第一个位为 1 之间 0 的数量
fmt.Println("LeadingZeros:", bits.LeadingZeros8(x)) // 3

// 返回从最低位开始到第一个位为 1 之间 0 的数量
fmt.Println("TrailingZeros:", bits.TrailingZeros8(x)) // 4

扩展阅读

图片来源: https://www.terathon.com/lengyel/