C++ 中的 lowbit
lowbit 的定义
首先了解 lowbit 的定义
,为 的二进制原码中最低的一位 以及其后面的 所表示的数
举个简单的例子:
将
其中最低位的
此时
lowbit 的计算
如何计算
lowbit 的特殊情况
由于 lowbit 会将除最低位
以外所有的位 改为 , lowbit 将只会对位 的位数高于1的二进制数产生影响,所以位 只有1位的二进制数和 处理后将得到原数据,即:
方法一:递归
先暂时假定
为正整数 将
转换为二进制,可得: ( 为 或 ) 此时
转换为二进制可得: 假设
转为二进制后,末尾有 个连续位 (显然,此时 ) 因此,
转为二进制后,末尾有 个连续位 (此时 ) 于是我们得到了:
此时
表示为二进制是 ,怎么样,有没有什么想法? 将
加上 ,得: 显然:
观察
的二进制形式: 对比
的二进制形式: (在原码中,我们一般使用第一位存储符号, 为正, 为负) 很明显,
无论
的符号为负还是为正,奇偶性都一致,因此,我们在上面推导出来的公式对负整数仍然成立 综上所述,任意奇数的 lowbit 值都为
,任意偶数的 lowbit 值都为其乘 得到的值的 lowbit 值乘 通过这种思路,我们可以编写一个递归函数计算
的 lowbit 值,遇到奇数直接返回 ,遇到偶数辄除以 后继续计算 写成伪代码是这样的:
int lowbit(int n) { if (n % 2 == 1) return 1; // Odd else return lowbit(n / 2) * 2; // Even }
方法二:公式
在方法一中,我们使用了深度优先搜索,时间复杂度可能有点高,我们当然可以使用记忆化数组降低复杂度,但,我们是否可以推导出一个公式直接计算,将复杂度降低为
呢? 当然是可行的。还是观察
的二进制形式: (假定 为非负整数) 还是对比
的二进制形式: 如果对
每一位取反(符号位除外),我们就得到了 的反码: 此时
末尾的 全部变为 ,而最低位的 也难逃变为 的命运 如果我们现在将其加上
,我们将得到 的补码: ,反码末尾的 将重新变为 ,而最低位 将重新变为 ,其他位不变,仍然是取反的状态,此时如果将 的补码与 原码进行按位与的运算( 与 的原码只有符号位的不同),由于除符号位、最低位 及其后面的位 ,其他位都进行了取反,这些位将被赋值为 0 & 1
或1 & 0
,即,而符号位也会赋值为 0 & 1
,只剩最低位及其后面的位 分别被赋值为 1 & 1
和0 & 0
,即和 ,最后结果为 (或者说 ) 那么
的反码、补码呢?上文所述的只是负数的反码与补码的计算方式,实际上,正数的原码、反码、补码都是一样的(对于原码、反码、补码,本博文已经进行了必要的解释,但如果你对其感兴趣,想知道其详细解释,可参考这篇博文:二进制|原码、反码、补码)
众所周知,C++ 中,数字是使用补码表示的,因此,我们可以将的补码视为 的原码,在 C++ 中进行运算。于是,我们得到了 的原码和 的补码 上文中,我们提到了将
的原码和 的补码进行按位与运算可以得到 和 ,现在我们使用 的补码作为其原码(毕竟是一样的),可以得到: 显然
是负数也不会造成影响 于是我们成功地得到了 lowbit 的计算公式,将算法的时间复杂度降低为
,并简化了代码: #define lowbit(n) (-n & n)
由于使用宏定义,一定要记得打括号,位运算的优先级是最低的
lowbit 的应用
说了这么多, lowbit 有什么作用呢?最主要的作用是用于维护树状数组,该算法可能会有一点点抽象,大家感兴趣可以参阅这篇博客:树状数组 | 维护区间和 别看了还没写好 。现在,我会给大家介绍几个 lowbit 的小用法。
1. 求奇偶性
我们都知道,在需要求一个数 (bool)a & 1
,返回 true
为奇数, 否则为偶数。但如果你觉得这种方法不够装逼的话,我们还可以使用 a & -a == 1
语句,同样是返回 true
为奇数,否则为偶数。
2. 关于 的幂相关判断
有点关于二进制的题目非常友善,会提出譬如 “判断 有趣的判断。这个时候,我们可以动动脑筋,尝试用 lowbit 以使用
int getone(int n) {
int ans = 0;
while (n) n -= n & -n, ans++;
return ans;
}
和宏定义 #define pow_of_two(n) (n & -n == n)
解决
如果你对这方面感兴趣,不妨订阅一下我的博客,我以后会分享更多有趣且有用的算法知识
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」