为啥有的数算着算着变1,有的却绕圈圈?Java一秒揪出“快乐数”!

你有没有过这种神奇体验:随便抓个数字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趣味算法手册”(含快乐数、回文数、斐波那契的通俗讲解+代码模板)!关注我,下期揭秘“如何用快乐数原理玩数字小游戏”——聚会时秀一手,朋友都得夸你“懂数学又会玩”~

原文链接:,转发请注明来源!