回溯算法——GO解决Leetcode

1,46题,全排列

var res [][]int
func permute(nums []int) [][]int {
    res=nil
    helper(nums,[]int(nil))
    return res
}

func helper(nums[]int,tmp []int){
    if len(tmp)==len(nums){
        //不能直接用tmp
        // 这个 tmp 变量是一个地址引用,结束当前递归,将它加入 res,后续的递归分支还要继续进行搜		
		// 索,还要继续传递这个 tmp ,这个地址引用所指向的内存空间还要继续被操作,所以 res 中的 		 
		// tmp 所引用的内容会被改变,这就造成了 res 中的内容随 tmp 变化。
        // 所以要复制 tmp 内容到一个新的数组里,然后放入 res,这样后续对 tmp 的操作,
		// 就不会影响已经放入 res 的内容。 
        temp := make([]int, len(tmp))
        copy(temp,tmp)
        res=append(res,temp)
        return
    }
    for i:=0;i<len(nums);i++{
        if container(tmp,nums[i]){
            continue
        }
        tmp=append(tmp,nums[i])
        //next
        helper(nums,tmp)
        //recover
        tmp=tmp[:len(tmp)-1]
    }
}

func container(nums[]int,num int)bool{
    for i:=0;i<len(nums);i++{
        if nums[i]==num{
            return true
        }
    }
    return false
}

2,47题,含有重复元素的重排列

var res [][]int
var used[]bool
/*
同一轮中即使前后数字相同(即重复)也可以加,但是不同轮中不行。以[1,1,2]为例 在第一轮(初始轮)中,先取了第一个1之后,第二个1依然可以取,并不能被continue掉,说明单纯 if (vis[i] || (i > 0 && nums[i] == nums[i - 1])) { continue; }是不行的,因为这样的话就会执行continue了。 而什么时候需要continue呢?实际上是第一轮结束后,[1,1,2]已经被加入结果集中之后,最外层for循环(也就是取可行解第一个元素的for循环)往后运行,走到下标为1的第二个“1”的位置,我称之为第二轮,在第二轮中先确定可行解第一个元素为“1”之后再调用backtrack进入第二层for循环,for循环依然从i=0开始走,会发现此时0号位的1是false,那么这个“1”加不加呢?答案是不能加,因为假设加了之后,第二轮的可行解的[1,1,2]跟第一轮的可行解[1,1,2]是重复的(唯一的区别就是这里面的1和1对应原数组的下标是互换的)。 所以,!vis[i - 1]这个限制条件的作用主要是针对不同轮。

所以,如果两个相同的数字中(为了容易区分,命名为1a 和 1b),前一个(1a)的标记位为false,说明此时肯定不是在同一轮了(因为每时每刻处于同一轮的一定是标记位同时为true的(好好体会这句话),况且1a还在前面,如果在同一轮中,1a肯定是true,之所以为false,说明是回溯到1a的父层后, 先把1a置false,再for遍历到1b),既然不在同一层,那就要注意,不能在1b的轮中再加入1a了(因为1a的轮中肯定加入过1b了),所以判断1a和1b值相同,同时1a标记位为false,那就果断continue,那如果1b后面还有1c呢?同样的道理,只需要判断1b的状态是不是false,如果是false,说明他俩不在同一轮,1b的轮次中肯定加入过1c了,所以1c的轮次中就可以跳过1b
*/
func permuteUnique(nums []int) [][]int {
    res=nil
    used=make([]bool,len(nums))
    sort.Ints(nums)
    helper(nums,[]int(nil))
    return res
}
func helper(nums[]int,tmp[]int){
    if len(nums)==len(tmp){
        temp:=make([]int,len(tmp))
        copy(temp,tmp)
        res=append(res,temp)
        return
    }
    for i:=0;i<len(nums);i++{
        if used[i]{
            //我们已经选择过的不需要再放进去
            continue
        }
        if i>0&&nums[i]==nums[i-1]&&used[i-1]==false{
            //每次填入的数一定是这个数所在重复数集合中「从左往右第一个未被填过的数字」
            continue
        }
        tmp=append(tmp,nums[i])
        used[i]=true
        helper(nums,tmp)
        //recove
        tmp=tmp[:len(tmp)-1]
        used[i]=false
    }
}

3,39题,组合总和

var res [][]int
func combinationSum(candidates []int, target int) [][]int {
    res=nil
    if len(candidates)<1{
        return res
    }
    sort.Ints(candidates)
    if candidates[0]>target{
        return res
    }
    helper(candidates,target,0,[]int(nil))
    return res
}
func helper(candidates []int,target,start int,tmp []int){
    if target==0{
        temp:=make([]int,len(tmp))
        copy(temp,tmp)
        res=append(res,temp)
        return
    }
    if target<0{
        return
    }
    for i:=start;i<len(candidates);i++{
        //无需保证唯一性,因为数字可以反复
        tmp=append(tmp,candidates[i])
        helper(candidates,target-candidates[i],i,tmp)
        tmp=tmp[:len(tmp)-1]
    }
}

4,40题,组合和2,分析可参考参考

var res [][]int
func combinationSum2(candidates []int, target int) [][]int {
    res=nil
    if len(candidates)<1{
        return res
    }
    sort.Ints(candidates)
    if candidates[0]>target{
        return res
    }
    helper(candidates,target,0,[]int(nil))
    return res
}
func helper(candidates[]int,target,start int,tmp[]int){
    if target==0{
        temp:=make([]int,len(tmp))
        copy(temp,tmp)
        res=append(res,temp)
        return
    }
    if target<0{
        return
    }
    for i:=start;i<len(candidates);i++{
        // 大剪枝:减去 candidates[i] 小于 0,减去后面的 candidates[i + 1]、candidates[i + 2] 肯定也小于 0,因此用 break
        if target-candidates[i]<0{
            break
        }
        // 小剪枝:同一层相同数值的结点,从第 2 个开始,候选数更少,结果一定发生重复,因此跳过,用 continue
        if i>start&&candidates[i]==candidates[i-1]{
            continue
        }
        tmp=append(tmp,candidates[i])
        helper(candidates,target-candidates[i],i+1,tmp)//不重复使用故i+1
        tmp=tmp[:len(tmp)-1]
    }
}

4,77题,组合

var res[][]int
func combine(n int, k int) [][]int {
    res=nil
    if n<1||k>n{
        return res
    }
    helper(n,k,1,[]int(nil))
    return res
}

func helper(n,k,start int,tmp[]int){
    if len(tmp)==k{
        temp:=make([]int,k)
        copy(temp,tmp)
        res=append(res,temp)
        return
    }
    for i:=start;i<=n;i++{
        tmp=append(tmp,i)
        helper(n,k,i+1,tmp)
        tmp=tmp[:len(tmp)-1]
    }
}

5,51题,N皇后问题

var res [][]string
var colFollow[][]string
func solveNQueens(n int) [][]string {
    res=nil
    colFollow=make([][]string,n)
    for i:=range colFollow{
        colFollow[i]=make([]string,n)
    }
    for i:=range colFollow{
        for j:=range colFollow[i]{
            colFollow[i][j]="."
        }
    }
    helper(n,0,colFollow)
    return res
}
func helper(n,row int,colFollow[][]string){
    if row==n{
        tmp:=make([]string,n)
        for i:=0;i<n;i++{
            tmp[i]=strings.Join(colFollow[i],"")
        }
        res=append(res,tmp)
        return
    }
    for col:=0;col<n;col++{
        if isValid(row,col,colFollow,n){
            colFollow[row][col]="Q"//放置
            helper(n,row+1,colFollow)
            colFollow[row][col]="."
        }
    }
}
func isValid(row,col int,colFollow[][]string,n int)bool{
    //check col
    for i:=0;i<col;i++{
        if colFollow[row][i]=="Q"{
            return false
        }
    }
    //check row
    for i:=0;i<row;i++{
        if colFollow[i][col]=="Q"{
            return false
        }
    }
    //check positive
    for i,j:=row-1,col-1;i>=0&&j>=0;i,j=i-1,j-1{
        if colFollow[i][j]=="Q"{
            return false
        }
    }
    //check negetive
    for i,j:=row-1,col+1;i>=0&&j<n;i,j=i-1,j+1{
        if colFollow[i][j]=="Q"{
            return false
        }
    }
    return true
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值