☰
LeetCode 401:二进制手表的算法解析与实现
2026/10/29 13:02:22 网站建设 项目流程

1. 问题背景与理解

第一次看到LeetCode 401这道"二进制手表"的题目时,我以为是考察硬件编程或者嵌入式开发。实际上这是一道伪装成硬件问题的经典算法题,考察的是组合数学和位运算的应用。题目要求我们列出所有可能的时间表示,其中小时(0-11)和分钟(0-59)的二进制形式中1的个数之和等于给定的亮灯数。

这个题目巧妙地将时间表示与二进制结合起来,我特别喜欢这种将现实场景抽象为算法问题的设计。它不像某些纯数学题那样枯燥,而是让我们在解决实际问题的过程中掌握核心算法思想。

2. 解题思路分析

2.1 暴力枚举法

最直观的解法就是枚举所有可能的时间组合,然后检查哪些组合满足亮灯数的要求。具体来说:

  1. 遍历所有可能的小时(0-11)
  2. 对于每个小时,遍历所有可能的分钟(0-59)
  3. 计算当前小时和分钟的二进制表示中1的个数总和
  4. 如果总和等于给定的亮灯数,就将这个时间加入结果列表

这种方法的时间复杂度是O(12*60)=O(720),在LeetCode的约束条件下完全够用。虽然看起来有点"暴力",但对于这个问题规模来说是最简单可靠的解法。

2.2 位运算优化

我们可以用位运算来优化1的个数的计算。在C++中可以使用__builtin_popcount函数,Java中可以使用Integer.bitCount,Python中可以使用bin(x).count('1')。

例如,计算5的二进制表示中1的个数:

  • 5的二进制是101
  • bin(5).count('1') = 2

这种优化虽然在大O表示法上没有改变时间复杂度,但实际运行时会更快,因为CPU有专门的指令来处理位运算。

2.3 组合数学方法

更高级的解法是使用组合数学。我们可以:

  1. 遍历所有可能的亮灯数分配方式(小时亮灯数k,分钟亮灯数num-k)
  2. 对于每种分配,找出所有二进制表示有k个1的小时
  3. 找出所有二进制表示有(num-k)个1的分钟
  4. 将这些组合起来形成有效时间

这种方法避免了无效的枚举,理论上更高效,但实现起来稍微复杂一些。

3. 代码实现与解析

3.1 Python实现

def readBinaryWatch(num): def count_bits(n): return bin(n).count('1') result = [] for h in range(12): for m in range(60): if count_bits(h) + count_bits(m) == num: result.append(f"{h}:{m:02d}") return result

这个实现非常直观:

  1. 定义了一个辅助函数count_bits来计算数字的二进制表示中1的个数
  2. 双重循环枚举所有可能的小时和分钟
  3. 检查亮灯数是否匹配
  4. 格式化时间字符串(注意分钟要补零)

3.2 Java实现

import java.util.*; public class Solution { public List<String> readBinaryWatch(int num) { List<String> result = new ArrayList<>(); for (int h = 0; h < 12; h++) { for (int m = 0; m < 60; m++) { if (Integer.bitCount(h) + Integer.bitCount(m) == num) { result.add(String.format("%d:%02d", h, m)); } } } return result; } }

Java实现使用了Integer.bitCount这个内置方法,它是用高效的位操作实现的,性能很好。

3.3 C++实现

#include <vector> #include <string> #include <bitset> using namespace std; vector<string> readBinaryWatch(int num) { vector<string> result; for (int h = 0; h < 12; ++h) { for (int m = 0; m < 60; ++m) { if (bitset<10>(h << 6 | m).count() == num) { result.push_back(to_string(h) + ":" + (m < 10 ? "0" : "") + to_string(m)); } } } return result; }

C++版本使用了bitset来高效计算1的个数。这里有个技巧:将小时和分钟合并成一个10位数(小时占高4位,分钟占低6位),然后用bitset一次性计算总亮灯数。

4. 复杂度分析与优化

4.1 时间复杂度

