LeetCode 401. 二进制手表

LeetCode 401 二进制手表

题目描述

二进制手表顶部有 4 个 LED 代表小时(0-11),6 个 LED 代表分钟(0-59)。每个 LED 亮表示对应位为 1。给定一个整数 turnedOn 表示当前亮着的 LED 的数量,返回所有可能表示的时间。你可以按任意顺序返回答案。

小时不会以零开头,例如 "01:00" 是无效的,应为 "1:00"。分钟必须由两位数组成,例如 "10:2" 是无效的,应为 "10:02"

解题思路

本题可以采用二进制枚举的方法。总共有 10 个 LED,每个 LED 只有亮(1)和灭(0)两种状态,因此所有可能的亮灭组合共有 (2^{10} = 1024) 种。我们可以枚举所有组合,即枚举 0 到 1023 的整数,每个整数的二进制表示恰好对应一种 LED 状态。

  • 高 4 位(从右向左第 6 位到第 9 位)代表小时(0~11)。
  • 低 6 位(第 0 位到第 5 位)代表分钟(0~59)。

对于每个枚举值 i,我们需要:

  1. 统计二进制中 1 的个数,即亮着的 LED 数量 s
  2. 如果 s 等于 turnedOn,则进一步分离出小时和分钟:
    • 小时 a = i >> 6(右移 6 位,得到高 4 位)
    • 分钟 b = i & 0b111111(按位与 63,保留低 6 位)
  3. 检查 a 是否在 0~11 之间,b 是否在 0~59 之间,若是则格式化时间存入结果。

该算法简单直接,无需复杂剪枝,且枚举量很小,效率很高。

代码实现

cpp 复制代码
class Solution {
public:
    vector<string> readBinaryWatch(int turnedOn) {
        vector<string> res;          // 存储结果的字符串数组
        char str[10];                 // 临时字符数组,用于格式化时间

        // 枚举所有可能的 10 位二进制数,范围 0 到 1023
        for (int i = 0; i < (1 << 10); i++) {
            int s = 0;                 // 统计亮灯数量

            // 逐位检查是否为 1
            for (int j = 0; j < 10; j++) {
                if (i >> j & 1) {
                    s++;
                }
            }

            // 如果亮灯数量等于要求,则处理这个组合
            if (s == turnedOn) {
                int hour = i >> 6;      // 高 4 位作为小时
                int minute = i & 63;     // 低 6 位作为分钟 (63 = 0b111111)

                // 检查合法性
                if (hour < 12 && minute < 60) {
                    sprintf(str, "%d:%02d", hour, minute);
                    res.push_back(str);
                }
            }
        }
        return res;
    }
};

代码逐行详解

  • vector<string> res;:定义一个字符串向量,用于存放最终结果。
  • char str[10];:C 风格字符数组,配合 sprintf 使用,确保有足够空间存放格式化后的时间字符串(如 "11:59" 长度为 5,加上 \0 共 6 字节,10 足够)。
  • 外层循环 for (int i = 0; i < (1 << 10); i++):遍历 0 到 1023 的所有整数,每个整数对应一种 LED 亮灭状态。1 << 10 是 1024。
  • int s = 0;:计数器,记录当前状态 i 中 1 的个数。
  • 内层循环 for (int j = 0; j < 10; j++):遍历 10 个二进制位。
    • if (i >> j & 1):右移 j 位后与 1 按位与,判断第 j 位是否为 1。注意这里 j 从 0 开始,即从最低位开始判断,但顺序不影响计数结果。
    • s++:如果为 1,计数器加一。
  • if (s == turnedOn):只有当亮灯数量等于题目要求时,才进一步处理。
  • int hour = i >> 6;:将 i 右移 6 位,得到高 4 位。例如 i 的二进制为 hhhh mmmmmm(h 表示小时位,m 表示分钟位),右移 6 位后得到 hhhh,即为小时值。
  • int minute = i & 63;:按位与 63(二进制 111111),得到低 6 位,即为分钟值。
  • if (hour < 12 && minute < 60):判断小时和分钟是否合法(小时范围 0~11,分钟范围 0~59)。
  • sprintf(str, "%d:%02d", hour, minute);:格式化字符串。%d 输出小时无前导零,%02d 输出分钟,保证两位,不足补零。
  • res.push_back(str);:将格式化后的字符串存入结果向量。
  • 循环结束后返回 res

复杂度分析

  • 时间复杂度 :外层循环执行 (2^{10}=1024) 次,内层循环每次固定执行 10 次,所以总操作次数约为 (1024 \times 10 = 10240),可以视为常数时间。因此时间复杂度为 O(1)
  • 空间复杂度 :除了存储答案的向量外,只使用了常数个变量,空间复杂度为 O(1)(不计输出空间)。

总结

本题解利用了二进制枚举的思想,将 LED 状态映射为整数的二进制位,直接遍历所有组合并筛选。这种方法简单易懂,代码简洁,非常适合初学者理解二进制表示和位运算。同时,由于总状态数固定,效率也非常高。

相关推荐
_F_y1 小时前
背包问题动态规划
算法·动态规划
Wect1 小时前
LeetCode 104. 二叉树的最大深度:解题思路+代码解析
前端·算法·typescript
Wect2 小时前
LeetCode 100. 相同的树:两种解法(递归+迭代)详解
前端·算法·typescript
不会敲代码12 小时前
面试必考:如何优雅地将列表转换为树形结构?
javascript·算法·面试
流云鹤2 小时前
数学入门(快速幂&乘法逆元&GCD&质数&组合数)
算法
努力学算法的蒟蒻2 小时前
day88(2.17)——leetcode面试经典150
算法·leetcode·面试
@––––––2 小时前
力扣hot100—系列6-栈
linux·python·leetcode
Anastasiozzzz2 小时前
LeetCode 287 寻找重复数字
算法·leetcode·职场和发展
im_AMBER2 小时前
Leetcode 123 二叉树的层平均值 | 二叉树的右视图 | 二叉树的层序遍历
数据结构·学习·算法·leetcode·二叉树