回溯法

回溯

回溯的思想比较简单,但是却可以解决很多问题。

思想

回溯的思想是穷举所有可能的结果去尝试,如果走的通就继续前进,否则就退回重新选择。在穷举的过程中可以利用剪枝去过滤一部分不可能的结果。

适用范围

回溯法是穷举,那么必然会得到一个问题的全部解,进而又可以得到一个问题的最优解。这两点也正是回溯法的适用范围。
这两种不同的情况又引出了两个属于,约束函数和限界函数。两者都属于剪枝函数,不同点在于约束函数是用来过滤不符合条件的解,限界函数用来过滤非最优解。

抽象具体意义
剪枝函数约束函数得到全部解
剪枝函数限界函数得到最优解

经典解空间树

经过上述分析,回溯法就是在解空间中不断的剪枝而得到最终的解。最常见的两种解空间就是子集数和排列树。

子集树

假设问题是求字符串"0123"的子集,结合图来讲解子集树,这样更清楚。第1层的节点代表第一个元素,第n层的节点代表第n个元素,任意一个节点node,都有两个路径走向下一个元素的代表节点,权值为0的路径代表不取node,权值为1的路径代表取node。

image

package main

import "fmt"

func main() {
	in := []byte("abc")
	out := make([]int, len(in))

	subset(in, out, len(in), 0)
}

// in:输入的数据
// out:保存的是对每个元素的选取结果
// size:输入数据的长度,可以不传参,在函数中使用len,这里更方便
// level:层数,本质上是以第几个元素做对象进行选择,编号从0方便编程。
func subset(in []byte, out []int, size, level int) {
	
	//已经访问过叶节点,此时已经生成了一种解
	if level == size {
		fmtRst(in, out)
		return
	}
	
	//当i=1递归时,其实就是从左子树回到当前节点然后选择了右子树的过程
	for i := 0; i <= 1; i++ {
		out[level] = i
		subset(in, out, size, level+1)
	}
}

// 因为利用out切片保存了对每个元素的选择结果
// 但是最终打印的是源数据的子集
// 这个函数根据源数据和对源数据的每个选择来打印
func fmtRst(in []byte, out []int) {
	cnt := 0
	for i, v := range out {
		if v == 0 {
			cnt++
			continue
		}
		fmt.Printf("%c", in[i])
	}

	if cnt == len(in) {
		fmt.Println("{}")
		return
	}
	fmt.Println("")
}

排列树

假设问题是求字符串"123"的全排列,结合图来讲解排列树,这样更清楚。第1层的节点代表所有的源数据,第2层的节点代表的是从第一层的源数据抽取一个元素后剩下的源数据,第n层的节点代表的是从第n-1层的源数据抽取一个元素后剩下的数据。

image

package main

import "fmt"

func main() {
	data := []byte("abc")
	arrange(data, len(data), 0)
}

// in:源数据
// size:源数据个数
// level:当前应该处理哪一层
func arrange(data []byte, size, level int) {
	
	//已经访问过叶节点,此时已经生成了一种解
	if level == size {
		fmt.Println(string(data))
		return
	}

    //level之前都是排列好的
	for i := level; i < size; i++ {
		data[i], data[level] = data[level], data[i]
		arrange(data, size, level+1)
		data[i], data[level] = data[level], data[i]
	}
}

关于作者

大四学生一枚,分析数据结构,面试题,golang,C语言等知识。QQ交流群:521625004。微信公众号:后台技术栈。
image

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值