news 2026/10/26 2:35:13

Leetcode 3314题解:位运算构造最小数组的贪心算法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Leetcode 3314题解:位运算构造最小数组的贪心算法

1. 题目解析与核心思路

Leetcode 3314题"构造最小位运算数组 I"是一道考察位运算基础与数组构造技巧的题目。题目要求我们构建一个满足特定位运算条件的数组,这类问题在实际编程面试中经常出现,尤其考察候选人对位运算特性的理解深度。

1.1 题目要求拆解

题目给定一个整数n,需要构造一个长度为n的数组arr,满足以下条件:

  1. arr[0] = 0
  2. 对于每个i(1 ≤ i < n),arr[i]必须满足arr[i] = arr[i-1] | x,其中x是一个非负整数
  3. 整个数组所有元素的按位或运算结果必须等于2^k - 1(即全1的二进制数)
  4. 在所有可能的解中,需要选择使数组元素和最小的解

这里的关键运算符是按位或(|),它会将两个数的二进制表示逐位进行或运算。例如5 | 3 = 7(二进制101 | 011 = 111)。

1.2 位运算特性分析

要解决这个问题,我们需要深入理解位运算的几个重要特性:

  1. 或运算的性质:a | b的结果的二进制表示中,任何一位为1当且仅当a或b在该位为1
  2. 全1数的特性:2^k - 1的二进制表示是k个连续的1(如7=2^3-1=111)
  3. 递增性:a | b ≥ max(a,b),这意味着数组是非递减的

基于这些特性,我们可以推导出构造数组的策略:每次添加的x应该尽可能小,同时确保最终能覆盖所有二进制位。

2. 解题思路与算法设计

2.1 贪心算法选择

这道题适合采用贪心算法,因为我们需要在每一步做出局部最优选择(添加最小的x),以达到全局最优(数组和最小)。具体策略如下:

  1. 初始化arr[0] = 0
  2. 维护一个变量current_or记录当前所有元素的或结果(初始为0)
  3. 对于每个i从1到n-1:
    • 找到最小的x,使得(current_or | x) > current_or
    • 这个x就是current_or的二进制表示中最低的未设置位对应的值
    • 设置arr[i] = arr[i-1] | x
    • 更新current_or = current_or | x
  4. 最终确保current_or等于2^k - 1

2.2 关键实现步骤

实现这个算法的关键在于如何高效找到每次需要添加的最小x。这里可以利用位运算技巧:

def construct_min_or_array(n): arr = [0] * n current_or = 0 for i in range(1, n): # 找到current_or的最低未设置位 x = (~current_or) & -(~current_or) arr[i] = arr[i-1] | x current_or |= x return arr

这个实现中,(~current_or) & -(~current_or)是一个位运算技巧,用于找到current_or的最低未设置位对应的值。例如:

  • 如果current_or=0(二进制00),则~current_or=-1(二进制11...11),计算结果是1
  • 如果current_or=1(二进制01),则~current_or=-2(二进制11...10),计算结果是2
  • 如果current_or=3(二进制011),则~current_or=-4(二进制11...100),计算结果是4

2.3 复杂度分析

  • 时间复杂度:O(n),因为我们只需要遍历数组一次
  • 空间复杂度:O(n),用于存储结果数组(不考虑输出空间则为O(1))

3. 正确性证明与边界条件

3.1 算法正确性证明

要证明这个算法的正确性,我们需要确认:

  1. 构造的数组满足所有给定的条件
  2. 数组的和是所有可能解中最小的

对于第一条,由于我们每次添加的x都确保current_or增加至少一个二进制位,最终current_or会变成全1(因为~current_or最终会变成0)。

对于第二条,由于我们每次都选择最小的可能x来扩展current_or,这保证了数组元素的和最小。任何其他选择要么会导致相同的和,要么会导致更大的和。

3.2 边界条件处理

在实际实现中需要考虑以下边界条件:

  1. n=1时,直接返回[0]
  2. 确保最终current_or确实是2^k-1的形式
  3. 处理大n的情况(虽然题目中n的范围可能有限制)

4. 优化与变种思考

4.1 空间优化

如果不需要返回整个数组,而只需要验证是否可以构造这样的数组,我们可以进一步优化空间:

