电话号码的字母组合
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
我的解答:
主要的思路是将输入的数字字符串从后向前遍历,每个字符进行单字符的映射,并将所有单字符映射与目标集合中的所有字符串拼接,形成新的字符串集合。拼接的过程,就是递归实现的部分。
具体的实现思路如下:
(1)建立字典映射表;
(2)从后向前遍历当前数字字符串;
(3)若当前数字字符串长度超过 1,则从当前字符串的第 2 位到末尾作为子字符串,将该子串作为输入参数,重新输入该函数,这里即为递归的实现。
(4)字典中查找当前字符串的首位数字对应的所有字符,并对目标集合进行双重遍历,实现首位数字对应字符与目标集合中所有字符串的拼接;
class Solution:
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
resultStr = []
dicts = {2:['a','b','c'],
3:['d','e','f'],
4:['g','h','i'],
5:['j','k','l'],
6:['m','n','o'],
7:['p','q','r','s'],
8:['t','u','v'],
9:['w','x','y','z']}
#数字是空的情况
if len(digits) == 0:
return []
#只剩下一位数字的情况(递归终止条件)
if len(digits) == 1:
return dicts[int(digits[0])]
#递归(除了数字的首位,其余均给递归函数)
result = self.letterCombinations(digits[1:])
#字符串拼接操作
for i in dicts[int(digits[0])]:
for j in result:
resultStr.append(i+j)
return resultStr
生成括号
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
我的解答:
思路:对于括号的组合,要考虑其有效性。比如说,)(, 它虽然也是由一个左括号和一个右括号组成,但它就不是一个有效的括号组合。 那么,怎样的组合是有效的呢?对于一个左括号,在它右边一定要有一个右括号与之配对, 这样的才能是有效的。所以,对于一个输出,比如说(()()), 从左边起,取到任意的某个位置得到的串,左括号数量一定是大于或等于右括号的数量, 只有在这种情况下,这组输出才是有效的。我们分别记左,右括号的数量为left和right, 如下分析可看出,(()())是个有效的括号组合。
参考:Leetcode22. Generate Parentheses(生成有效的括号组合)
class Solution:
def generateParenthesis(self, n):
"""
:type n: int
:rtype: List[str]
"""
res = []
self.DFS(n,n,'',res)
return res
def DFS(self,left,right,s,res):
if left == 0 and right == 0:
res.append(s)
else:
if left > 0:
self.DFS(left-1,right,s+'(',res)
if right > left:
self.DFS(left,right-1,s+')',res)
这道题整体代码逻辑非常清晰,主函数+递归函数, DFS的left , right 分别表示左右括号的剩余数量,s表示目前制造的括号。高含金量的逻辑出现在DFS函数中的else部分。首先我们都知道base条件是左括号和右括号的数量都为0时。巧妙在于,我们的DFS一开始只会进入 if left > 0:里的函数,第一次函数递归完毕返回后,还是停留在left = 1 , s='(('的状况下,而且返回点在if left>0:下。接着它会进入right>left里,进行一次递归后又会进入if left > 0:里的递归函数。这就是DFS的魔力,它通过2个看起来平行的递归入口,通过代码顺序制造递归的先后,最终达到了深度优先搜索。
全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
我的解答:
class Solution:
def permute(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
self.res = []
subList = []
self.DFS(nums,subList)
return self.res
def DFS(self,nums,subList):
#判断subList是否全满
if len(subList) == len(nums):
self.res.append(subList[:]) #在最后的结果中添加这个全满的子列
for m in nums:
#如果子列中已经含有了m,就跳出这个循环
if m in subList:
continue
#如果子列中没有m,就添加m
subList.append(m)
#递归
self.DFS(nums,subList)
#从子列中移除m(回溯?)
subList.remove(m)
假设我们输入的nums是[1,2,3],下面简单讲下顺序执行过程:
DFS(nums,[])
m = 1
subList = [1]
#下面进入第二层DFS(nums,[1])
m = 2
subList = [1,2]
#下面进入第三层DFS(nums,[1,2])
m = 3
subList = [1,2,3]
#下面进入第四层DFS(nums,[1,2,3])
self.res.append(subList[:])
#下面返回到第三层DFS
subList.remove(3)
subList = [1,2]
#下面返回到第二层DFS
subList.remove(2)
subList = [1]
#关键在这里!这时候m=2,所以下一个for循环后m=3
m = 3
subList = [1,3]
#下面进入到第三层DFS(nums,[1,3])
m = 2
subList = [1,3,2]
#下面进入第四层DFS(nums,[1,3,2])
self.res.append(subList[:])
......
......
每次subList.append(m)以后,递归返回之后都会对应一个subList.remove(m),这个大概就是回溯的体现。
子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
我的解答:
三层嵌套,不过每一层循环的起点是上一层对应位置+1,这样避免出现123,132这种重复情况
处理流程:
遍历开始前,先把[]加入list
第一层i1=0,”1“
第二层i2=i1+1=1,”12“
第三层i3=i2+1=2,”123“
回溯到第二层,i2++后i2=2,”13“
进入到第三层由于i3=i2+1=3,到边界之外了,直接返回到第二层
第二层i2++后,i2=3,越界返回第一层
第一层i1++后,i1=2,”2“
第二层i2=i1+1=2,“23“,回退
第一层i1++后,i1=3,”3“,退出
class Solution:
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
self.res = []
s = []
self.DFS(nums,s,0)
return self.res
def DFS(self,nums,s,index):
if len(nums) == index:
self.res.append(s)
return
self.DFS(nums,s+[nums[index]],index+1)
self.DFS(nums,s,index+1)
下面是顺序执行过程:
#第一层
search(nums,[],0)
#第二层
search(nums,[1],1)
#第三层
search(nums,[1,2],2)
#第四层
search(nums,[1,2,3],3)
index == len(nums)
#这里添加[1,2,3]到结果,然后return
#执行第三层中的下一个search
search(nums,[1,2],3)
#(新的)第四层
index == len(nums)
#这里添加[1,2]到结果,然后return
#这时候第三层执行到终点,然后return
#执行第二层下一个search
search(nums,[1],2)
#(新的)第三层
search(nums,[1,3],3)
#(新的)第四层
index == len(nums)
#这里添加[1,3]到结果,然后return
#执行第三层中的下一个search
search(nums,[1],3)
#(新的)第四层
index == len(nums)
#这里添加[1]到结果,然后return
#这时候第三层执行到终点,然后return
#这时候第二层执行到终点,然后return
#执行第一层下一个search
search(nums,[],1)
#(新的)第二层
search(nums,[2],2)
#(新的)第三层
search(nums,[2,3],3)
#(新的)第四层
index == len(nums)
#这里添加[2,3]到结果,然后return
......
......
单词搜索
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board = [ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ] 给定 word = "ABCCED", 返回 true. 给定 word = "SEE", 返回 true. 给定 word = "ABCB", 返回 false.
我的解答:
DFS,典型的深度优先遍历,对每一点的每一条路径进行深度遍历,遍历过程中一旦出现:
1.数组越界。
2.该点已访问过。
3.该点的字符和word对应的index字符不匹配。
就要对该路径进行剪枝。
class Solution:
def exist(self, board, word):
"""
:type board: List[List[str]]
:type word: str
:rtype: bool
"""
visited = [[False for j in range(len(board[0]))] for i in range(len(board))]
for i in range(len(board)):
for j in range(len(board[0])):
if self.DFS(board,i,j,word,0,visited):
return True
return False
def DFS(self,board,i,j,word,k,visited):
#判断是否搜索到最后一个字母
if k == len(word):
return True
#判断是否越界/是否访问过/是否匹配,剪枝操作
if i<0 or j<0 or i>=len(board) or j>=len(board[0]) or visited[i][j] == True or board[i][j] != word[k]:
return False
##上面都不符合,说明匹配
#标记该点为访问过
visited[i][j] = True
#递归搜索
result = self.DFS(board,i-1,j,word,k+1,visited) or\
self.DFS(board,i+1,j,word,k+1,visited) or\
self.DFS(board,i,j-1,word,k+1,visited) or\
self.DFS(board,i,j+1,word,k+1,visited)
#遍历过后,将该点还原为未访问过
visited[i][j] = False
return result