回溯算法
回溯是一种试探算法,和暴力搜索法相比,根据每一步小心试探得到的情况进行评估,如果当前的情况已经无法满足要求,那么我们就没有必要继续进行下去,可以帮助我们避免走很多弯路。
在回溯中,当出现非法的情况时,算法可以回退到之前的情景,可以是返回一步,有时候甚至可以返回多步,然后再去尝试别的路径和办法。
1.首先判断当前情况是否非法,如果非法就立即返回
2.看看当前情况是否已经满足递归结束条件,如果是就将当前结果保存起来并返回
3.在当前情况下,遍历所有可能出现的情况并进行的下一步尝试
4.递归完毕后,立即回溯,回溯的方法就是取消前一步进行的尝试
1.电话号码的字母组合
题目:给定一个仅包含数字2-9的字符串,返回所有它能表示的字母组合。
注:这里的数字指电话按键(九宫格)。例如2代表可能的字母:“a\b\c”
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
思路:
1.想出递归终止条件;
2.遍历当前情况下的所有可能;
可以利用递归或队列实现:
class Solution(object):
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
dic = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
if not digits:
return []
res = [""]
#从头开始添加字符,像树一样,从顶开始张开,一层一层...
for i in digits:
#得到当前字符的对应字母集
letters = dic[ord(i)-48]
l = len(res)
#将字母集中的每个字母,加到前一位的结果后面,字符串长度加1
for j in range(l):
t = res.pop(0)
for k in letters:
res.append(t+k)
return res
2.括号生成
题目:给出n代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
示例:n=3,生成结果为:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
思路:
1.非法情况:这里我们可以控制非法状态的发生;即每次添加结果中保证右括号数目小于等于左括号。
2.终止条件:右括号数目j=n,即已经添加了n对括号;
3.当前的情况只有两种:添加左或右括号;
1) 当左括号的个数小于右括号的个数时,可以添加右括号.‘
2) 若左括号的个数小于n,可以添加左括号.
class Solution(object):
def generateParenthesis(self, n):
"""
:type n: int
:rtype: List[str]
"""
res = []
def dfs(s, i, j):
if j == n:
res.append(s)
return
if i < n:
dfs(s + '(',i+1,j)
if j < i:
dfs(s + ')',i,j+1)
dfs("",0,0)
return res
3.全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入:[1,2,3]
输出:[ [1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,2,1],
[3,1,2] ]
思路:
1.终止条件:当前结果的长度等于给定序列长度;
2.添加可能情况:需要一个列表存储剩余未添加的数字;
class Solution(object):
def permute(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = []
#lis代表未添加的数字,tmp为当前序列;
def dfs(tmp, lis):
#终止条件:只剩1个未添加的数字
if len(lis) == 1:
res.append(tmp + lis)
return
for i in range(len(lis)):
dfs(tmp + [lis[i]], lis[:i] + lis[i+1:])
dfs([], nums)
return res
4.单词搜索
题目:给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board = [
['A', 'B', 'C', 'E'],
['S', 'F', 'C', 'S'],
['A', 'D', 'E', 'E'] ]
给定 word = "ABCCED", 返回true
给定 word = “SEE”, 返回true
思路:
index表示word的索引(从0开始),扫描board中的所有字符,若与word[index]相等,则进入循环,index加1;当index到达word末尾时返回true。
1.终止条件:index到达word末尾
2.未避免重复,需要一个列表存储已扫描的二维坐标。将上下左右符合条件的点加入,继续扫描。
class Solution(object):
def exist(self, board, word):
"""
:type board: List[List[str]]
:type word: str
:rtype: bool
"""
lis = []
used = []
for i in range(len(board)):
for j in range(len(board[0])):
if board[i][j] == word[0]:
lis.append([i,j])
used.append([str(i) + '#' + str(j)])
if len(word) == 1:
if lis:
return True
return False
while lis:
t = lis.pop()
y = used.pop()
#四个方向,是否符合条件
if t[0] > 0:
if str(t[0]-1) + '#' + str(t[1]) not in y:
if board[t[0]-1][t[1]] == word[len(y)]:
lis.append([t[0]-1,t[1]])
used.append(y + [str(t[0]-1) + '#' + str(t[1])])
if len(used[-1]) == len(word):
return True
if t[1] > 0:
if str(t[0]) + '#' + str(t[1]-1) not in y:
if board[t[0]][t[1]-1] == word[len(y)]:
lis.append([t[0],t[1]-1])
used.append(y + [str(t[0]) + '#' + str(t[1]-1)])
if len(used[-1]) == len(word):
return True
if t[0] < len(board)-1:
if str(t[0]+1) + '#' + str(t[1]) not in y:
if board[t[0]+1][t[1]] == word[len(y)]:
lis.append([t[0]+1,t[1]])
used.append(y + [str(t[0]+1) + '#' + str(t[1])])
if len(used[-1]) == len(word):
return True
if t[1] < len(board[0])-1:
if str(t[0]) + '#' + str(t[1]+1) not in y:
if board[t[0]][t[1]+1] == word[len(y)]:
lis.append([t[0],t[1]+1])
used.append(y + [str(t[0]) + '#' + str(t[1]+1)])
if len(used[-1]) == len(word):
return True
return False