什么诡异的题目都有

本文探讨了在一个大部分元素成对出现的数组中寻找唯一元素的方法。分别讨论了一个元素、两个元素和三个元素仅出现一次的情况,并提出了时间复杂度为O(n)、空间复杂度为O(1)的算法。
1. 一个数组,其中只有一个数只出现一次,其余数皆出现偶数次。设计Time: O(n) Space: O(1)的算法得出那个只出现一次的数。
2. 如果只有两个数只出现一次呢?
3. 如果只有三个数只出现一次呢?
Thinking......


[url=http://groups.google.com/group/pongba/browse_frm/thread/f4a080edbe3ce0e1?pli=1]原帖[/url]
# 题目重述 题目要求计算从 $0$ 到 $n-1$ 所有整的 `popcnt` 值之和,其中 `popcnt(x)` 表示 $x$ 的二进制表示中 $1$ 的个。 给出的 C 语言代码试图通过逐个遍历 $0$ 到 $n-1$ 的每个,并统计其二进制中 $1$ 的个来求解总和。但在循环结束后有一句: ```c sum = sum - 32; ``` 问题是:**为什么输出前要减去 32?** --- # 详解 这段代码存在严重逻辑错误,**“减去 32” 是一个错误的补丁操作,目的是试图修正内层循环对每一位重复计导致的严重高估结果的问题**。 我们逐步分析: ### 1. 外层循环: ```c for (long long j = m; j >= 0; j--) ``` 这是正确的思路方向:遍历从 $0$ 到 $n-1$ 的每一个。 ### 2. 内层找最高位: ```c for(i=0; i<32; i++) { if ((j >= (1 << i)) && (j < (1 << (i+1)))) break; } ``` 这一段逻辑是错的。它想找到 $j$ 的最高有效位(MSB),但条件 `(j >= (1<<i)) && (j < (1<<(i+1)))` 实际上是在检查 $j$ 是否落在区间 $[2^i, 2^{i+1})$,这本应是用于确定位宽的,但它放在了一个 `for` 循环里从 $i=0$ 开始查找,会导致即使对于 $j=0$,也会一直找到 $i=31$ 左右才可能“匹配”。 更糟的是,这个 `break` 出来的 `i` 并不是最高位的位置,而是满足该区间的最小 $i$,这完全错误。 ### 3. 接下来的 while 循环才是真正的“二进制中 1 的个”的过程: ```c while(i >= 0) { if(temp >= (1 << i)) { sum++; temp -= (1 << i); } i--; } ``` 这部分逻辑本身是对的 —— 类似于二进制分解,从高位往低位试减。 但是由于前面那个 `for` 循环没有正确设置初始的 $i$ 值(即最高位位置),所以这里使用的 $i$ 可能极小或极大,造成漏计或错计。 ### 4. 最关键的问题:变量作用域与初始化缺失! - 变量 `i` 在外层被多次复用,却没有在每次处理新字前重置。 - 更致命的是,在最内层 `while` 结束后执行了: ```c i = 0; ``` 这会导致下一个外层 `for` 循环(`for(i=0; ...)`)的行为异常吗?不会直接,但如果中间状态混乱就会。 但真正诡异的是最后这一句: ```c sum = sum - 32; ``` 在整个累加完成后,居然减去 32! ### 5. **为何要减 32?** 观察整个程序结构: - 它对每个 $j \in [0, n-1]$ 都尝试分解二进制位。 - 但由于上面提到的寻找最高位的方式错误,很可能导致每个都被当作具有全部 32 位参与运算。 - 特别地,内层 `for(i=0;...;i<32)` 的写法实际上并不能正确找出最高位,反而可能导致 `i` 被设为某个中间值。 - 然后进入 `while(i >= 0)` 时,会从那个错误的 $i$ 开始向下扫描。 - **更大的问题是:作者可能发现每次字都被多加了 32 个 bit(假设有 32 位),于是强行在最后减去 32 来“纠正”。** 但这显然不对: - 每个最多只有 $\log_2(j)+1$ 个位需要处理。 - 减一次 32 只影响最终结果一次,而不是每个都减。 所以结论是: > ❌ “减去 32” 是一种无意义的硬编码补偿行为,源于算法设计错误,无法修复根本问题。 --- # 知识点 - **位运算与 popcount 原理**:通过不断减去 $2^i$ 判断某一位是否为 1,可用于统计二进制中 1 的个。 - **循环控制与变量作用域**:重复使用循环变量而未正确初始化,会导致逻辑错乱。 - **算法复杂度优化意识**:暴力枚举 $O(n \log n)$ 不适用于 $n \leq 10^9$,应采用位 DP 或学规律法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值