暴力枚举法的时间复杂度是O(12*60)=O(720),这是一个常数时间复杂度,因为无论输入是什么,我们都要遍历所有可能的时间组合。

4.2 空间复杂度

空间复杂度是O(1)的额外空间(不考虑输出结果),因为我们只需要常数级别的额外空间来存储临时变量。

4.3 可能的优化

虽然暴力法已经足够好,但我们还可以做一些优化:

  1. 预计算所有数字的亮灯数,存储在一个数组中,避免重复计算
  2. 使用组合数学方法提前生成有效的小时和分钟组合
  3. 对于无效的亮灯数(如>8)可以直接返回空列表

5. 边界条件与测试用例

5.1 特殊输入处理

  • num=0:只有"0:00"一个解
  • num>8:无解,因为最大亮灯数是小时(3亮灯) + 分钟(5亮灯) = 8
  • num<0:无效输入,应该返回空列表

5.2 典型测试用例

测试用例1:num=1 期望输出:["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"] 测试用例2:num=2 期望输出应包含:"0:03","0:05","0:06","0:09","0:10","0:12","0:17","0:18","0:20","0:24","0:33","0:34","0:36","0:40","0:48","1:01","1:02","1:04","1:08","1:16","1:32","2:01","2:02","2:04","2:08","2:16","2:32","3:00","4:01","4:02","4:04","4:08","4:16","4:32","5:00","6:00","8:01","8:02","8:04","8:08","8:16","8:32","9:00","10:00"] 测试用例3:num=8 期望输出:["7:31","7:47","7:55","7:59","11:31","11:47","11:55","11:59"]

6. 常见错误与调试技巧

6.1 常见错误

  1. 分钟格式化错误:忘记补零,如"1:2"而不是"1:02"
  2. 亮灯数计算错误:特别是使用位运算时可能出错
  3. 边界条件处理不当:如num=0或num>8的情况
  4. 无效时间包含:如小时>11或分钟>59

6.2 调试技巧

  1. 打印中间结果:在循环中打印当前小时、分钟和亮灯数
  2. 单元测试:为count_bits函数单独编写测试
  3. 小规模测试:先用num=1这样的小输入验证基本逻辑
  4. 对比输出:与预期输出对比,找出差异点

7. 算法扩展与变种

7.1 类似问题

  1. 二进制手表II:给定亮灯数,返回所有可能的时间(就是本题)
  2. 二进制手表I:给定时间,返回亮灯数(更简单)
  3. 数字的二进制表示中1的个数(191. Number of 1 Bits)

7.2 进阶思考

  1. 如果LED可以显示24小时制,如何修改算法?
  2. 如果手表有更多LED(如秒数),如何扩展解法?
  3. 如何高效生成所有满足条件的组合,而不是暴力枚举?

8. 实际应用场景

虽然这个问题看起来有点抽象,但它实际上体现了几个重要的编程概念:

  1. 位运算的应用:如何高效计算二进制表示中1的个数
  2. 组合问题的解法:如何系统地枚举所有可能组合
  3. 问题抽象能力:将现实问题转化为算法问题

这些技能在嵌入式开发、密码学、数据压缩等领域都有实际应用。例如,在嵌入式系统中,我们经常需要检查或设置特定的位;在密码学中,计算汉明重量(1的个数)是一个基本操作。

9. 个人心得与建议

在解决这个问题时,我有几点体会:

  1. 不要被题目描述迷惑:看似硬件问题,实则是算法题
  2. 从简单方法开始:暴力法虽然"笨",但往往是最可靠的起点
  3. 善用语言特性:不同语言提供的位运算工具可以简化代码
  4. 注意输出格式:特别是时间格式化这种细节容易出错

对于刷题建议,我认为这道题适合用来练习:

  • 位运算基础
  • 组合问题的枚举方法
  • 边界条件处理
  • 代码的清晰性和正确性

最后提醒一点:在面试中遇到这类问题时,一定要先和面试官确认输入输出的具体要求,比如时间格式、异常处理等,这能展现你的沟通能力和严谨性。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询