回溯算法(递归+队列实现)

回溯算法

回溯是一种试探算法,和暴力搜索法相比,根据每一步小心试探得到的情况进行评估,如果当前的情况已经无法满足要求,那么我们就没有必要继续进行下去,可以帮助我们避免走很多弯路。

在回溯中,当出现非法的情况时,算法可以回退到之前的情景,可以是返回一步,有时候甚至可以返回多步,然后再去尝试别的路径和办法。

 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
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值