快乐数(Happy Number)是LeetCode上一道经典的算法题,编号202。题目要求我们判断一个正整数是否为快乐数。所谓快乐数,是指经过以下过程最终能得到1的数字:
例如数字19:
code复制1² + 9² = 82
8² + 2² = 68
6² + 8² = 100
1² + 0² + 0² = 1
最终得到1,因此19是快乐数。
快乐数问题的核心在于判断一个数字序列是否最终会收敛到1,或者进入无限循环。这实际上是一个循环检测问题,类似于检测链表是否有环。
基于以上观察,我们可以采用以下方法来判断快乐数:
无论采用哪种解法,我们都需要一个辅助函数来计算数字各位的平方和:
c复制int getNext(int n) {
int sum = 0;
while (n) {
int digit = n % 10;
sum += digit * digit;
n /= 10;
}
return sum;
}
这个函数通过取模运算获取数字的每一位,计算平方和,是解决快乐数问题的基础。
哈希表法的核心思想是利用哈希表记录已经出现过的数字。如果在计算过程中某个数字重复出现,说明进入了循环,该数字不是快乐数。
c复制#include <stdbool.h>
int getNext(int n) {
// 同上
}
bool isHappy(int n) {
int seen[1000] = {0}; // 简易哈希表
while (n != 1 && !seen[n]) {
seen[n] = 1;
n = getNext(n);
}
return n == 1;
}
提示:在实际应用中,哈希表的大小需要合理设置。由于数学证明表明数字最终会降到1000以下,所以这里使用1000大小的数组足够。
快慢指针法借鉴了Floyd判环算法的思想,通过两个指针以不同速度遍历序列来检测循环:
如果快指针等于1,则是快乐数;如果快慢指针相遇,说明存在循环。
c复制#include <stdbool.h>
int getNext(int n) {
// 同上
}
bool isHappy(int n) {
int slow = n;
int fast = getNext(n);
while (fast != 1 && slow != fast) {
slow = getNext(slow);
fast = getNext(getNext(fast));
}
return fast == 1;
}
快慢指针法的原理类似于两个人在环形跑道上跑步,速度快的人最终会追上速度慢的人。在数字序列中:
研究表明:
基于上述性质,我们可以:
优化后的快慢指针版本:
c复制bool isHappy(int n) {
while (n != 1 && n != 4) {
n = getNext(n);
}
return n == 1;
}
Q:为什么快慢指针法能检测循环?
A:类似于环形链表问题,快指针最终会追上慢指针。
Q:哈希表法的空间复杂度可以优化吗?
A:可以,因为数字最终会降到1000以下,所以哈希表大小可以限制。
Q:有没有更简单的判断方法?
A:可以检查数字是否等于4,因为非快乐数最终会进入含4的循环。
快乐数问题涉及的思想可以应用于以下类似问题:
这些问题的共同点是都需要检测循环或重复,可以使用类似的快慢指针技巧。
在实际编程中,快乐数问题教会我们几个重要的编程思想:
我在实际解决这个问题时发现,理解Floyd判环算法是关键。最初可能会想用哈希表记录所有出现过的数字,但快慢指针法展示了如何用常数空间解决问题,这是算法设计中非常精妙的思想。