401.二进制手表
【LetMeFly】两种方法详解:401.二进制手表
力扣题目链接:https://leetcode.cn/problems/binary-watch/
二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。每个 LED 代表一个 0 或 1,最低位在右侧。
- 例如,下面的二进制手表读取
"3:25"
。
(图源:WikiMedia - Binary clock samui moon.jpg ,许可协议:Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) )
给你一个整数 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
方法一.1:二进制枚举1024种状态,从中选择合法的时和分
一共有10盏灯,每盏灯有开与不开两种状态,也就是说一共有$2^{10}=1024$种状态
因此我们可以枚举这$1024$种状态,对于某种状态,首先判断其中二进制下有多少个$1$,如果$1$的个数恰好和$turnedOn$相同,就把“小时”和“分钟”取出来,看是否为合法时间。
关于如何确定二进制下有多少个$1$,可以参考我的博客:https://blog.letmefly.xyz/2022/09/28/LeetCode 0338.比特位计数/,这篇博客用三种方法进行了讲解。
怎么取出“小时”和“分钟”呢?我们上述枚举种,可以将4位的“时”作为高4位,6位的“分”作为低6位
那么,想要取出“分”,直接将“状态码”与上一个二进制下的“$111111_{(2)}$”即可。而$111111_{(2)}=(1 << 6) - 1$
想要取出“时”,可以先将高4位取出来(与上一个$1111000000_{(2)}$)。而$1111000000_{(2)}=1111111111_{(2)}-111111_{(2)}=(1 << 10) - 1 - 111111_{(2)}$
取出高4位后,再右移6位,得到的即为“分”
- 时间复杂度$O(1)$,运算量几乎是固定的,可以理解为$O(1)$
- 空间复杂度$O(1)$,力扣答案不计入算法空间复杂度
AC代码
C++
1 |
|
方法一.2:对方法一.1的小简化
总体思路保持不变,但具体细节可以简化。
首先是判断二进制状态码有多少个1,这一点完全可以参考我博客中的其他方法,比如使用内置函数__builtin_popcount()
,这就让代码简洁了不少
其次是取出高位的“时”。方法一.1中我们先取出了高4位,之后右移了6位得到了“时”。但是不难发现,既然要右移,那么低6位原本是什么就无关紧要了。因此可以直接状态数右移6位,直接得到“时”。
再次是“分”,因为$111111_{(2)}=63$,所以我们就没有必要再在程序中使用位运算来计算了,可以直接$state & 63$就得到了“分”
- 时间复杂度$O(1)$
- 空间复杂度$O(1)$
AC代码
C++
1 |
|
相比于方法一.1,运算量小了一丢丢,代码也简洁了不少。
方法二:枚举合法的时和分,判断二进制下是否恰好为turned个1
方法一中,我们枚举了所有可能的状态,因此还需要判断某种状态是否表示合法的时间。
在方法二中,不如我们换个思路,直接在合法的时间范围内枚举不就好了么。
两层循环,第一层枚举“时”,第二层枚举“分”
这样所有的时间都是合法的了。
再计算“时”和“分”二进制下是否恰好为trunedOn
个1,如果是,就是答案的情况之一。
- 时间复杂度$O(1)$
- 空间复杂度$O(1)$
AC代码
C++
1 |
|
相比于方法一,运算量再次减少了一些。方法一中枚举了$1024$种可能状态,其中包含一些不合法的时间。
而方法二只枚举了$12\times60=720$种状态,每种枚举的状态都是合法的时间状态。
同步发文于CSDN,原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/127318166