1. LeetCode 401题解析:二进制手表的算法实现
二进制手表是LeetCode上一道经典的位运算题目,编号401。这道题要求我们根据给定的LED灯点亮数量,返回所有可能表示的时间。题目看似简单,但涉及到位运算、回溯算法等多个计算机科学核心概念。
1.1 问题描述与理解
题目给出一个非负整数turnedOn,表示当前点亮的LED数量。我们需要返回所有可能的时间,以字符串列表形式返回,并按升序排列。手表有以下特点:
- 顶部有4个LED代表小时(0-11)
- 底部有6个LED代表分钟(0-59)
- 每个LED代表一个0或1,组合起来就是二进制数
例如,当turnedOn=1时,可能的输出包括:
["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"]
1.2 解题思路分析
解决这个问题主要有三种思路:
- 枚举法:遍历所有可能的小时和分钟组合,统计每个组合中1的位数
- 位运算:利用位运算特性直接计算符合条件的组合
- 回溯算法:系统地探索所有可能的LED点亮组合
从效率角度考虑,枚举法虽然直观但时间复杂度较高。位运算方法更为高效,是本题的最佳解法。
2. 位运算解法详解
2.1 核心算法原理
位运算解法的核心思想是利用整数二进制表示中1的个数来匹配题目要求。具体步骤:
- 遍历0-11的所有小时
- 遍历0-59的所有分钟
- 计算小时和分钟的二进制表示中1的个数之和
- 如果等于turnedOn,则将该时间加入结果列表
2.2 关键代码实现
python复制def readBinaryWatch(turnedOn):
def countBits(n):
return bin(n).count('1')
result = []
for h in range(12):
for m in range(60):
if countBits(h) + countBits(m) == turnedOn:
result.append(f"{h}:{m:02d}")
return result
代码解析:
countBits函数使用Python内置的bin函数将整数转换为二进制字符串,然后统计'1'的个数- 双重循环遍历所有可能的小时和分钟组合
- 使用f-string格式化输出时间,确保分钟总是两位数
2.3 复杂度分析
- 时间复杂度:O(1),因为循环次数固定(12*60=720次)
- 空间复杂度:O(1),输出列表大小最多为12*60=720个元素
3. 算法优化与变种
3.1 预计算优化
我们可以预先计算0-59所有数字的二进制1的个数,避免重复计算:
python复制def readBinaryWatch(turnedOn):
bits = [0]*60
for i in range(60):
bits[i] = bin(i).count('1')
result = []
for h in range(12):
for m in range(60):
if bits[h] + bits[m] == turnedOn:
result.append(f"{h}:{m:02d}")
return result
这种优化在多次调用函数时效果更明显。
3.2 位运算技巧进阶
更高级的位运算技巧是使用Brian Kernighan算法计算1的位数:
python复制def countBits(n):
count = 0
while n:
n &= n - 1
count += 1
return count
这个算法每次清除最低位的1,直到n变为0,效率比转换为字符串更高。
4. 回溯算法解法
4.1 回溯思路解析
回溯算法可以系统地探索所有可能的LED点亮组合:
- 将小时和分钟的LED视为一个整体,共10位(4小时+6分钟)
- 从这10位中选择turnedOn位点亮
- 检查选中的位组合是否构成合法时间
- 如果是,则转换为时间格式加入结果
4.2 回溯算法实现
python复制def readBinaryWatch(turnedOn):
def backtrack(pos, turnedOn, hour, minute, result):
if hour > 11 or minute > 59:
return
if turnedOn == 0:
result.append(f"{hour}:{minute:02d}")
return
for i in range(pos, 10):
if i < 4: # 小时LED
backtrack(i+1, turnedOn-1, hour + (1<<i), minute, result)
else: # 分钟LED
backtrack(i+1, turnedOn-1, hour, minute + (1<<(i-4)), result)
result = []
backtrack(0, turnedOn, 0, 0, result)
return result
4.3 回溯算法分析
- 优点:更系统地探索解空间,适合复杂约束条件
- 缺点:实现较复杂,常数因子较大
- 适用场景:当约束条件更复杂时,回溯法可能更灵活
5. 常见问题与调试技巧
5.1 边界条件处理
常见问题包括:
- turnedOn=0时,只有"0:00"一个解
- turnedOn>8时,无解(因为最多4+6=10个LED)
- 分钟格式化要保证两位数
解决方案:
python复制def readBinaryWatch(turnedOn):
if turnedOn == 0:
return ["0:00"]
if turnedOn > 8:
return []
# 正常处理逻辑
5.2 性能优化技巧
- 提前终止不可能的组合:
- 如果当前小时已经需要太多LED,可以跳过剩余分钟
- 如果剩余LED不足以满足要求,可以提前返回
优化示例:
python复制for h in range(12):
h_bits = countBits(h)
if h_bits > turnedOn:
continue
for m in range(60):
if h_bits + countBits(m) == turnedOn:
result.append(f"{h}:{m:02d}")
5.3 测试用例设计
全面的测试用例应包括:
- 常规情况(turnedOn=1,2,3)
- 边界情况(turnedOn=0,8)
- 无效输入(turnedOn=9,10)
- 最大最小情况
示例测试:
python复制test_cases = {
0: ["0:00"],
1: ["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"],
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"],
8: ["7:31","7:47","7:55","7:59","11:31","11:47","11:55","11:59"],
9: []
}
6. 算法扩展与应用
6.1 类似题目推荐
- LeetCode 191. 位1的个数
- LeetCode 78. 子集
- LeetCode 90. 子集 II
- LeetCode 136. 只出现一次的数字
6.2 实际应用场景
- 二进制编码设备的状态显示
- 数字电路设计中的状态表示
- 嵌入式系统中的资源监控
- 计算机图形学中的位图操作
6.3 进阶学习方向
- 位运算的高级技巧
- 组合数学在算法中的应用
- 回溯算法的优化方法
- 状态压缩动态规划
在实际开发中,这类位运算技巧常用于:
- 权限系统的设计(用位表示不同权限)
- 游戏开发中的状态标记
- 高性能计算中的优化手段
掌握二进制和位运算不仅能解决这类算法题,更能提升编程思维和代码效率。建议从简单题目开始,逐步深入理解位运算的各种应用场景。
