202. 快乐数

170 阅读2分钟

思路: 环形链表那一套

最终只会出现两种情况:

  • 重复多次后平方和变为1
  • 无限进行下去,出现重复的循环。

image.png 为何平方和不会越来越大?

如果当n越来越大的时候,n的每一位数字的平方和总小于n,则说明n不会越来越大。

证明:

方法2:用快慢指针判断。时间复杂度O(n),空间复杂度O(1)

  • 快慢指针一定在环内相遇,

    • 原因:快指针会在圈内一直绕,当慢指针进入圈后,快指针会追赶慢指针,直到套圈。并且慢指针最多走完一个圈的距离
  • 慢指针肯定不会走多圈,会在一圈之内被追上。

    • 假设fast在slow前x个单位,假设环长度为L, fast需要追赶L-x长度,追赶的速度为2-1=1,追赶上所花费的时间为L-x/1 = L-x。在这段时间内,slow走的距离为 1 * (L-x) = L - x, 永远小于L,因此slow不可能走完一整个环。
class Solution {
    public boolean isHappy(int n) {
        int slow = n, fast = n;
        while (fast != 1 && sqrSum(fast) != 1) {//因为fast一次跳两步
            slow = sqrSum(slow);
            fast = sqrSum(sqrSum(fast));
            if (fast == slow) {//如果快慢指针相遇,则出现了循环
                return false;
            }
        }
        return true;
        
    }
    //求n的每一位数字构成的平方和
    public int sqrSum(int n) {
        int sum = 0;
        while (n != 0) {
            sum = sum + (int)Math.pow(n % 10, 2);
            n = n / 10;
        }
        return sum;
    }
}

Find entrance of loop: Let’s say:

  • L = distance from head to loop start
  • C = cycle length
  • x = distance from loop start to meeting point

At the meeting point:

  • fast has traveled L + nC + x
  • slow has traveled L + x
  • Since fast moves twice as fast:
    2(L + x) = L + nC + x
    Simplifies to: L = nC - x
class Solution {
    public boolean isHappy(int n) {
        int slow = n, fast = n;

        // Step 1: Detect cycle
        while (fast != 1 && sqrSum(fast) != 1) {
            slow = sqrSum(slow);
            fast = sqrSum(sqrSum(fast));
            if (slow == fast) {
                // Step 2: Find entrance to the cycle
                int entry = n;
                while (entry != slow) {
                    entry = sqrSum(entry);
                    slow = sqrSum(slow);
                }
                System.out.println("Loop starts at: " + entry);
                return false; // There's a loop, and 1 is not reachable
            }
        }

        return true; // fast == 1 or its next step is 1 → happy number
    }

    public int sqrSum(int n) {
        int sum = 0;
        while (n != 0) {
            sum += (int)Math.pow(n % 10, 2);
            n = n / 10;
        }
        return sum;
    }
}

方法1:用set记录出现过的数字。时间复杂度O(n),空间复杂度O(n)

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<>();
        set.add(n);
        while (sqrSum(n) != 1) {
            if (set.contains(sqrSum(n))) {
                return false;
            }
            n = sqrSum(n);
            set.add(n);

        }
        return true;
    }
  //求n的每一位数字组成的平方和
    public int sqrSum(int n) {
        int sum = 0;
        while (n != 0) {
            sum = sum + (int)Math.pow(n % 10,2);
            n = n / 10;
        }
        return sum;
    }
}