def can_construct_min_or(n): current_or = 0 for _ in range(n-1): x = (~current_or) & -(~current_or) current_or |= x return (current_or + 1) & current_or == 0 # 检查是否是2^k-1

4.2 相关变种题目

这类位运算构造问题有多种变种,例如:

  1. 构造数组使得所有元素的异或和为特定值
  2. 构造数组使得相邻元素的与运算满足某些条件
  3. 在构造过程中加入其他约束条件(如元素范围限制)

5. 实际应用与经验分享

5.1 位运算的实际应用场景

位运算在以下场景中非常有用:

  1. 权限系统:用位掩码表示不同权限
  2. 压缩存储:用位表示布尔值可以极大节省空间
  3. 高性能计算:位运算通常比算术运算更快
  4. 加密算法:许多加密算法依赖位运算

5.2 解题经验分享

在解决这类位运算问题时,我总结了一些实用技巧:

  1. 记住常见的位运算恒等式,如x & (x-1)可以清除最低位的1
  2. 理解补码表示法,这对于处理负数很有帮助
  3. 使用掩码来提取或设置特定位
  4. 在纸上画出二进制表示往往能帮助理解

注意:位运算的优先级通常较低,在复杂表达式中要合理使用括号。例如a | b & c实际上等同于a | (b & c),这可能不是你想要的效果。

6. 常见错误与调试技巧

6.1 典型错误模式

在实现这类算法时,常见的错误包括:

  1. 位运算优先级理解错误,导致逻辑错误
  2. 没有正确处理边界条件(如n=1)
  3. 在寻找最低未设置位时使用了错误的位运算
  4. 没有验证最终结果是否满足全1的条件

6.2 调试建议

当你的解法出现问题时,可以:

  1. 打印出中间变量的二进制表示,这能帮助你直观理解位运算的效果
  2. 对小规模输入手动计算预期结果,与程序输出对比
  3. 特别注意循环的起始和结束条件
  4. 检查是否所有可能的输入都被正确处理

例如,可以添加调试打印:

def construct_min_or_array_debug(n): arr = [0] * n current_or = 0 for i in range(1, n): x = (~current_or) & -(~current_or) print(f"i={i}, current_or={bin(current_or)}, x={bin(x)}") arr[i] = arr[i-1] | x current_or |= x print(f"Final current_or: {bin(current_or)}") return arr

7. 扩展思考与进阶挑战

7.1 进阶问题

基于这道题,可以思考以下进阶问题:

  1. 如果要求数组严格递增(arr[i] > arr[i-1]),解法需要如何调整?
  2. 如果给定一个目标或值,而不是要求全1,如何构造数组?
  3. 如果要求数组元素的乘积最小而不是和最小,该如何解决?

7.2 性能优化

虽然当前算法已经是线性时间,但在某些特殊情况下可以进一步优化:

  1. 当n超过二进制位数时,可以提前终止(因为current_or已经达到全1)
  2. 如果只需要特定位置的元素值,可以设计O(1)的查询方法
  3. 利用位运算的并行特性,可以使用SIMD指令加速

8. 代码实现与测试案例

8.1 完整Python实现

def construct_min_or_array(n): if n == 0: return [] arr = [0] * n current_or = 0 for i in range(1, n): # 找到current_or的最低未设置位 x = (~current_or) & -(~current_or) arr[i] = arr[i-1] | x current_or |= x # 验证最终current_or是否为2^k-1形式 if (current_or + 1) & current_or != 0: raise ValueError("Cannot construct array satisfying the condition") return arr # 测试案例 test_cases = [ (1, [0]), (2, [0, 1]), (3, [0, 1, 3]), (4, [0, 1, 3, 7]), (5, [0, 1, 3, 7, 15]), ] for n, expected in test_cases: result = construct_min_or_array(n) assert result == expected, f"Failed for n={n}, got {result}, expected {expected}" print("All test cases passed")

8.2 复杂度更高的替代实现

为了更好理解算法,这里提供一个更直观但复杂度更高的实现:

