一、解题思路
判断给定数组能否被拆成两个和相等的集合,首先可以通过该数组的和是否能被2整除,来排除一部分情况。
接下来最简单的思路就是通过DFS搜索算法找出该数组中是否存在和值为sum/2的集合:
const canPartition = nums => {
const sum = nums.reduce((x, y) => x + y, 0)
if (sum % 2 !== 0) {
return false
}
nums.sort((a, b) => a - b)
const subSum = sum / 2
let ans = false
dfs(0, 0)
return ans
function dfs (startIndex, sum) {
if (sum === subSum) {
ans = true
return
}
if (sum > subSum) {
return
}
for (let i = startIndex; i < nums.length; i++) {
dfs(i + 1, sum + nums[i])
}
}
}
时间复杂度为O(n^2), 但是该方法通过不了测试用例。
换一种思路思考该问题,实际对于数组中任何一个元素有两种操作:选择或者不选择。最终需要达到和值为sum/2的目标,可见这是一个0/1背包的问题,定义状态:
dp[i][j] 表示组成和值j在前i个元素中是否存在
边界状态:
dp[0][0] = true
dp[0][j] = false j >= 1
dp[i][0] = true
状态转移方程:
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]]
二、 代码实现
const canPartition = nums => {
const sum = nums.reduce((x, y) => x + y, 0)
if (sum % 2 !== 0) {
return false
}
const subSum = sum / 2
const max = nums.length
const dp = new Array(max + 1)
for (let i = 0; i <= max; i++) {
dp[i] = [true]
}
for (let i = 1; i <= subSum; i++) {
dp[0][i] = false
}
for (let i = 1; i <= max; i++) {
for (let j = 1; j <= subSum; j++) {
dp[i][j] = dp[i - 1][j]
const item = nums[i - 1]
if (j >= item) {
dp[i][j] = (dp[i][j] || dp[i - 1][j - item])
}
}
}
return dp[max][subSum]
}
----------------------------
关注「漫谈大前端」
不断成长为一名优秀的前端开发工程师
----------------------------
您还可以在这些地方找到我: