写代码的第二十一天
回溯第三天,加油呀呀呀呀!!!
93.复原IP地址
思路
这个题也是一道切割问题,和昨天的回文串不同之处在于,回文串是可以一直切割一直到字符串结束,但是本题是找合法的IP地址,还是画个图。所以只能切三刀,切完需要判断三刀生成的四个字符串数字格式是否在0-255之间并且不以0开头,所以纵向的递归一共是三层分别是第一刀,第二刀和第三刀。
解决问题1:参数和返回值是什么?参数一定包含本题传入的字符串,并且还要有startindex代表每次切割的位置,以及用来判断切了几刀的pointnum这个参数是为了终止条件设置的;返回值这里也就是树中符合条件的路径,那么本题中用result存储符合条件的输出值。
解决问题2:终止条件是什么?本题中只能切三刀,所以我们要以这个作为终止条件,所以设置一个变量pointnum用来存储切了几刀,一旦等于3了那就证明可以结束了。上次的分割回文串的问题我们使用startindex作为终止条件是因为上次的结束条件是字符串的最后一位,也没限制切几刀,而是一直到最后,所以我们用的startindex表示每次切割的位置也完全可以到达字符串的最后位置,所以是可以用startindex作为终止条件的,但是本题中startindex只有三次,谁也不能确定这个第三次的位置究竟在哪,所以要在设置一个变量用来存储切了几次,其实也就是IP地址中的“.”。
解决问题3:单次回溯的逻辑是什么?横向用for循环,纵向用递归。横向for循环遍历整个字符串,纵向进行递归和回溯。如果切割出来的字符串的数字值大于255,或者不等于零但是首位字符为0,或者字符串中含有其他非正整数的字符,不符合条件。将出了这几种情况之外的字符串append。需要注意一定要把第四段加入进去!!!
正确代码:这个是别人的代码,我理解了一下写出来的。。。。。
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
result = []
self.backtracking(s, 0, 0, "", result)
return result
def backtracking(self, s, start_index, point_num, current, result):
if point_num == 3:
if self.is_valid(s, start_index, len(s) - 1):
current += s[start_index:]
result.append(current)
return
for i in range(start_index, len(s)):
if self.is_valid(s, start_index, i):
sub = s[start_index:i + 1]
self.backtracking(s, i + 1, point_num + 1, current + sub + '.', result)
else:
break
def is_valid(self, s, start, end):
if start > end:
return False
if s[start] == '0' and start != end:
return False
num = 0
for i in range(start, end + 1):
if not s[i].isdigit():
return False
num = num * 10 + int(s[i])
if num > 255:
return False
return True
下面是我自己的思路我没改出来救命了。。。先留下,周末看看
错误第一版:解答错误,发现下面的结果中都是空,也就证明path里面没有值。每个ip地址的分块都是最多三位,一旦四位就超过255了,所以在for循环中不应该从startindex一直遍历到最后一位,而是最多三位,所以应该改为for i in range(startindex,min(startindex + 3, len(s))):。
class Solution:
def __init__(self):
self.path = []
self.result = []
def isvaild(self,s,start,end):
if start > end:
return False
elif s == "":
return False
elif s[0] == "0":
return False
elif int(s) > 255 or int(s) < 0:
return False
return True
def backtracing(self,s,startindex,pointnums):
if pointnums == 3:
if self.isvaild(s,startindex,len(s)-1):
self.result.append('.'.join(self.path))
return
for i in range(startindex,len(s)):
if self.isvaild(s,startindex,i+1):
self.path.append(s[startindex:i+1])
self.backtracing(s,i+1,pointnums+1)
self.path.pop()
def restoreIpAddresses(self, s: str) -> List[str]:
self.backtracing(s,0,1)
return self.result
错误第二版:输出结果没有最后两位数?????最后判断的第四块没进入path。
class Solution:
def __init__(self):
self.path = []
self.result = []
def isvaild(self,s,start,end):
if start > end:
return False
elif s == "":
return False
elif s[0] == "0" and start != end:
return False
elif int(s[start:end+1]) > 255:
return False
return True
def backtracing(self,s,startindex,pointnums):
if pointnums == 4:
if self.isvaild(s,startindex,len(s)-1):
self.result.append('.'.join(self.path))
return
for i in range(startindex,min(startindex + 3, len(s))):
if self.isvaild(s,startindex,i):
self.path.append(s[startindex:i+1])
self.backtracing(s,i+1,pointnums+1)
self.path.pop()
def restoreIpAddresses(self, s: str) -> List[str]:
self.backtracing(s,0,1)
return self.result
78.子集
思路
还是先画图,画个树出来。图中可以很清晰的知道我们要存储的最终结果是哪些,和之前组合的题很像是吧,但是区别在于组合的题我们要看的都是叶子结点的内容,而子集问题要看每一个结点的内容,除了跟结点。
解决问题1:参数和返回值?参数是传入的数组以及每次遍历开始的startinedex,也就是告诉我们下一次递归从哪里开始取;设置全局变量self.path以及self.result两个用来存储结果。
解决问题2:终止条件是什么?当数组中的全部数值都已经取完了,也就是startindex已经到最后了,startindex>=len(nums),的情况也就是到达了叶子结点了,此时终止。
解决问题3:单次回溯的逻辑是什么?横向for,纵向递归。横向的for指的是图中的取1,取2,取3这块。
注释:if终止条件大家可以看见其实在for循环中已经判断了,这个if其实写不写都行,因为本题要的是全部的结点值,不仅仅是叶子结点,所以不管是不是叶子结点的值都要append进result。
正确代码:
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums,startindex):
self.result.append(self.path.copy())
if startindex >= len(nums):
return
for i in range(startindex,len(nums)):
self.path.append(nums[i])
self.backtracing(nums,i+1)
self.path.pop()
def subsets(self, nums: List[int]) -> List[List[int]]:
self.backtracing(nums,0)
return self.result
90.子集II
思路
又是似曾相识的题,先画图。
解决问题1:参数和返回值?参数是传入的数组以及每次遍历开始的startinedex,也就是告诉我们下一次递归从哪里开始取;设置全局变量self.path以及self.result两个用来存储结果。
解决问题2:终止条件是什么?当数组中的全部数值都已经取完了,也就是startindex已经到最后了,startindex>=len(nums),的情况也就是到达了叶子结点了,此时终止。
解决问题3:单次回溯的逻辑是什么?横向for,纵向递归。横向的for指的是图中的取1,取2,取3这块。上面的所有都和前一个题一样,现在看一下区别:本题给的有重复的值,也就是遍历过一次下次碰见直接跳过,所以可以先排序。
错误第一版:只有从第一个数开始的,后面的数没有了。因为for循环中的if判断语句应该是i > startindex。
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums,startindex):
self.result.append(self.path.copy())
for i in range(startindex,len(nums)):
self.path.append(nums[i])
if i > 0 and nums[i] == nums[i-1]:
continue
else:
self.backtracing(nums,i+1)
self.path.pop()
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
self.backtracing(nums,0)
return self.result
错误第二版:每次遍历都把前面的数值append进去了,应该是先判断这个数在之前有没有用过,如果有的话就在进行append和递归操作。
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums,startindex):
self.result.append(self.path.copy())
for i in range(startindex,len(nums)):
self.path.append(nums[i])
if i > startindex and nums[i] == nums[i-1]:
continue
self.backtracing(nums,i+1)
self.path.pop()
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
nums = sorted(nums)
self.backtracing(nums,0)
return self.result
错误第三版:说了排序排序!!!没写排序我真服了。
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums,startindex):
self.result.append(self.path.copy())
for i in range(startindex,len(nums)):
if i > startindex and nums[i] == nums[i-1]:
continue
self.path.append(nums[i])
self.backtracing(nums,i+1)
self.path.pop()
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
self.backtracing(nums,0)
return self.result
正确代码:
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums,startindex):
self.result.append(self.path.copy())
for i in range(startindex,len(nums)):
if i > startindex and nums[i] == nums[i-1]:
continue
self.path.append(nums[i])
self.backtracing(nums,i+1)
self.path.pop()
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
nums = sorted(nums)
self.backtracing(nums,0)
return self.result