标签: 回溯
难度: Medium
标签: 回溯
难度: Medium
运行时间: 36 ms
内存: 0.0 MB
import functools class Solution: @functools.lru_cache(maxsize=None) def getFactors(self, n: int) -> List[List[int]]: if n == 1: return [] ans = [] for i in range(2, int(math.sqrt(n)) + 1): if n % i == 0: ans.append([i, n // i]) for f in self.getFactors(n // i): tmp = sorted([i] + f) if tmp not in ans: ans.append(tmp) return ans
这个题解使用递归的方式来求解所有可能的因子组合。主要思路如下: 1. 从2开始遍历到sqrt(n),对于每个数i,如果i能整除n,则将[i, n//i]加入结果。 2. 对于每个满足条件的i,递归调用getFactors(n//i),获取n//i的所有因子组合,将i加入每个组合的开头,并去重后加入结果。 3. 为了避免重复计算,使用@functools.lru_cache装饰器来缓存递归调用的结果。 4. 当n为1时,返回空列表作为递归的终止条件。
时间复杂度: O(logn)
空间复杂度: O(n)
import functools class Solution: @functools.lru_cache(maxsize=None) def getFactors(self, n: int) -> List[List[int]]: if n == 1: return [] # 递归终止条件 ans = [] for i in range(2, int(math.sqrt(n)) + 1): # 遍历从2到sqrt(n)的所有数 if n % i == 0: # 如果i能整除n ans.append([i, n // i]) # 将[i, n//i]加入结果 for f in self.getFactors(n // i): # 递归调用getFactors(n//i) tmp = sorted([i] + f) # 将i加入每个组合的开头 if tmp not in ans: # 去重 ans.append(tmp) return ans
当寻找一个数n的因子时,如果存在因子i使得i乘以j等于n,则必有一个因子不大于sqrt(n),另一个因子不小于sqrt(n)。因此,只需遍历到sqrt(n),对于每个i,可以计算出相应的j(即n/i)。这样可以减少不必要的重复遍历,提高效率。如果遍历到n,会重复计算因子对(i, j)和(j, i),其中i和j大于sqrt(n)。
在递归过程中,为了确保每个因子组合都是唯一的,题解中使用了两种方式:首先,通过将组合排序来保证每个组合内部的因子顺序一致;其次,在添加新的组合到结果列表之前,检查这个组合是否已经存在于结果列表中。具体实现是,通过对每次递归得到的组合添加当前因子i,并排序生成新的组合tmp,然后在添加到最终答案之前,检查tmp是否已经存在于答案列表中,从而避免重复。
`functools.lru_cache`是一个装饰器,用于缓存函数的返回值。当函数使用相同的参数再次被调用时,不需要重新执行函数,而是直接从缓存中获取结果。在递归过程中,尤其是处理具有重复子问题的递归,这可以显著减少计算量,提高性能。LRU(最近最少使用)策略会在缓存满时移除最久未使用的数据,以保持缓存的有效性。在这个题解中,使用lru_cache来存储已经计算过的n的因子组合,避免重复的计算工作。
在因子组合问题中,当n为1时,意味着不能再进行任何因子分解(1不是一个有效的因子)。因此,将n为1作为递归的终止条件,表示当前分支不能再进一步分解,应该停止递归。返回空列表表示这个分支没有找到任何有效的因子组合。这是递归算法设计中的一种常见策略,用于处理基本情况,避免无限递归。