你有没有过这种神奇体验:随便抓个数字19,把它的每一位平方相加——1^2+9^2=82,再对82做同样操作:8^2+2^2=68,继续算…最后居然变成1!但换个数字4,算来算去却在4→16→37→…→4之间绕圈圈,永远到不了1?这就是数学里的“快乐数”谜题,而Java用“快慢指针”这招,像“抓路痴”一样轻松判断,再也不用跟着数字瞎绕!今天用“操场跑步抓迷路者”的段子给你讲透,看完笑到会写代码,下次朋友问你“19是不是快乐数”,你能秒答~
先懂“快乐数”:像数字版“闯关游戏”,通关到1就是赢家
先给“快乐数”下个接地气的定义:
对于一个正整数,把它每一位数字的平方加起来,得到一个新数;再对新数做同样操作,重复下去。如果最终能得到1,这个数就是“快乐数”(闯关成功);如果永远在几个数字之间绕圈圈(进入循环),就是“非快乐数”(闯关失败,被困住了)。
举两个例子,一看就懂:
- **快乐数19(一路通关到1)**:
19 → 1^2+9^2=82 → 8^2+2^2=68 → 6^2+8^2=100 → 1^2+0^2+0^2=1(停在1,通关!)
- **非快乐数4(绕圈困死)**:
4 → 4^2=16 → 1^2+6^2=37 → 3^2+7^2=58 → 5^2+8^2=89 → 8^2+9^2=145 → 1^2+4^2+5^2=42 → 4^2+2^2=20 → 2^2+0^2=4(又回到4,开始绕圈!)
要是不懂方法,判断一个数是不是快乐数,很容易像“跟着4绕圈”一样,算到天荒地老还没结果——比如算到第20步还没到1,你根本不知道是还在闯关,还是已经被困住了!
核心思路:用“快慢指针”抓“绕圈的数字”,像操场抓路痴
判断快乐数的关键,其实是判断“数字变换过程中会不会进入循环”。而检测循环,最牛的方法就是“快慢指针”,原理和“操场抓迷路者”一模一样:
- 让两个指针(快指针、慢指针)同时从“起点数字”出发,快指针每次“走两步”(连续做两次平方和变换),慢指针每次“走一步”(做一次平方和变换);
- 如果是快乐数(能到1):快指针会先到1,然后一直停在1(因为1的平方和还是1),等慢指针也到1,两者相遇,说明“一路通关,没绕圈”;
- 如果是非快乐数(会绕圈):快指针和慢指针迟早会在“绕圈的某个数字”上相遇,就像操场跑步时,快的人追上了绕圈的慢的人,说明“有人迷路绕圈了”。
这招妙就妙在不用记任何中间数字(省内存),也不用怕算到天荒地老(效率高),两个指针跑起来,相遇时就有答案!
Java实现3步走:操场抓迷路者类比+代码,新手一看就懂
Java实现快乐数判断,核心分“写平方和变换工具”“用快慢指针跑”“判断相遇结果”三步,每一步都对应“操场抓迷路者”场景:
第一步:写“平方和变换工具”——给数字“定跑步规则”
先写一个方法,输入一个数字,返回它每一位平方和的结果,就像给跑步者定“每一步怎么跑”的规则(比如“每跑10米,要跳一下”)。
代码实现:
public class HappyNumber {
// 工具方法:计算一个数的每一位平方和
private static int getSquareSum(int n) {
int sum = 0;
while (n > 0) {
int digit = n % 10; // 取最后一位数字(比如19%10=9)
sum += digit * digit; // 平方后加进总和(9^2=81,sum=81)
n = n / 10; // 去掉最后一位(19/10=1)
}
return sum; // 返回平方和(1^2+81=82)
}
}比如输入19,返回82;输入82,返回68——这就是数字的“跑步步伐”!
第二步:用“快慢指针”让数字“跑起来”
初始化快指针(fast)和慢指针(slow)都指向起点数字(比如要判断的n),然后让快指针每次“跑两步”(连续调用两次getSquareSum),慢指针每次“跑一步”(调用一次),直到两者相遇。
代码实现:
public static boolean isHappy(int n) {
int slow = n; // 慢指针:每次跑一步
int fast = n; // 快指针:每次跑两步
// 让指针跑起来,直到相遇
do {
slow = getSquareSum(slow); // 慢指针跑一步
fast = getSquareSum(getSquareSum(fast)); // 快指针跑两步
} while (slow != fast); // 没相遇就继续跑
// 相遇后判断:如果是1,就是快乐数;否则不是
return slow == 1;
}举个例子,判断n=19(快乐数):
- 慢指针:19→82→68→100→1→1→1…
- 快指针:19→68→1→1→1…
- 最终两者都停在1,相遇后返回true(是快乐数)。
判断n=4(非快乐数):
- 慢指针:4→16→37→58→89→145→42→20→4…
- 快指针:4→37→89→42→4…
- 最终两者在4相遇,返回false(非快乐数)。
第三步:测试验证——秒出结果,再也不绕圈
写个main方法测试一下,不管是快乐数还是非快乐数,都能一秒出答案:
public static void main(String[] args) {
System.out.println(isHappy(19) ? "19是快乐数~" : "19不是快乐数"); // 输出快乐数
System.out.println(isHappy(4) ? "4是快乐数~" : "4不是快乐数"); // 输出不是
}整个过程没有用任何额外数组或集合(空间复杂度O(1)),最多跑两圈就能相遇(时间复杂度O(logn)),比“暴力记数字”快10倍,还不费内存!
避坑提醒:新手常踩的2个“快乐数陷阱”
1. **忘记“1的平方和还是1”,以为指针会一直跑**:比如快指针到1后,下次调用getSquareSum(1)还是1,所以会停在1,等慢指针也到1,不会无限循环。要是误以为“到1后还会变别的数”,就会多写很多没必要的判断——记住:1是“终点站”,到了就停下!
2. **平方和计算时“漏了个位为0的情况”**:比如计算100的平方和,有人会忘记0的平方是0,算成1^2+0+0=1(其实是对的),但要是算20,写成2^2+0=4(也是对的)。这里只要用“n%10取最后一位,n/10去掉最后一位”的逻辑,不管有没有0,都能算对,不用额外处理!
互动时间:来测测你的“快乐数判断力”!
1. 数字7是不是快乐数?算一算:7→49→97→130→10→1,最后到1,所以是快乐数!用今天的代码验证,快慢指针会在哪里相遇?(提示:都在1相遇)
2. 数字111是不是快乐数?算前几步:111→1^2+1^2+1^2=3→9→81→65→61→37→…(进入循环),所以不是。快慢指针会在哪个数字相遇?(提示:循环圈里的某个数,比如3)
3. 你有没有试过自己算一个数是不是快乐数,结果算到头晕还没出结果?评论区说说你算的数字和“绕圈经历”!
评论区交出你的答案,前3名答对的送“Java趣味算法手册”(含快乐数、回文数、斐波那契的通俗讲解+代码模板)!关注我,下期揭秘“如何用快乐数原理玩数字小游戏”——聚会时秀一手,朋友都得夸你“懂数学又会玩”~
