二进制手表

标签: 位运算 回溯

难度: Easy

二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。每个 LED 代表一个 0 或 1,最低位在右侧。

  • 例如,下面的二进制手表读取 "4:51"

给你一个整数 turnedOn ,表示当前亮着的 LED 的数量,返回二进制手表可以表示的所有可能时间。你可以 按任意顺序 返回答案。

小时不会以零开头:

  • 例如,"01:00" 是无效的时间,正确的写法应该是 "1:00"

分钟必须由两位数组成,可能会以零开头:

  • 例如,"10:2" 是无效的时间,正确的写法应该是 "10:02"

示例 1:

输入:turnedOn = 1
输出:["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"]

示例 2:

输入:turnedOn = 9
输出:[]

提示:

  • 0 <= turnedOn <= 10

Submission

运行时间: 24 ms

内存: 0.0 MB

class Solution:
    def readBinaryWatch(self, num: int) -> List[str]:
        return ['%d:%02d' %(h,m) for h in range(12) for m in range(60) if (bin(h) + bin(m)).count('1') == num]

Explain

这个题解的思路是使用列表生成式(List Comprehension)枚举所有可能的时间组合。外层两个循环分别枚举小时(0到11)和分钟(0到59),然后使用bin()函数将小时和分钟转换为二进制字符串,并计算其中'1'的个数。如果'1'的个数等于输入的turnedOn,则将该时间格式化为字符串并加入结果列表。最后返回生成的结果列表。

时间复杂度: O(1)

空间复杂度: O(1)

class Solution:
    def readBinaryWatch(self, num: int) -> List[str]:
        # 使用列表生成式枚举所有可能的时间组合
        return ['%d:%02d' %(h,m) for h in range(12) for m in range(60) \
                # 将小时和分钟转换为二进制,并统计其中 1 的个数
                if (bin(h) + bin(m)).count('1') == num]

Explore

在二进制手表中,小时和分钟的显示是基于LED灯的数量来表示二进制数。由于小时的最大值为11,因此至少需要4个LED灯(0000到1011)来表示所有可能的小时数。对于分钟,最大值为59,需要6个LED灯(000000到111011)来表示所有可能的分钟数。因此,小时和分钟的范围必须限制在0到11和0到59之间,以确保时间的正确表示并避免超出二进制数的范围。

直接连接两个二进制字符串后统计'1'的个数通常不会引起误判,因为这里的目的是统计两个数字(小时和分钟)的二进制表示中'1'的总个数,而不关心具体'1'出现在哪个具体位置。每个数字转换成二进制后的字符串是独立的,虽然连接在一起,但只要我们关注的是'1'的数量而非其位置,就不会有边界混淆的问题。

虽然使用`'%d:%02d' % (h, m)`进行字符串格式化是有效的,但它不是现代Python中推荐的做法。Python 3.6及以上版本推荐使用f-string(格式化字符串字面量),因为它们更易读、更简洁且执行速度更快。例如,可以使用f'{h}:{m:02d}'来替代。相比之下,.format()方法也比'%d:%02d'更易读,但f-string在性能上更有优势。

确实,应该在实现中考虑turnedOn的非法值。添加输入验证可以防止程序在接收到不合理的输入时产生错误或不确定的行为。例如,可以在函数开始时检查turnedOn是否在合理的范围内(例如0到10,因为手表最多显示10个LED灯),如果不在,则直接返回空列表或抛出异常。这样的处理可以增加程序的健壯性和可用性。