持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
题目
你将得到一个整数数组 matchsticks ,其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次 。
如果你能使这个正方形,则返回 true ,否则返回 false 。
示例 1:
输入: matchsticks = [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: matchsticks = [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
提示:
1 <= matchsticks.length <= 151 <= matchsticks[i] <= 10^8
思考
本题难度中等。
首先是读懂题意。我们需要用 所有的火柴棍 拼成一个正方形。注意每根火柴都不能折断且必须使用一次。
我们首先可以计算这些火柴的和,判断是否是4的倍数(也就是初步判断能否组成正方形的四条边)。还可以判断最长的火柴是否超出正方形的边长,若超出一定不能组成正方形。
我们可以使用回溯算法来解决问题。首先是定义数组edges,作为正方形的四条边,逐一进行判断。对火柴数组从大到小排序,每次对于每条边添加一根火柴,如果不超出边长则继续添加,超出边长则舍弃当前火柴,继续添加下一根火柴。依此类推,直至所有火柴都使用到了为止,此时返回true。
解答
方法一:回溯
/**
* @param {number[]} matchsticks
* @return {boolean}
*/
var makesquare = function(matchsticks) {
// 求和、计算正方形的边长
let sum = matchsticks.reduce((prev, cur) => prev + cur, 0)
if (sum % 4 !== 0) {
return false
}
let sideLength = sum / 4
// 从大到小排序
matchsticks.sort((a, b) => b - a)
// 最长火柴的长度大于边长,无法拼成
if (matchsticks[0] > sideLength) {
return false
}
const edges = new Array(4).fill(0)
return dfs(0, matchsticks, edges, sideLength)
}
const dfs = (index, matchsticks, edges, len) => {
if (index === matchsticks.length) {
return true
}
for (let i = 0; i < edges.length; i++) {
// 第i个边使用火柴matchsticks[index]
edges[i] += matchsticks[index]
// 不超出len则继续调用dfs,判断是否可以使用下一个索引的火柴
if (edges[i] <= len && dfs(index + 1, matchsticks, edges, len)) {
return true
}
// 超出len则回溯
edges[i] -= matchsticks[index]
}
return false
}
// 执行用时:300 ms, 在所有 JavaScript 提交中击败了9.95%的用户
// 内存消耗:40.5 MB, 在所有 JavaScript 提交中击败了99.92%的用户
// 通过测试用例:190 / 190
复杂度分析:
- 时间复杂度:O(4^n),其中 n 是火柴的数目。每根火柴都可以选择放在 4 条边上。
- 空间复杂度:O(n)。递归栈需要占用 O(n) 的空间。