经典算法之回溯

解决一个回溯问题,实际上就是一个决策树的遍历过程。

  1. 路径:也就是已经做出的选择。
  2. 选择列表:也就是你当前可以做的选择。
  3. 结束条件:也就是到达决策树底层,无法再做选择的条件。

遍历当前可选择的项,在递归调用之前做选择,在递归调用之后撤销选择

回溯算法的框架:

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
 
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

以上框架可以套到一些经典问题中:

求集合的全排列

问题:给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。

思路:假设集合[1,2,3], 每次可选择的列表为去掉路径中已有的元素,遍历可选择的choose列表,选择一条路(即一个整数),递归,结束条件是choose列表为空,回溯(撤销这次选择,继续遍历...

func permute(nums []int) [][]int {
	if len(nums) == 0{
		return [][]int{}
	}
	var res [][]int
	var backtrack func([]int,[]int)
	backtrack = func(path []int,choose []int) {
		if len(choose) == 0{ 
			res = append(res,path)
		}

		for index, c := range choose{
			leftChoose := []int{}
			leftChoose = append(leftChoose,choose[:index]...)
			leftChoose = append(leftChoose,choose[index+1:]...)
            //新的choose列表 排除当前元素,
			backtrack(append(path,c),leftChoose)
		}
	}
	backtrack([]int{},nums)
	return res
}

求集合的所有子集

问题:给定一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

思路:由于不能包含重复的子集,每次选择之后,新的choose列表是当前path之后的所有元素,即path=[1,],可选择的只有[2,3]; 如果path=[2,],可选择的只有[3,];每次都将path加入到结果中,及时path=[],空集也是其子集之一;

func subsets(nums []int) [][]int {
	if len(nums) == 0{
		return [][]int{[]int{}}
	}
	var res = [][]int{}
	var backtrack func([]int,[]int)
	backtrack = func(path []int,choose []int) {
		res =append(res,path)
		for index,c := range choose{
			addChoose := []int{}
			addChoose = append(addChoose,choose[index+1:]...)
			// 新的choose列表是当前path之后的所有元素
			tmp := append([]int{},path...)
			tmp = append(tmp,c)
			backtrack(tmp,addChoose)
		}
	}
	backtrack([]int{},nums)
	return res
}

求括号的组合方式

问题:正整数 n 代表生成括号的对数,请设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

思路:这道题的每次的选择有两种,左括号或者右括号,在选择时要注意 右括号的数量不能大于左括号的数量 ()))这种path是不允许的;结束条件是括号都匹配完毕

func generateParenthesis(n int) []string {
	if n <= 0{
		return []string{}
	}
	var res []string
	var backtrack func(string,int,int)
	backtrack = func(path string, left, right int) {
		if left == 0 && right == 0{
			res = append(res, path)
		}
		if left > 0{
			backtrack(path+"(",left-1,right)
		}
		if left < right{
			backtrack(path+")",left,right-1)
		}
	}
	backtrack("",n,n)
	return res
}

01背包问题

问题描述:给你一个背包, 最多可以承受的重量为w,那么给你一组物品,每个物品无法分割,问在背包总重量和总物品数量不超过约定数量的前提下,如何做才能够让背包的总重量最重。

func bagWeight(item []int,weight int)int{
	if len(item) == 0 || weight <= 0{
		return 0
	}
	var maxWeight  = 0
	var backtrack func(choose [] int, curWeight int)
	backtrack = func(choose [] int, curWeight int){
		if curWeight > weight{
			return
		}
		if curWeight > maxWeight{
			maxWeight = curWeight
		}
		for index, c := range choose{
			leftChoose := append([]int{},choose[index+1:]...)
			backtrack(leftChoose,curWeight+c)
		}
	}
	backtrack(item,0)
	return maxWeight
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值