这是我参与11月更文挑战的19天,活动详情查看:2021最后一次更文挑战。
前言
一直都计划学习数据结构与基本算法,但是平时都看一阵停一阵。现在决心坚持下去,我准备从LeetCode的HOT100开始,每天完成1~2道习题,希望通过这种方式养成持续学习的习惯。因为我是做iOS开发的,主要是用Objective-C语言,最近也在学习Swift,所以本系列的题解都将使用swift语言完成,本文更新的是LeetCode中HOT100的第19题039 组合总和。
题目
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
示例 1:
输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
示例 2:
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 1
输出: []
示例 4:
输入: candidates = [1], target = 1
输出: [[1]]
示例 5:
输入: candidates = [1], target = 2
输出: [[1,1]]
提示:
1. 1 <= candidates.length <= 30
2. 1 <= candidates[i] <= 200
3. candidate 中的每个元素都是独一无二的。
4. 1 <= target <= 500
分析
本题其实有点类似全排列,从数组中找出满足条件的组合,再加上每一个元素可以出现多次,所以第一想法就是采用回溯法。
回溯法的特点是递归调用,所以这里涉及到的几个问题就是递归终止条件,递归分支,并且因为要求结果集中的组合不能重复,所以要注意剪枝(本题解中采用贪心算法的思路来实现剪枝和去重)。
1. 回溯终止条件:当前组合中的总和 >= 目标值,等于则保存到结果集
2. 回溯步骤:依次将候选列表的元素添加到组合中,进行回溯
3. 去重剪枝:这一部分包含两个内容,一是将候选列表从大到小进行排序,二是在回溯的时候从当前下标开始,不再重新添加前面的元素
题解
class KLLC039 {
func combinationSum(_ candidates: [Int], _ target: Int) -> [[Int]] {
//候选列表从大到小排序
let nums = candidates.sorted(by: >)
//初始化结果集,当前组合列表
var result = [[Int]]()
var curConbine = [Int]()
//开始调用回溯法
backTrace(nums, target, 0, 0, &curConbine, &result)
return result
}
/// 回溯法
/// - candidates: 候选数组
/// - target: 目标值
/// - curIndex: 当前加入的元素对应的下标
/// - curSum: 当前加入元素的总和
/// - curConbine: 当前加入元素集合
/// - result: 结果集
func backTrace(_ candidates: [Int], _ target: Int, _ curIndex:Int, _ curSum:Int, _ curConbine:inout [Int], _ result:inout [[Int]]) {
if curSum > target { return }
//如果当前元素总和 等于 目标值,则将元素组合保存到结果集中
if curSum == target {
let combine = curConbine
result.append(combine)
}
for (index, item) in candidates.enumerated() {
//不允许加入 上一次加入元素 之前的元素。类似贪心算法
if curSum + item <= target && index >= curIndex {
//加入元素
curConbine.append(item)
//回溯
backTrace(candidates, target, index, curSum + item, &curConbine, &result)
//删除元素,进行下一个元素测试
curConbine.removeLast()
}
}
}
}