You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化类零钱兑换的完全平方数问题Python实现(超时求助)

完全平方数问题代码优化求助

我之前实现过LeetCode的零钱兑换问题,知道完全平方数问题和它逻辑相似,唯一区别是候选数换成了[1,4,9,25…]这类完全平方数。我自己写的代码逻辑是对的,但处理大输入(比如8328)时会超时,希望能优化代码。

问题描述

给定整数n,返回和为n的完全平方数的最少数量。

完全平方数是指某个整数的平方,即一个整数与自身的乘积。例如1、4、9、16都是完全平方数,而3、11不是。

我的实现代码

class Solution:
    def numSquares(self,n:int)->int:
        nums=[]
        # 创建不超过n的完全平方数列表
        for i in range(1,n+1):
            if self.is_perfect(i):
                nums.append(i)
                print(nums)
        def dfs(target, memo={}):
            if target in memo:
                return memo[target]
            if target == 0:
                return []
            if target < 0:
                return None
            shortest_combination = None
            for num in nums:                
                remainder = target - num
                result = dfs(remainder)
                # 不能用if result,因为result可能是空列表会被判定为False
                if result != None:
                    # 拼接组合,这种写法比result+[num]更高效
                    combination = [*result, num]
                    if shortest_combination == None or len(combination) < len(shortest_combination):
                        shortest_combination = combination
            memo[target] = shortest_combination
            return shortest_combination
        return len(dfs(n))
    def is_perfect(self,n):
        for i in range(1,n+1):
            if i*i>n:
                return False
            if i*i==n:
                return True        
        return False

代码问题分析与优化方案

1. 完全平方数生成逻辑低效

原代码遍历1到n的每个数,再调用is_perfect判断是否为完全平方数,时间复杂度是O(n*sqrt(n)),完全没必要。直接遍历i从1开始,计算i²,只要i²<=n就加入列表,时间复杂度降到O(sqrt(n)),同时去掉拖慢速度的print(nums):

nums = []
i = 1
while i*i <= n:
    nums.append(i*i)
    i += 1

2. 递归DFS存储完整组合,内存与时间开销大

原DFS返回完整的组合数组,但我们只需要组合的长度。存储数组会占用大量内存,拼接操作也浪费时间。修改记忆化逻辑,只存储每个target对应的最少完全平方数数量:

def dfs(target, memo={}):
    if target in memo:
        return memo[target]
    if target == 0:
        return 0
    if target < 0:
        return float('inf')
    min_count = float('inf')
    for num in nums:
        remainder = target - num
        count = dfs(remainder)
        if count != float('inf'):
            min_count = min(min_count, count + 1)
    memo[target] = min_count
    return min_count

最后直接返回dfs(n)即可,不用再取长度。

3. 递归DFS的栈溢出风险与性能问题

Python递归深度有限,大n可能触发栈溢出。更高效的是用动态规划(DP)迭代或广度优先搜索(BFS):

方案一:动态规划

DP数组dp[i]表示和为i的最少完全平方数数量,初始化dp[0]=0,其他为无穷大,遍历更新数组:

class Solution:
    def numSquares(self, n: int) -> int:
        # 生成完全平方数列表
        nums = []
        i = 1
        while i*i <= n:
            nums.append(i*i)
            i += 1
        
        dp = [float('inf')] * (n + 1)
        dp[0] = 0
        
        for num in nums:
            for j in range(num, n + 1):
                dp[j] = min(dp[j], dp[j - num] + 1)
        
        return dp[n]

方案二:广度优先搜索(BFS)

BFS的层级对应使用的完全平方数数量,第一次到达n时的层级就是答案,效率更高:

class Solution:
    def numSquares(self, n: int) -> int:
        # 生成完全平方数列表
        nums = []
        i = 1
        while i*i <= n:
            nums.append(i*i)
            i += 1
        
        from collections import deque
        visited = set()
        q = deque()
        q.append((n, 0))
        visited.add(n)
        
        while q:
            current, count = q.popleft()
            if current == 0:
                return count
            for num in nums:
                next_val = current - num
                if next_val >= 0 and next_val not in visited:
                    visited.add(next_val)
                    q.append((next_val, count + 1))

最终优化效果

以上优化后,处理8328这类大输入时,DP和BFS都能快速得到结果,不会超时。

内容的提问来源于stack exchange,提问作者Yilmaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 06:49:06