Perfect Squares LeetCode

Key takeaways

  • A perfect square is a number that can be expressed as the product of an integer with itself, like 1(11)1 (1*1), 4(22)4 (2*2), or 16(44)16 (4*4).

  • Perfect Squares LeetCode problem statement is given an integer n, determine the minimum number of perfect square numbers that add up to n.

  • Breadth-first search (BFS) is used to determine the minimum number of perfect square numbers that add up to n.

  • The time complexity is approximately is O(nh2)O\left(n^{\frac{h}{2}}\right).

  • The space complexity is also O(nh2)O\left(n^{\frac{h}{2}}\right).

Imagine you have a number that can be expressed as the product of an integer with itself. These numbers are known as perfect squares. For example, if you take the number 11, it can be written as 111*1. Similarly, 44 can be written as 222*2, 99 as 333*3, and 1616 as 444*4. These numbers, called perfect squares, have many applications in various branches of mathematics.

Problem statement

Given an integer n, determine the minimum number of perfect square numbers that add up to n. For example, perfect squares such as 11, 44, 99, and 1616 can be used in the sum, but numbers like 55 and 1010 cannot. Your task is to find the smallest count of these perfect square numbers needed to sum exactly to the given integer n.

Constraints:

  • 11 \leq n 104\leq 10^4

Example

canvasAnimation-image
1 of 4

Knowledge test

Let’s take a moment to solve the quiz to ensure you’ve correctly understood the problem.

Perfect Squares

1

If n = 1414, what will be the correct combination of the perfect squares that sum to n?

A)

1,3,101, 3, 10

B)

1,4,91, 4, 9

C)

3,5,63, 5, 6

D)

2,3,92, 3, 9

Question 1 of 30 attempted

Solution

In this solution, finding the minimum number of perfect square numbers is visualized as a shortest path problem in an unweighted graph where each node represents a remainder of nn after subtracting some perfect squares, and each edge represents the subtraction of a perfect square. Using breadth-first search (BFS), we can explore the shortest path from nn to 00, corresponding to the minimum number of perfect squares that sum to nn.

Let’s look at how the implementation of the algorithm works:

  1. Generate perfect squares:

    1. The first step is to generate a list of perfect squares that are less than or equal to n. These perfect squares will be used to reduce the given number n.

    2. We do this by iterating from 11 up to the integer square root of n (inclusive) and squaring each number.

  2. Initialize BFS variables:

    1. Initialize a level counter level to 00. This counter keeps track of the number of levels we’ve processed in the BFS, corresponding to the number of perfect squares used so far.

    2. Initialize a queue (implemented as a set for efficient membership testing) with the initial value n. This queue will store the remainder as we subtract perfect squares.

  3. BFS loop:

    1. Begin the BFS loop, which continues until the queue is empty.

    2. Increment the level counter by 11 to represent moving to the next depth level in the BFS.

    3. Initialize an empty set next_queue to hold the remainder for the next level of BFS.

  4. Process current level:

    1. Try subtracting each perfect square from each remainder in the current queue.

      1. If the remainder equals a perfect square, return the current level because we’ve found the solution at this level.

      2. If the remainder is less than a perfect square, break out of the inner loop since no further perfect squares can be subtracted meaningfully.

      3. Otherwise, add the new remainder remainder - square_num to the next_queue.

  5. Prepare for the next level:

    1. Assign next_queue to queue to process the next level in the subsequent iteration of the while loop.

  6. Return result:

    1. The function returns the level, which is the minimum number of perfect squares that sum to n.

Let’s look at the illustration below to better understand the solution:

canvasAnimation-image
1 of 11

Let’s look at the code for this solution below:

def perfect_squares_upto_n(n):
# Generate a list of perfect square numbers up to the square root of n
squares = [i * i for i in range(1, int(n**0.5) + 1)]
level = 0
queue = {(n, ())}
# Iterate over each remainder in the current queue
while queue:
level += 1
updated_queue = set()
for remainder, path in queue:
for square in squares:
# If the remainder equals the current perfect square number,
# return the current level as the minimum number of perfect squares
if remainder == square:
path += (square,)
return level, path
# If the remainder is less than the current perfect square number,
# break the loop because further perfect square numbers will be larger
elif remainder < square:
break
# If the remainder is greater than the current perfect square number,
# add the difference to the updatedQueue for further processing
else:
updated_queue.add((remainder - square, path + (square,)))
queue = updated_queue
return level, []
# Driver code
def main():
n = [13, 344, 561, 78, 900]
for number in n:
squares_count, squares_used = perfect_squares_upto_n(number)
print("n = ", number)
print("Minimum squares = ", squares_count)
print("Perfect squares used = ", squares_used)
print("-" * 100)
if __name__ == "__main__":
main()
Perfect Squares

