面试刷题:钟针之间的角度
By 青衣极客 Blue Geek In 2020-04-15
很多的问题看起来很难,并不是因为需要很复杂的技巧和经验,而是因为情况比较多。如果能够静下心来把情况分清楚,就像中学数学中常做的对参数分情况讨论一样。情况也不是随便划分的,常常有一些数学关系天然地划分了各种情况。Leetcode Q1344的钟针夹角问题就可以说明这一点,因此,事实上,这是一道中学生,甚至小学生就能做出来的题目。不过,从编程的角度看,除了分情况,我们还需要适度的合并一些情况,因为这样才能让代码更加简洁高效。
1. 题目 Q1344 钟针夹角
给定两个数字,hour和minutes。返回时针和分针之间形成的较小角度(以60进制单位)。
约束条件:
(a) 1 <= hour <= 12
(b) 0 <= minutes <= 59
(c) 实际值的10 ^ -5以内的答案将被认为是正确的。
关于这个问题,我们可以使用下图来进行更加清晰地说明:

有几个常识需要说明一下:
- 钟针转一圈是360度
- 分钟转一圈是60分钟,时针转一圈是12小时
- 一分钟的时间分针转动6度,时针转动30/60=0.5度
- 一小时时针转动30度
一些具体的例子可能更能说明一些具体的问题,下面就展示3个例子的图示:

2. 求解思路
对于给定时间 $h$ 小时 $m$ 分钟,根据问题中的常识,假设0点对应的角度为0度,则我们可以计算出时针和分针的角度:
那么就会有两种很明显的情况存在,即 $D_m > D_h$ 和 $D_m < D_h$。具体演示如图:

这两种情况下,计算夹角时的公式如图所示,但是可以使用绝对值运算简化成一个公式:
从以上公式可以看出,夹角 $D$ 的数值范围是 $[0,360)$,这与题目中的较小角要求不符,因此,关于这个角度是不是较小角,即 $D < 180$ 和 $D > 180$ 能划分成两种个情况。第一种夹角的情况上面已经讨论,接下来就看看第二种夹角的情况示意图。

从以上示意图和夹角小于180的情况可以知道,在夹角大于180的情况下,角度为
到此,这个问题的求解思路就理清楚了。
3. C++解决方案
根据以上思路实现一个解决方案当然是非常容易的,不过,如果对于这四种情况逐个去实现就未免太笨拙。以下给出一种合并的实现方案。
class Solution {
public:
// Runtime: 0 ms, faster than 100.00%
// Memory Usage: 8.4 MB, less than 100.00%
double angleClock(int hour, int minutes) {
double ang_h = minutes * 30 / 60.0 + hour * 30;
int ang_m = minutes * 6;
double diff = abs(ang_h - ang_m);
return diff > 180 ? 360 - diff : diff;
}
};
这个实现中需要注意整数与浮点数的计算规则,在计算时针角度时,使用 $60.0$ 来做浮点数运算,避免整数运算造成的精度损失。 时间复杂度是 $O(1)$。

COMMENT
博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题。
"BG88-面试刷题:钟针之间的角度 Q1344"