Solution: Happy Number
Let's solve the Happy Number problem using the Fast and slow pointers pattern
Statement#
Write an algorithm to determine if a number is a happy number.
We use the following process to check if a given number is a happy number:
- Starting with the given number , replace the number with the sum of the squares of its digits.
- Repeat the process until:
- The number equals , which will depict that the given number is a happy number.
- It enters a cycle, which will depict that the given number is not a happy number.
Return TRUE if is a happy number, and FALSE if not.
Constraints
Solution#
So far, you have probably brainstormed some approaches and have an idea of how to solve this problem. Let’s explore some of these approaches and figure out which one to follow based on considerations such as time complexity and any implementation constraints.
Naive approach#
The brute force approach is to repeatedly calculate the squared sum of digits of the input number and store the computed sum in a hash set. For every calculation, we check if the sum is already present in the set. If yes, we've detected a cycle and should return FALSE. Otherwise, we add it to our hash set and continue further. If our sum converges to
While this approach works well for small numbers, we might have to perform several computations for larger numbers to get the required result. So, it might get infeasible for such cases. The time complexity of this approach is
Optimized approach using Fast and Slow Pointers#
An efficient approach to solve this problem is to use fast and slow pointers. We know that a unhappy number eventually gets stuck in an infinite loop. However, there is no way for our program to detect this loop and terminate, unless we keep track of the calculated sums, which requires additional space.
If we use the fast and slow pointers approach here, the fast pointer would eventually reach 1, in which case we will return TRUE. Otherwise, it would meet the slow pointer, which would mean that the two pointers are in an endless loop, and we can return FALSE.
As an example, suppose we have the number as our . This is what the infinite loop would look like:
1 of 8
2 of 8
3 of 8
4 of 8
5 of 8
6 of 8
7 of 8
8 of 8
Note: In the following section, we will gradually build the solution. Alternatively, you can skip straight to just the code.
Step-by-step solution construction#
We will start off our solution by constructing a helper function to calculate the squared sum of digits of the input number. We know that we need to isolate the digits in our number to calculate the squared sum. This can be achieved by repeatedly removing the last digit of the number and adding its squared value to the total sum.
The helper function will find the last digit of the given number by taking its modulus with . We’ll store this in a variable digit. Now, since we’ve already separated the last digit, we can get the remaining digits by dividing the number by .
Lastly, we’ll store the squared sum of digit in a variable named total_sum. We’ll repeat this until our number becomes .
To understand this better, consider a number, :
First iteration
digit(last digit)number(remaining digit(s))total_sum
Second iteration
digit(last digit)number(remaining digit(s))total_sum
As the number has become , we’ll terminate our program here. The squared sum of the digits in is .
Next, we’ll initialise two variables fast and slow with the input number, and the sum of its digits respectively. In each iteration, we’ll move slow one step forward and fast two steps forward. That is, we’ll call the sum_of_squared_digits() function once for slow and twice for fast.
slowsum_of_squared_digits(slow)fastsum_of_squared_digits(sum_of_squared_digits(fast))
If at any instance fast becomes , we’ve found a happy number. We’ll return TRUE in this case. Otherwise, if fast becomes equal to slow, we’ve found a loop and will return FALSE.
Just the code#
Here’s the complete solution to this problem:
Solution summary#
We maintain track of two values using a slow pointer and a fast pointer. The slow runner advances one number at each step, while the fast runner advances two numbers. We detect if there is any cycle by comparing the two values and checking if the fast runner has indeed reached the number one. We return True or False depending on if those conditions are met.
Time complexity#
The time complexity for this algorithm is
The worst case time complexity of this algorithm is given by the case of a non-happy number, since it gets stuck in a cycle, whereas a happy number quickly converges to
Before delving into the detailed complexity analysis, let's first consider the largest possible next number for each given number of digits:
Digits | Largest number | Sum of squared digits |
1 | 9 | 81 |
2 | 99 | 162 |
3 | 999 | 243 |
4 | 9999 | 324 |
12 | 999999999999 | 972 |
13 | 9999999999999 | 1053 |
14 | 99999999999999 | 1134 |
To estimate the count of numbers in a cycle, let’s consider the following two cases:
Numbers with three digits: The largest three-digit number is
. The sum of the squares of its digits is . Therefore, for three-digit numbers, no number in the cycle can go beyond . Therefore, the time complexity in this case is given as , where is the maximum count of numbers in a cycle and is the number of digits in a three-digit number. This is why the time complexity in the case of numbers with three digits comes out to be . Numbers with more than three digits: Any number with more than three digits will lose at least one digit at every step until it becomes a three-digit number. For example, the first four-digit number that we can get in the cycle is
, which is the sum of the square of the digits in . Therefore, the time complexity of any number with more than three digits can be expressed as . Since is the dominating term, we can write the time complexity as .
Therefore, the total time complexity comes out to be
Space complexity#
The space complexity for this algorithm is
Happy Number
Linked List Cycle