回溯算法理论基础
什么是回溯算法
1. 回溯和递归是相辅相成的,只要有递归就会有回溯
2. 递归函数的下面的部分就是回溯的逻辑
1. 递归结束后向次级函数回退的过程
使用原因以及解决的问题
1. 回溯算法其实就是纯暴力搜索,并不是一个高效的算法
2. 因为有些问题暴力搜索是唯一办法
3. 能解决的问题:
1. 组合问题: 给你个集合找出大小为n的组合
2. 切割问题: 给一个字符串,如何切割才能保证子串都是回文字符串,并有多少种切割方式
3. 子集问题: 罗列出来该集合的子集
4. 排列问题:
5. 棋盘问题: 解数独,N皇后
4. 组合是无序的(1,2)(2,1)算一种,排列是有序的(1,2)(2,1)算两种
如何理解回溯法
1. 回溯算法可以抽象成一个n叉树形结构
1. 树的宽度,就是回溯法中集合的大小
2. 树的深度,递归的深度
2. 递归函数没有返回值
回溯算法模板
// 参数一般是写完逻辑,再填写参数
void backtarcking(参数){
// 递归终止条件
if(终止条件){
// 达到终止条件,说明应该收集结果
return;
}
// 递归单层搜索逻辑
for(集合的元素){//用来处理集合里的每一个元素
// 处理节点
// 递归函数
// 回溯操作;撤销处理节点的操作
}
}
77. 组合
1. 回溯算法通过递归来控制多少层for循环
2. 因为这里是组合,我们去完一个元素就不要取前面的那个元素,不然会重复
1. 1,2; 2,1是一个
3. 求组合的时候,元素是不能重复使用的
剪枝优化
n = 4, k = 4,元素:1,2,3,4
1. 由于只有4个元素,我们目标集需要4个元素为一个组合
2. 当我们选择完元素1之后,就没有必要再看从元素2开始选择了,因为后面都不符合要求,因此我们可以进行减枝
class Solution(object):
def backtracking(self,n,k,startindex,path,result):
# recursion end condition
if len(path) == k:
# we find our leaf,which is our answer set append it to our array
result.append(path[:])
return
# each level recursion logic
"""
1. 已经选择的元素个数:path.size();
2. 还需要的元素个数为:k - path.size();
3. n-(k-len(path))这个是代表 剩下元素中还需要选取的元素个数
4. +1的目的是因为for循环时左闭右闭区间,因为for循环要包含这个区间所以要+1
这里python是+2,因为range是左关右开区间的所以要再+1的基础上再次+1
"""
for i in range(startindex,n-(k-len(path))+2):
# add element into our array
path.append(i)
# recursion start
self.backtracking(n,k,i+1,path,result)
# backtracking
path.pop()
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
result = [] # collect final answer set two dimension array
self.backtracking(n,k,1,[],result)
return result
216.组合总和III
0. 本题和77题类似
1. k等于几说明就需要几层for循环去遍历答案相加的值
2. 剪枝操作:
1. 如果我们的sum大于targetsum的话,直接return没有必要再去做递归了(放在递归停止条件)
2. 如果目前剩余的元素,不够k个目标元素了,说明后面集合没必要去遍历了(放在for循环取数的时候)
1. 所以没有必要遍历到9,遍历到8就可以
2. for循环里面的i是控制集合的起始位置
3. k—path.size: 还需要多少个元素
4. 如果还需要两个元素,i最多到8才能满足我们所却的元素
5. 9-(k-path.size)+1: 代表后面还缺多少个元素,+1的目的是下标补齐
1. 也就是说i最多指向8,才能保证后面元素满足k的要求
3. 假设从i开始取,则从i到n一共有n-i+1个元素,而当前还需要k-path.size()个元素,所以必须满足n-i+1>=k-path.size(),移项就可以得到i<=n+1-(k-path.size())
class Solution(object):
def backtracking(self,k,n,currentSum,startindex,path,result):
"""
k:find all valid combination
n: number that sum up to
sum_: store sum of each array
path: record indivual cobination
result: final result set
"""
# recursion end codition
if len(path) == k:
# reduce unncerssary calucaltion
if currentSum > n:
# if our sum bigger than target value,just return don't recursion
return
# check if they array number can add to n
if currentSum == n:
# we find our valid combination
result.append(path[:])
return
# recursion logic
"""
suppose start from i, from i to n have this amount n-i+1 elements,
currently we still need k-len(path) amount element's
which, n-i+1 >= len(path) then, we can get i <= n-(k-len(path)+1)
"""
for i in range(startindex,9-(k-len(path))+2):
# add current value
currentSum += i
# process each case
path.append(i)
# recursion
self.backtracking(k,n,currentSum,i+1,path,result)
# backtracking
currentSum -= i
path.pop()
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
result = [] # store our final answer result set
self.backtracking(k,n,0,1,[],result)
return result
17.电话号码的字母组合
1. 数字到字符串还需要做一个映射
1. 可以用二维数组或者map做一个映射
2. 二维数组 0 和 1的位置对应的字符串是空
3. 其他数字对应的字符串就是,电话上对应的字母
2. 输入的数字的个数,对应树的深度
3. 每一个数字所对应字母的长度,对应的是树的宽度
class Solution(object):
def __init__(self):
self.leeterMap = [
"", # 0
"", # 1
"abc", # 2
"def", # 3
"ghi", # 4
"jkl", # 5
"mno", # 6
"pqrs", # 7
"tuv", # 8
"wxyz" # 9
]
self.result = []
self.s = ""
def backtracking(self,digits,index):
# recursion stop condition
if index == len(digits):
self.result.append(self.s)
return
# convert index place string to int
digit = int(digits[index])
# get stirng set
letters = self.leeterMap[digit]
for i in range(len(letters)):
# process string
self.s += letters[i]
# recursion
self.backtracking(digits,index+1)
# backtracking
self.s = self.s[:-1]
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
# if digits set is empty
if len(digits) == 0:
return self.result
self.backtracking(digits,0)
return self.result

被折叠的 条评论
为什么被折叠?