def construct_min_or_array_naive(n): arr = [0] * n current_or = 0 for i in range(1, n): # 找到最小的x使得(current_or | x) > current_or x = 1 while (current_or | x) == current_or: x <<= 1 arr[i] = arr[i-1] | x current_or |= x if (current_or + 1) & current_or != 0: raise ValueError("Cannot construct array satisfying the condition") return arr

这个实现更容易理解,但时间复杂度在最坏情况下是O(n log(max_or)),因为内层循环可能需要遍历所有位。

9. 位运算技巧总结

9.1 常用位运算技巧

解决这道题用到了几个重要的位运算技巧:

  1. 取最低设置位:x & -x
  2. 清除最低设置位:x & (x - 1)
  3. 检查是否为2的幂:x & (x - 1) == 0
  4. 获取最低未设置位:(~x) & -(~x)

9.2 位运算优先级表

在组合多个位运算时,必须清楚它们的优先级(从高到低):

  1. ~ (按位取反)
  2. <<, >> (移位)
  3. & (按位与)
  4. ^ (按位异或)
  5. | (按位或)

在实际编程中,建议使用括号明确优先级,避免混淆。

10. 实际面试中的应用

10.1 面试考察点

这类题目在面试中通常考察:

  1. 对位运算的理解深度
  2. 贪心算法的应用能力
  3. 边界条件的处理能力
  4. 代码实现的简洁性和效率

10.2 面试回答策略

当面试中遇到类似问题时,可以按照以下步骤回答:

  1. 明确题目要求和约束条件
  2. 分析位运算的相关性质
  3. 提出贪心算法的思路并证明其正确性
  4. 讨论时间复杂度和空间复杂度
  5. 考虑边界条件和可能的优化
  6. 编写清晰可读的代码
  7. 用测试案例验证代码正确性

在面试中,沟通思考过程比直接给出正确答案更重要。即使不能立即想到最优解,展示出系统的解题思路也能获得好评。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 2:34:53

C++设计模式之享元模式

前言 享元模式&#xff08;flyweight pattern&#xff09;要解决的是「大量细粒度对象占用过多内存」的问题。它的做法是把对象的状态拆成两部分&#xff1a;可以在多个上下文之间共享的「内在状态」&#xff0c;以及随上下文变化的「外在状态」。内在状态存放在享元对象里&…

作者头像 李华
网站建设 2026/10/26 2:34:46

智能制造数字化转型:五化四型十场景实践指南

1. 智能制造数字化转型全景解析这个102页的PPT浓缩了当前制造业数字化转型的核心方法论与实践路径。作为深耕工业互联网领域多年的从业者&#xff0c;我完整参与过3个灯塔工厂的数字化改造项目&#xff0c;深刻体会到"五化四型十场景"框架在实际落地中的价值。这份方…

作者头像 李华
网站建设 2026/10/26 2:34:15

2026年MES系统核心技术架构与实施指南

1. 制造业数字化转型中的MES系统定位车间里那台老式打卡机终于被拆下来了&#xff0c;取而代之的是每个工位上的平板终端。这个场景正在2026年的制造工厂里加速上演&#xff0c;而背后支撑这场变革的核心系统正是MES&#xff08;制造执行系统&#xff09;。作为连接ERP计划层与…

作者头像 李华
网站建设 2026/10/26 2:32:33

博世BMI160六轴传感器自写驱动:I2C/SPI双总线与避坑指南

简介&#xff1a;面向嵌入式开发者的博世BMI160陀螺仪自写驱动参考代码&#xff0c;聚焦STM32F030平台与传感器之间的I2C通信集成&#xff0c;适合已具备单片机外设开发基础、希望快速打通六轴姿态采集链路的工程师学习借鉴。压缩包共3个文件&#xff0c;约6KB&#xff0c;以C源…

作者头像 李华
网站建设 2026/10/26 2:31:41

LVS+Keepalived+NFS构建高可用Web集群实战

1. 项目概述在互联网服务架构中&#xff0c;Web集群的高可用性直接关系到业务连续性和用户体验。传统单点部署的Web服务器在面对突发流量或硬件故障时往往力不从心&#xff0c;而基于LVSKeepalivedNFS的技术栈组合&#xff0c;能够构建一个既具备负载均衡能力&#xff0c;又能实…

作者头像 李华