golang 组合数总和

题面

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

回溯思想

回溯算法实际上一个类似枚举的深度优先搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,
当发现已不满足求解条件时,就“回溯”返回(也就是递归返回),尝试别的路径。

  • 选择 有限个选择,或不选取
  • 条件 不符合条件,就要回溯,即递归调用的返回
  • 结束 把符合要求的解用全局变量存储,作为参数之一传递给递归函数

递归参数选择

  1. 必须要有一个临时变量参数,传递不完整解
  2. 通常用全局变量,存储完整解
  3. 在参数设计中,可以得到结束条件
  4. 确保递归函数返回后,状态可以恢复到递归前,以此达到真正回溯(在使用golang的append尤其要注意,其底层可能改变)

算法分析

树图分解
去重
在这里插入图片描述

golang

append 切片陷阱

import "sort"

func combinationSum(candidates []int, target int) [][]int {
     sort.Ints(candidates)
     rs :=[][]int{}

     var backtrack func(int,int,[]int)
     
     backtrack =func(begin,target int,path []int){
         if begin == len(candidates){
             return
         }
         if target==0 {
             t := make([]int,len(path))
             copy(t,path)
             rs = append(rs,t)
             return 
         }
         for i:=begin;i<len(candidates);i++{
            if  candidates[i] > target{
                break
            }
            path = append(path,candidates[i])
            backtrack(i,target-candidates[i],path)
            path = path[:len(path)-1]
         }
     }

     backtrack(0,target,[]int{})
     return rs
}

python

py列表加法操作返回的是一个全新的副本列表
py基本类型传参时,是值复制

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:

        def dfs(candidates, begin,  path, rs, target):
            if target == 0:
                rs.append(path)
                return

            for index in range(begin, len(candidates)):
                residue = target - candidates[index]
                if residue < 0:
                    break
                dfs(candidates, index, path + [candidates[index]], rs, residue)

        if len(candidates) == 0:
            return []
        candidates.sort()
        path = []
        rs = []
        dfs(candidates, 0, path, rs, target)
        return rs

php

保证回溯状态一致,入参的临时不完全解变量与外层函数保持一致,即要求回溯后的不完全解状态与递归前外层不完解状态一样或相同

class Solution {

    /**
     * @param Integer[] $candidates
     * @param Integer $target
     * @return Integer[][]
     */
    public $rs = [];

    function combinationSum($candidates, $target) {
        sort($candidates);
        $this->candidates = $candidates;
        $this->backTrace(0,[],$target);

        return $this->rs;
    }

    function backTrace($idx,$path,$target){
        if($i==count($this->candidates)){
            return ;
        }
        if($target == 0){
            array_push($this->rs, $path);
        }
        for($i=$idx;$i<count($this->candidates);$i++){
            if($this->candidates[$i]> $target){
                break;
            }
            $path = array_merge($path,[$this->candidates[$i]]);
            $this->backTrace($i,$path, $target-$this->candidates[$i]);
            array_pop($path);
        }
    }
}

n和挑k数

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

解法分析

  • 广度起始点
  • 深度计数器
  • 广度挑选去重,广度回溯状态重置
  • 数据的有序性对去重十分重要
func combinationSum3(k int, n int) [][]int {
    rs :=[][]int{}
    var backtrace func(int,int,[]int)

    backtrace = func(idx int, target int, path []int){
        if idx == 9 {
            return 
        }
        if len(path) >= k {
            if target == 0 {
                t :=make([]int,k)
                copy(t,path)
                rs = append(rs,t)
            }
            return 
        }
        for i:=idx;i<9;i++{
            if target < i+1{
                break
            }
            if len(path)>0 &&  path[len(path)-1]==i+1 {
                continue
            }
            path = append(path, i+1)
            backtrace(i,target-i-1,path)
            path = path[:len(path)-1]
        }
    }
    backtrace(0,n,[]int{})
    return rs
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值