Let’s look at the time and space complexity of this solution:

Time complexity

The time complexity of this solution is O(nh+11n1)=O(nh2)O(\frac{\sqrt{n^{h+1}-1}}{\sqrt{n-1}}) = O(n^\frac{h}{2}) where hh is the height of the N-ary tree and nh+11n1\frac{{n^{h+1}-1}}{{n-1}} is the formula to calculate the number of nodes in a complete N-ary tree. Let’s look at the breakdown of the time complexity:

  • For a complete N-ary tree of height hh and where each node has nn children, the total number of nodes in the tree can be calculated using the formula: nodes=nh+11n1\text{nodes} = \frac{n^{h+1} - 1}{n - 1}. This formula comes from summing the geometric series: 1+n+n2++nh=nh+11n11 + n + n^2 + \dots + n^h = \frac{n^{h+1} - 1}{n - 1}

  • When we traverse all the nodes, the time complexity is directly proportional to the total number of nodes, which is nh+11n1\frac{n^{h+1} - 1}{n - 1}. For large nn, this simplifies to: O(nh+11n1)O(nh)O\left(\frac{n^{h+1} - 1}{n - 1}\right) \approx O\left(n^h\right)

  • The final time complexity is approximately O(nh2)O\left(n^{\frac{h}{2}}\right), indicating that the time complexity grows exponentially with the tree’s height.

Space complexity

The space complexity is O(nh2)O(n^\frac{h}{2}), which represents the maximum number of nodes that can appear at level hh. While we maintain a list of perfect squares square_nums, the primary space usage comes from the queue variable. This variable tracks the remainders that must be visited for a given level in the N-ary tree. In the worst case, the number of nodes at the level hh can be as large as nn raised to the power of h2\frac{h}{2}. This is because:

  • Each level potentially multiplies the number of nodes by a factor related to the number of perfect squares that can be subtracted.

  • Since perfect squares are a sequence like 1,4,9,16,...,1, 4, 9, 16, ..., the number of new nodes generated by a single node at level hh is bounded by n{\sqrt{n}}.

  • If we raise n{\sqrt{n}} to the power of hh, we get nh2n^\frac{h}{2}.

Frequently asked questions

Haven’t found what you were looking for? Contact Us


How do you code a perfect square in Python?

While you can’t directly code a perfect square as a data type in Python, you can create functions or classes to represent and manipulate perfect squares. Here is an example code:

def perfect_squares_upto_n(n):
    # Generate a list of perfect square numbers up to the square root of n
    squares = [i * i for i in range(1, int(n**0.5) + 1)]
    level = 0
    queue = {(n, ())}

    # Iterate over each remainder in the current queue
    while queue:
        level += 1
        updated_queue = set()
        
        for remainder, path in queue:
            for square in squares:
                # If the remainder equals the current perfect square number,
                # return the current level as the minimum number of perfect squares
                if remainder == square:
                    path += (square,)
                    return level, path
                # If the remainder is less than the current perfect square number,
                # break the loop because further perfect square numbers will be larger
                elif remainder < square:
                    break
                # If the remainder is greater than the current perfect square number,
                # add the difference to the updatedQueue for further processing
                else:
                    updated_queue.add((remainder - square, path + (square,)))    
        queue = updated_queue
    
    return level, []

# Driver code
def main():
    n = [13, 344, 561, 78, 900]
    
    for number in n:
        squares_count, squares_used = perfect_squares_upto_n(number)
        print("n = ", number)
        print("Minimum squares = ", squares_count)
        print("Perfect squares used = ", squares_used)
        print("-" * 100)

if __name__ == "__main__":
    main()

What is the logic of a perfect square?

A perfect square is a number that is the product of another integer multiplied by itself. In other words, it’s the square of an integer. For example, 4 is a perfect square because it’s the square of 2(22=4)2 (2 * 2 = 4).


Is 0 a perfect square?

Yes, 0 is a perfect square. It’s the square of 0(00=0)0 (0 * 0 = 0).


Free Resources

Copyright ©2025 Educative, Inc. All rights reserved