个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创双指针(3)_快慢指针_快乐数问题
收录于专栏【经典算法练习】
本专栏旨在分享学习C++的一点学习笔记,欢迎大家在评论区交流讨论💌
目录
1.题目链接:
2.题目描述:
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
示例 1:
输入:n = 19 输出:true 解释: 12 + 92 = 82 82 + 22 = 68 62 + 82 = 100 12 + 02 + 02 = 1
示例 2:
输入:n = 2 输出:false
提示:
1 <= n <= 231 - 1
3.题目分析:
为了方便叙述,将「对于⼀个正整数,每⼀次将该数替换为它每个位置上的数字的平方和」这一个操作记为x
题目告诉我们,当我们不断重复 x 操作的时候,计算一定会「死循环」,死的方式有两种:
▪ 情况一:一直在 1 中死循环,即 1 -> 1 -> 1 -> 1......
▪ 情况二:在历史的数据中死循环,但始终变不到 1
由于上述两种情况只会出现一种,因此,只要我们能确定循环是在「情况一」中进行,还是在「情况二」中进行,就能得到结果。
示例一:
示例二:
简单证明:
a. 经过一次变化之后的最大值是: 9^2 * 10 = 810 ,也就是在题目要求变化的区间 [1, 2^31-1=2147483647] ,选一个更大的最大( 999999999 ), 也就是变化区间在[1, 810] 之间;
b. 根据「鸽巢原理」(n个巢,n+1个鸽,至少有一个巢里面的鸽子数大于1),一个数变化 811 次之后,必然会形成⼀个循环;
c. 因此,变化的过程最终会走到⼀个圈里面,因此可以用「快慢指针」来解决。
4.解法:
算法思路:
根据上述的题目分析,我们可以知道,当重复执行 x 的时候,数据会陷入到一个「循环」之中。 而「快慢指针」有一个特性,就是在一个圆圈中,快指针总是会追上慢指针的,也就是说他们总会相遇在一个位置上。如果相遇位置的值是 1 ,那么这个数一定是快乐数;如果相遇位置不是 1 的话,那么就不是快乐数。
证明为什么两个快慢指针一定会相遇?两个快慢指针为什么相遇点是入环的起始点?
这个我就不过多证明,详细证明大家可以看下面的博客,里面关于链表的带环问题进行了详细的分析与证明:
算法流程:
1. 单独实现将每个数的位数进行平方求和的函数(因为在题目中会多次使用)
2. 定义两个快慢指针slow和fast
3. 当我们的slow != fast时,说明slow还没与fast相遇,那么slow++,fast += 2;
当我们的slow == fast时,说明slow与fast相遇,且相遇点是入环起始点,那么我们只需要判断这个点的值是否为1,为1则为快乐数,否则不是.
注意:我们的循环判断条件为slow != fast,那么我们两个指针初始化时,不能同时初始化为n,应该将slow = n, fast = n各个位数的平方和;
代码展示:
class Solution {
public:
int numsum(int n)
{
int sum = 0;
while(n)
{
int x = n % 10;
sum += x * x;
n /= 10;
}
return sum;
}
bool isHappy(int n) {
int slow = n, fast = numsum(n);
while(slow != fast)
{
slow = numsum(slow);
fast = numsum(numsum(fast));
}
if(slow == 1) return true;
else return false;
}
};
结果分析:
顺利通过!!!
4.总结:
这道题考察了双指针算法中的快慢指针,快慢指针在平常算法中还算比较常见,希望大家可以通过这道题掌握这种算法,如果大家还想练习的话,可以参考下面的博客--有关链表的带环问题,进行加深练习!!!
对了,如果这篇博客对你有帮助的话,不要忘记赞👍 收藏✨ 留言✉ 加关注💓哦!!
大家也可以订阅我的经典算法专辑[经典算法练习],每天都会更新一道经典算法(正常来说),让你遇到算法不在害怕!!!