// 这个框架能够求出数组中所有的具体排列组合
// 比如求:
// 在可重复选取的数组 [1,2,3] 中组成 4 的组合。
// 那么这个框架可以得出的结果集是: [[1,1,1,1],[1,1,2],[2,2],[1,3]],
// 且最终这个结果存放在 resultSet 中。
var resultSet [][]int // 结果集
// 返回结果集的函数
func resultSetReturner() [][]int {
/*
1. 进行一些预处理
a. 如果数组中有重复元素,且该问题是组合问题的话,那么这里需要排序,
为什么要排序? 请看下面的 3. 对框架的一些解答
*/
/* 2. 调用回溯函数 */
/* 5. 返回结果集 */
}
// 回溯函数
func backtracer() {
/* 3. 判断是否需要加入结果集以及进行剪枝 */
isVisit := make(map[int]bool) // 有重复元素才需要这个结构,没有重复元素的话,这个结构可以直接删除。
for i : =0; i < len(nums); i++{
// 判断在该层,这个数字是否已经使用过了
if isVisit[nums[i]] == true {
// 使用过时
continue
}
isVisit[nums[i]] = true
/*
4.继续调用回溯函数,这里会有以下几种情况。
a. 如果题目要求: 求组合数,不能重复选取的话, 那么下一层处理的应该是 nums[i+1:]。
b. 如果题目要求: 求组合数,能重复选取的话, 那么下一层处理的应该是 nums[i:]。
c. 如果题目要求: 求排列数,能重复选取的话,那么下一层处理的应该是 nums[:],即还是 nums。
d. 如果题目要求: 求排列数,不能重复选取的话,那就把nums[i]与nums[0]交换后,处理nums[1:],处理好后再交换回来。
*/
}
}
39. 组合总和
var combinationSequence [][]int // 结果集
// 返回结果集的函数
func combinationSum(candidates []int, target int) [][]int {
/* 1. 进行一些预处理 */
combinationSequence = make([][]int, 0)
/* 2. 调用回溯函数 */
combinationSumExec(candidates, target, make([]int, 0, 100))
/* 5. 返回结果集 */
return combinationSequence
}
// 回溯函数
func combinationSumExec(candidates []int, target int, sequence []int) {
/* 3. 判断是否需要加入结果集以及进行剪枝 */
// 剪枝
if target < 0 {
return
}
if target == 0 {
combinationSequence = append(combinationSequence, newSlice(sequence))
return
}
for i := 0; i < len(candidates); i++ {
/* 4. 继续调用回溯函数 */
// 因为题目要求的是组合数且能重复选取,所以下一层处理的是 candidates[i:]
combinationSumExec(candidates[i:], target-candidates[i], append(sequence, candidates[i]))
}
}
// 深拷贝
func newSlice(oldSlice []int) []int {
slice := make([]int, len(oldSlice))
copy(slice, oldSlice)
return slice
}
40. 组合总和 Ⅱ
var combinationSequence [][]int // 结果集
// 返回结果集的函数
func combinationSum2(candidates []int, target int) [][]int {
/* 1. 进行一些预处理 */
combinationSequence = make([][]int, 0)
sort.Ints(candidates) // 有重复元素的组合问题就要排序。
/* 2. 调用回溯函数 */
combinationSumExec(candidates, target, make([]int, 0, 5))
/* 5. 返回结果集 */
return combinationSequence
}
// 回溯函数
func combinationSumExec(candidates []int, target int, sequence []int) {
/* 3. 判断是否需要加入结果集以及进行剪枝 */
if target < 0 {
return
}
if target == 0 {
combinationSequence = append(combinationSequence, newSlice(sequence))
return
}
isVisited := make(map[int]bool) // 题目有重复元素,所以需要这个结构
for i := 0; i < len(candidates) && target >= candidates[i]; i++ {
if isVisited[candidates[i]] == true {
continue
}
isVisited[candidates[i]] = true
/* 4. 继续调用回溯函数 */
// 因为题目要求的是组合数且不能重复选取,所以下一层处理的是 candidates[i+1:]
combinationSumExec(candidates[i+1:], target-candidates[i], append(sequence, candidates[i]))
}
}
// 深拷贝
func newSlice(oldSlice []int) []int {
slice := make([]int, len(oldSlice))
copy(slice, oldSlice)
return slice
}
90. 子集 Ⅱ
var subsetSequence [][]int // 结果集
// 返回结果集的函数
func subsetsWithDup(nums []int) [][]int {
/* 1. 预处理 */
subsetSequence = make([][]int, 0)
sort.Ints(nums) // 有重复元素的组合问题就要排序。为什么要排序呢?后面会说
/* 2. 调用回溯函数 */
subsetsExec(nums, []int{})
/* 5. 返回结果集 */
return subsetSequence
}
// 回溯函数
func subsetsExec(nums []int, sequence []int) {
/* 3. 判断是否需要加入结果集以及进行剪枝 */
subsetSequence = append(subsetSequence, newSlice(sequence))
isVisit := make(map[int]bool) // 记录数字是否使用过,防止出现重复的结果
for i := 0; i < len(nums); i++ {
if isVisit[nums[i]] {
continue
}
isVisit[nums[i]] = true
/* 4. 继续调用回溯函数 */
// 因为题目要求的是组合数且不能重复选取,所以下一层处理的是 nums[i+1:]
subsetsExec(nums[i+1:], append(sequence, nums[i]))
}
}
// 深拷贝
func newSlice(slice []int) []int {
s := make([]int, len(slice))
copy(s, slice)
return s
}
78. 子集
var subsetSequence [][]int // 结果集
// 返回结果集的函数
func subsets(nums []int) [][]int {
/* 1. 预处理 */
subsetSequence = make([][]int, 0)
/* 2. 调用回溯函数 */
subsetsExec(nums, []int{})
/* 5. 返回结果集 */
return subsetSequence
}
func subsetsExec(nums []int, sequence []int) {
/* 3. 判断是否需要加入结果集以及进行剪枝 */
subsetSequence = append(subsetSequence, newSlice(sequence))
for i := 0; i < len(nums); i++ {
/* 4. 继续调用回溯函数 */
// 因为题目要求的是组合数且不能重复选取,所以下一层处理的是 nums[i+1:]
subsetsExec(nums[i+1:], append(sequence, nums[i]))
}
}
func newSlice(slice []int) []int {
s := make([]int, len(slice))
copy(s, slice)
return s
}
216. 组合总和 Ⅲ
var combinationSequence [][]int // 结果集
func combinationSum3(k int, n int) [][]int {
/* 1. 进行一些预处理 */
candidates := make([]int, 9)
combinationSequence = make([][]int, 0)
for i := 1; i <= 9; i++ {
candidates[i-1] = i
}
/* 2. 调用回溯函数 */
combinationSumExec(candidates, n, k, make([]int, 0, 10))
/* 5. 返回结果集 */
return combinationSequence
}
func combinationSumExec(candidates []int, n int, k int, sequence []int) {
/* 3. 判断是否需要加入结果集以及进行剪枝 */
if n == 0 && k == 0 {
combinationSequence = append(combinationSequence, newSlice(sequence))
return
}
if n == 0 || k == 0 {
return
}
for i := 0; i < len(candidates); i++ {
/* 4. 继续调用回溯函数 */
// 因为题目要求的是组合数且不能重复选取,所以下一层处理的是 candidates[i+1:]
combinationSumExec(candidates[i+1:], n-candidates[i], k-1, append(sequence, candidates[i]))
}
}
// 深拷贝
func newSlice(oldSlice []int) []int {
slice := make([]int, len(oldSlice))
copy(slice, oldSlice)
return slice
}
46. 全排列
var permuteSequence [][]int // 结果集
// 返回结果集的函数
func permute(nums []int) [][]int {
/* 1. 进行一些预处理 */
permuteSequence = make([][]int, 0)
/* 2. 调用回溯函数 */
permuteUniqueExec(nums, []int{})
/* 5. 返回结果集 */
return permuteSequence
}
// 回溯函数
func permuteUniqueExec(nums []int, sequence []int) {
/* 3. 判断是否需要加入结果集以及进行剪枝 */
if len(nums) == 0 {
permuteSequence = append(permuteSequence, newSlice(sequence))
return
}
for i := 0; i < len(nums); i++ {
/* 4. 继续调用回溯函数,这里会有以下几种情况。*/
// 因为题目要求的是排列数,且不可重复选取,所以处理如下。
nums[0], nums[i] = nums[i], nums[0]
permuteUniqueExec(nums[1:], append(sequence, nums[0]))
nums[0], nums[i] = nums[i], nums[0]
}
}
// 深拷贝
func newSlice(oldSlice []int) []int {
slice := make([]int, len(oldSlice))
copy(slice, oldSlice)
return slice
}
47.全排列 Ⅱ
var permuteSequence [][]int // 结果集
// 返回结果集的函数
func permuteUnique(nums []int) [][]int {
/* 1. 进行一些预处理 */
permuteSequence = make([][]int, 0)
/* 2. 调用回溯函数 */
permuteUniqueExec(nums, []int{})
/* 5. 返回结果集 */
return permuteSequence
}
// 回溯函数
func permuteUniqueExec(nums []int, sequence []int) {
/* 3. 判断是否需要加入结果集以及进行剪枝 */
if len(nums) == 0 {
permuteSequence = append(permuteSequence, newSlice(sequence))
return
}
isVisit := make(map[int]bool) // 有重复元素才需要这个结构,没有重复元素的话,这个结构可以直接删除。
for i := 0; i < len(nums); i++ {
if isVisit[nums[i]] == true {
// 使用过时
continue
}
isVisit[nums[i]] = true
/* 4. 继续调用回溯函数,这里会有以下几种情况。*/
// 因为题目要求的是排列数,且不可重复选取,所以处理如下。
nums[0], nums[i] = nums[i], nums[0]
permuteUniqueExec(nums[1:], append(sequence, nums[0]))
nums[0], nums[i] = nums[i], nums[0]
}
}
// 深拷贝
func newSlice(oldSlice []int) []int {
slice := make([]int, len(oldSlice))
copy(slice, oldSlice)
return slice
}
假如有一个不可重复选取的数组 [1, 4, 1]
, 我们要求出组成总和为 5
的所有组合。 我们调用上面的框架,但是不对他进行排序。
那么我们得出的结果是 : [[1, 4], [4, 1]]
,显然,这 2
个组合是一样的,那是什么导致了这个问题呢?
先说说这两个元组的形成原因:
1
后,它发现自己和后面的 4
相加就能组成 5
,于是出现 [1, 4]
这个组合。4
后面,它发现自己和后面的 1
相加就能组成 5
,于是出现 [4, 1]
这个组合。那我们如何避免这种重复组合的情况呢?
最简单的方法就是使用排序,让数组有序化,这样就不会出现 a
与后面的 b
组合后,b
又与后面另外一个 a
进行组合。
于是,[1, 4, 1]
这个数组经过排序后,变为了 [1, 1, 4]
,4
的后面没有 1
,这样就不会导致重复组合的情况了。
而你此时可能会问: 为什么此时不会出现两个[1, 4]
的情况呢?即返回结果是[[1, 4], [1, 4]]
。
这个问题我们已经用框架中的isVisited
这个结构解决了,当第一个 1
被加入 sequence
后,我们把 1
标记为已访问。
之后在该层又遇到 1
, 此时由于我们已经标记了 1
,即此时 isVisited[1] == true
,于是接下来会执行 continue
操作,
跳过了这个1
的后续操作,所以不会出现两个[1, 4]
的情况