我的思路
- 返回可能的所有时间,即穷举,考虑回溯算法
- 回溯算法,labuladong给的套路如下:
List<E> result = new ArrayList<>();
void backtrack(路径,选择列表){
if(满足结束条件){
result.add(路径);
return ;
}
for( 选择 : 选择列表 ){
做选择;
backtrack(更新的路径,选择列表);
撤销选择;//路径回退
}
}
两种回溯
- 针对组合问题的回溯方法(本题)
void backtrack(/*路径*/int led/*选择列表0-9省略*/, /*路径终止条件*/int turnedOn,/*去重*/int start){
if( (led>>6) >=12 || (led & 0x3F) >= 60){
return ;
}
if( Integer.bitCount(led) == turnedOn){
temp.add(led);
return;
}
for(int i = start; i < 10; ++i){//有重复,如何去重?
if( ( (led>>i) & 1 ) == 1 ) //说明第i个灯已经亮了
continue;
//做选择,让第i个灯亮
led += 1<<i ;
backtrack(led, turnedOn, i+1);
//撤销选择,让第i个灯灭
led -= (1<<i);
}
}
- 可以看到,在函数的参数列表中,多了两个,一个是路径的终止条件,一个是用来去重的,因为是组合问题。为什么要有一个参数来控制选择列表呢,考虑三个led灯的简略问题,当亮两个led时,根据算法首先变成001→011( add )→001→101( add )→001→000→010→011( 重复 ),当递归到根节点的第二颗子树的时候,子树的子树又从0开始选择,这就造成了重复解,因为这是一个选择问题,只要十个灯里面亮五个就可以了,亮的顺序无所谓的,因此知道了组合问题的求解,排列问题就很简单了,直接贴代码
- 其次,判断条件(剪枝)一定要放在函数的开头,先剪枝后递归,恰好递归到最后一次值超了,这种情况也会被添加进去,而先递归再剪枝,可以防止不满足的情况被添加进去
2.针对排列问题的回溯:只需要去掉start每次从头开始选择即可
官方题解:打表或者暴力
class Solution {
public List<String> readBinaryWatch(int turnedOn) {
List<String> ans = new ArrayList<String>();
for (int i = 0; i < 1024; ++i) {
int h = i >> 6, m = i & 63; // 用位运算取出高 4 位和低 6 位
if (h < 12 && m < 60 && Integer.bitCount(i) == turnedOn) {
ans.add(h + ":" + (m < 10 ? "0" : "") + m);
}
}
return ans;
}
}