)
合并三元组凑出目标值LeetCode 1899 Merge Triplets to Form Target 贪心解法全解析附 Python / Java / C 等多语言实现【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode本篇技术指南围绕 LeetCode 1899 号问题「Merge Triplets to Form Target」展开系统讲解如何利用贪心算法判断若干三元组能否通过「按位取最大值」的合并操作精确构成目标三元组并在当前开源仓库Leetcode solutions中以 Python、Java、C、JavaScript、C#、Go、Kotlin、Swift、Rust 等多语言实现为佐证。读完本文你将掌握两类贪心解法集合收集法与布尔标记法的推导思路、边界过滤规则与 O(n) 时间 / O(1) 空间的复杂度分析并能够准确规避该题最常见的三类编码陷阱。题目背景与问题定义该题对应的 LeetCode 题号为1899仓库内的实现文件均以此为前缀命名如 python/1899-merge-triplets-to-form-target-triplet.py、cpp/1899-merge-triplets-to-form-target-triplet.cpp。题目给出一个二维数组triplets与一个目标数组target两者中的每个元素都是长度为 3 的三元组triplet。允许的操作是任意选择两个三元组[a1, b1, c1]与[a2, b2, c2]合并得到[max(a1, a2), max(b1, b2), max(c1, c2)]。合并可以不断进行结果还可以继续与其他三元组合并问题要求判断是否存在一系列合并操作使得最终结果恰好等于target。例如仓库 cpp/1899-merge-triplets-to-form-target-triplet.cpp 的注释中给出的示例输入triplets [[2,5,3],[1,8,4],[1,7,5]]target [2,7,5]输出true解释合并第 1 个与第 3 个三元组得到[max(2,1), max(5,7), max(3,5)] [2,7,5]恰好等于目标。前置知识在动手求解之前需要具备以下三方面基础能力对应 articles/merge-triplets-to-form-target.md 中的 Prerequisites 部分数组Arrays能够熟练遍历并逐位比较二维数组中的元素贪心算法Greedy Algorithms理解「每一步做局部最优选择最终拼出全局最优解」的核心思想集合 / 哈希集合Sets / Hash Sets用集合记录已满足的目标下标从而验证所有必要条件是否被覆盖。仓库 hints/merge-triplets-to-form-target.md 中对本题的提示也印证了求解方向先忽略所有大于目标的三元组再从剩余合法三元组中检查目标值是否全部可被覆盖并建议将时间、空间复杂度分别控制在O(n)与O(1)。核心观察合并只会让值「变大」无论采取哪种贪心写法第一步都是建立同一个关键认知合并运算对每个位置取的是最大值因此合并过程中每个位置的值只增不减。由此立刻推导出两条重要规则任何在某个位置上大于目标值的三元组都必须被丢弃。因为合并只会让值变得更大一旦某个值超过目标对应位置就永远无法再降回来该三元组参与任何合并都会「污染」结果对于剩余「每个位置都不超过目标」的合法三元组只要它能与目标在某个位置上相等它就为覆盖该位置提供了一份「贡献」。如果能在合法三元组中分别找到覆盖目标第 0、1、2 个位置的贡献来源那么把它们依次合并最终结果就能精确等于目标——因为取最大值的过程中每个位置要么由某个恰好等于目标值的元素保持要么由更小的值被「顶」到目标值而绝不会超过目标。解法一贪心 集合收集Hash Set 版直觉用一个集合good记录「当前已被合法三元组覆盖到的目标下标」。遍历所有三元组时先把任何一位超过目标的三元组跳过对剩下的合法三元组检查其每一位是否与目标相等相等就把该下标加入good。遍历结束后若good中已包含{0, 1, 2}全部三个下标则说明目标的三个位置都能被覆盖答案为true。算法步骤初始化空集合good用于记录可被匹配到的目标下标遍历每个三元组t若t中存在任意一位大于目标对应位t[0] target[0]、t[1] target[1]或t[2] target[2]直接跳过该三元组对剩余合法三元组逐位检查i若t[i] target[i]将下标i加入集合good处理完全部三元组后若good同时包含下标{0, 1, 2}返回true否则返回false。多语言实现以下九种语言实现完整覆盖该思路与仓库中的实际提交文件一一对应class Solution: def mergeTriplets(self, triplets: List[List[int]], target: List[int]) - bool: good set() for t in triplets: if t[0] target[0] or t[1] target[1] or t[2] target[2]: continue for i, v in enumerate(t): if v target[i]: good.add(i) return len(good) 3public class Solution { public boolean mergeTriplets(int[][] triplets, int[] target) { SetInteger good new HashSet(); for (int[] t : triplets) { if (t[0] target[0] || t[1] target[1] || t[2] target[2]) { continue; } for (int i 0; i t.length; i) { if (t[i] target[i]) { good.add(i); } } } return good.size() 3; } }class Solution { public: bool mergeTriplets(vectorvectorint triplets, vectorint target) { unordered_setint good; for (const auto t : triplets) { if (t[0] target[0] || t[1] target[1] || t[2] target[2]) { continue; } for (int i 0; i t.size(); i) { if (t[i] target[i]) { good.insert(i); } } } return good.size() 3; } };class Solution { /** * param {number[][]} triplets * param {number[]} target * return {boolean} */ mergeTriplets(triplets, target) { const good new Set(); for (const t of triplets) { if (t[0] target[0] || t[1] target[1] || t[2] target[2]) { continue; } for (let i 0; i t.length; i) { if (t[i] target[i]) { good.add(i); } } } return good.size 3; } }public class Solution { public bool MergeTriplets(int[][] triplets, int[] target) { HashSetint good new HashSetint(); foreach (var t in triplets) { if (t[0] target[0] || t[1] target[1] || t[2] target[2]) { continue; } for (int i 0; i t.Length; i) { if (t[i] target[i]) { good.Add(i); } } } return good.Count 3; } }func mergeTriplets(triplets [][]int, target []int) bool { good : make(map[int]bool) for _, t : range triplets { if t[0] target[0] || t[1] target[1] || t[2] target[2] { continue } for i, v : range t { if v target[i] { good[i] true } } } return len(good) 3 }class Solution { fun mergeTriplets(triplets: ArrayIntArray, target: IntArray): Boolean { val good HashSetInt() for (t in triplets) { if (t[0] target[0] || t[1] target[1] || t[2] target[2]) continue for ((i, v) in t.withIndex()) { if (v target[i]) { good.add(i) } } } return good.size 3 } }class Solution { func mergeTriplets(_ triplets: [[Int]], _ target: [Int]) - Bool { var good SetInt() for t in triplets { if t[0] target[0] || t[1] target[1] || t[2] target[2] { continue } for (i, v) in t.enumerated() { if v target[i] { good.insert(i) } } } return good.count 3 } }impl Solution { pub fn merge_triplets(triplets: VecVeci32, target: Veci32) - bool { let mut good HashSet::new(); for t in triplets { if t[0] target[0] || t[1] target[1] || t[2] target[2] { continue; } for i in 0..3 { if t[i] target[i] { good.insert(i); } } } good.len() 3 } }仓库源码印证该写法与仓库提交完全一致。例如 python/1899-merge-triplets-to-form-target-triplet.py 与 cpp/1899-merge-triplets-to-form-target-triplet.cpp 均采用「先过滤超限三元组再把命中的下标插入集合最后判断集合大小是否为 3」的流程javascript/1899-merge-triplets-to-form-target-triplet.js 的前半部分同样是该思路的实现。复杂度分析时间复杂度$O(n)$——只需单次遍历全部n个三元组每个三元组固定检查 3 个位置空间复杂度$O(1)$——虽然使用了集合但集合中最多只会出现{0, 1, 2}三个下标占用空间为常数级。解法二贪心 布尔标记最优写法直觉集合收集法的本质是「分别确认目标的三个位置都能被某个合法三元组命中」。既然下标只有 3 个完全可以用三个布尔变量x、y、z替代集合让代码更轻量、可读性更强要凑出target[0]至少需要一个三元组满足第 0 位恰好等于target[0]且第 1、2 位都不超过目标同理可推导target[1]、target[2]的覆盖条件。只要三个位置能分别被独立满足把对应的三元组合并起来就能精确得到目标。算法步骤初始化三个布尔标记x→ 能否匹配target[0]y→ 能否匹配target[1]z→ 能否匹配target[2]遍历每个三元组t按如下规则更新标记置x true当且仅当t[0] target[0]且t[1] target[1]且t[2] target[2]置y true当且仅当t[1] target[1]且t[0] target[0]且t[2] target[2]置z true当且仅当t[2] target[2]且t[0] target[0]且t[1] target[1]若在遍历过程中x、y、z同时为true立即返回true提前剪枝若循环结束仍未满足返回false。多语言实现class Solution: def mergeTriplets(self, triplets: List[List[int]], target: List[int]) - bool: x y z False for t in triplets: x | (t[0] target[0] and t[1] target[1] and t[2] target[2]) y | (t[0] target[0] and t[1] target[1] and t[2] target[2]) z | (t[0] target[0] and t[1] target[1] and t[2] target[2]) if x and y and z: return True return Falsepublic class Solution { public boolean mergeTriplets(int[][] triplets, int[] target) { boolean x false, y false, z false; for (int[] t : triplets) { x | (t[0] target[0] t[1] target[1] t[2] target[2]); y | (t[0] target[0] t[1] target[1] t[2] target[2]); z | (t[0] target[0] t[1] target[1] t[2] target[2]); if (x y z) { return true; } } return false; } }class Solution { public: bool mergeTriplets(vectorvectorint triplets, vectorint target) { bool x false, y false, z false; for (const auto t : triplets) { x | (t[0] target[0] t[1] target[1] t[2] target[2]); y | (t[0] target[0] t[1] target[1] t[2] target[2]); z | (t[0] target[0] t[1] target[1] t[2] target[2]); if (x y z) return true; } return false; } };class Solution { /** * param {number[][]} triplets * param {number[]} target * return {boolean} */ mergeTriplets(triplets, target) { let x false, y false, z false; for (let t of triplets) { x | t[0] target[0] t[1] target[1] t[2] target[2]; y | t[0] target[0] t[1] target[1] t[2] target[2]; z | t[0] target[0] t[1] target[1] t[2] target[2]; if (x y z) return true; } return false; } }public class Solution { public bool MergeTriplets(int[][] triplets, int[] target) { bool x false, y false, z false; foreach (var t in triplets) { x | (t[0] target[0] t[1] target[1] t[2] target[2]); y | (t[0] target[0] t[1] target[1] t[2] target[2]); z | (t[0] target[0] t[1] target[1] t[2] target[2]); if (x y z) return true; } return false; } }func mergeTriplets(triplets [][]int, target []int) bool { x, y, z : false, false, false for _, t : range triplets { x x || (t[0] target[0] t[1] target[1] t[2] target[2]) y y || (t[0] target[0] t[1] target[1] t[2] target[2]) z z || (t[0] target[0] t[1] target[1] t[2] target[2]) if x y z { return true } } return false }class Solution { fun mergeTriplets(triplets: ArrayIntArray, target: IntArray): Boolean { var x false var y false var z false for (t in triplets) { x x || (t[0] target[0] t[1] target[1] t[2] target[2]) y y || (t[0] target[0] t[1] target[1] t[2] target[2]) z z || (t[0] target[0] t[1] target[1] t[2] target[2]) if (x y z) return true } return false } }class Solution { func mergeTriplets(_ triplets: [[Int]], _ target: [Int]) - Bool { var x false, y false, z false for t in triplets { if t[0] target[0] t[1] target[1] t[2] target[2] { if t[0] target[0] { x true } if t[1] target[1] { y true } if t[2] target[2] { z true } } if x y z { return true } } return false } }impl Solution { pub fn merge_triplets(triplets: VecVeci32, target: Veci32) - bool { let (mut x, mut y, mut z) (false, false, false); for t in triplets { x | t[0] target[0] t[1] target[1] t[2] target[2]; y | t[0] target[0] t[1] target[1] t[2] target[2]; z | t[0] target[0] t[1] target[1] t[2] target[2]; if x y z { return true; } } false } }仓库源码印证布尔标记法在仓库中同样有直接对应物javascript/1899-merge-triplets-to-form-target-triplet.js 的第三个版本用res[0] | a _a等表达式逐个位置打标记最后检查三个标记是否全为真java/1899-merge-triplets-to-form-target-triplet.java 则用布尔数组greedy[3]完成同样的「过滤超限 → 标记命中 → 三者全真」流程。复杂度分析时间复杂度$O(n)$——单次线性扫描且可在三个标记集齐时提前返回空间复杂度$O(1)$——仅使用三个布尔变量比集合写法更省。变体延伸直接累积按位最大值仓库 C 语言实现除上述两种主流写法外仓库的 c/1899-merge-triplets-to-form-target-triplet.c 还提供了一种等价且非常直观的变体用x、y、z三个变量累积所有合法三元组在每个位置上的最大值最后再与目标逐位比较只把「每一位都不超过目标」的合法三元组纳入累积对每个合法三元组用x max(x, t[i][0])、y max(y, t[i][1])、z max(z, t[i][2])更新结束后判断x target[0] y target[1] z target[2]。这种写法的正确性同样建立在「合并只取最大值、且超限三元组已被过滤」这两个核心观察之上累积结果必然不超过目标若恰好等于目标即说明可行。JavaScript 提交中的第二个版本javascript/1899-merge-triplets-to-form-target-triplet.js也是同一思路用res [Math.max(res[0], a), ...]逐步累积。三种写法殊途同归时间复杂度均为 $O(n)$空间均为 $O(1)$读者可以根据语言习惯任选其一。常见陷阱Common Pitfalls1. 混入超过目标值的三元组任何一位大于目标对应值的三元组都永远不可能参与构成目标——因为合并按位取最大值值只会增大不会减小。若不过滤这类三元组最终结果必然被「顶」超过目标导致错误答案。2. 只检查精确命中却忘记过滤其余位置部分实现只判断某个位置是否等于目标却忘了校验其余位置不超过目标。例如三元组[5, 2, 3]虽然命中了target[0] 5但当target[1] 1时它毫无用处因为第 1 位的2已经超过目标合并它会污染结果。3. 误以为必须找到单个与目标完全相等的三元组题目允许合并多个三元组并不要求存在某个三元组恰好等于目标。正确做法是找到若干合法三元组让它们分别独立地贡献目标的每一个位置值且互不超出目标再通过合并拼出目标。总结「Merge Triplets to Form Target」是贪心算法与数组结合的经典入门题解题链路清晰抓住单调性合并操作按位取最大值值只增不减大胆过滤丢弃任何一位超过目标的三元组缩小候选集分而治之三个目标位置可独立验证——要么用集合收集命中的下标解法一要么用三个布尔标记解法二要么直接累积合法三元组的按位最大值再比较C 语言变体。三类实现的时间复杂度均为 $O(n)$、空间复杂度均为 $O(1)$。需要动手验证时可直接查阅本仓库的 Python 实现、C 实现、Java 实现、JavaScript 实现 与 C 实现 进行对照学习。【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考