【发布时间】:2019-06-21 01:36:34
【问题描述】:
我知道在 stackoverflow 中已经提出了一些相关问题。然而,这个问题更多地与三种方法之间的性能差异有关。
问题是:给定一个仅包含正整数的非空数组,求该数组是否可以分成两个子集,使得两个子集中的元素之和相等。 https://leetcode.com/problems/partition-equal-subset-sum/
即 [1, 5, 11, 5] = 真,[1, 5, 9] = 假
通过解决这个问题,我尝试了3种方法:
-
方法 1:动态规划。自上而下递归+记忆(结果:超过时间限制):
def canPartition(nums): total, n = sum(nums), len(nums) if total & 1 == 1: return False half = total >> 1 mem = [[0 for _ in range(half)] for _ in range(n)] def dp(n, half, mem): if half == 0: return True if n == -1: return False if mem[n - 1][half - 1]: return mem[n - 1][half - 1] mem[n - 1][half - 1] = dp(n - 1, half, mem) or dp(n - 1, half - nums[n - 1], mem) return mem[n - 1][half - 1] return dp(n - 1, half, mem) -
方法 2:动态规划。自下而上。 (结果:2208 毫秒 接受):
def canPartition(self, nums): total, n = sum(nums), len(nums) if total & 1 == 1: return False half = total >> 1 matrix = [[0 for _ in range(half + 1)] for _ in range(n)] for i in range(n): for j in range(1, half + 1): if i == 0: if j >= nums[i]: matrix[i][j] = nums[i] else: matrix[i][j] = 0 else: if j >= nums[i]: matrix[i][j] = max(matrix[i - 1][j], nums[i] + matrix[i - 1][j - nums[i]]) else: matrix[i][j] = matrix[i - 1][j] if matrix[i][j] == half: return True return False -
方法3:哈希表(字典)。结果(172 毫秒 接受):
def canPartition(self, nums): total = sum(nums) if total & 1 == 0: half = total >> 1 cur = {0} for number in nums: cur |= { number + x for x in cur} # update the dictionary (hashtable) if key doesn't exist if half in cur: return True return False
关于时间复杂度的上述 3 种方法,我真的不明白两件事:
- 我希望方法 1 和 方法 2 应该有相同的结果。两者都使用表格(矩阵)来记录计算状态,但为什么自底向上的方法更快?
- 我不知道为什么方法 3 比其他方法快得多。注意:乍一看,方法3似乎是2的N次方方法,但它是使用字典丢弃重复值,所以时间复杂度应该是T(n * half)。
【问题讨论】:
-
cur |= { number + x for x in cur} 这是一个非常 Python 的范例,它到底在做什么?
-
如果键不存在更新字典(哈希表)。
-
1 和 2. 这是背包问题的变体,1. 的时间复杂度不是 nW 而是 2^n,在 python 中使用递归是很糟糕的,我相信 leetcode 会适应的,无论如何.对于 2. 时间复杂度是 nW,对于 3. 我需要做更多的工作才能找到预期的运行时间。
-
is { number + x for x in cur} 联合?还是整个过程只是一个简单的插入?
标签: python algorithm hashtable dynamic-programming