递归套路解决链表问题:
1.找终止条件:当head指向链表只剩一个元素的时候,自然是不可能重复的,因此return
2.想想应该返回什么值:应该返回的自然是已经去重的链表的头节点
每一步要做什么:宏观上考虑,此时head.next已经指向一个去重的链表了,而根据第二步,我应该返回一个去重的链表的头节点。因此这一步应该做的是判断当前的head和head.next是否相等,如果相等则说明重了,返回head.next,否则返回head
例一:
我们以leetcode 24为例子,题目描述:两两交换链表,返回首结点
step1:找终止条件
(1)最后啥也不剩 (因为可能输入 []的情况,所以这种情况不能省略)
(2)最后只剩一个元素 直接返回
(3)最后剩两个元素 交换之后返回
step2:返回什么值
swap(s)表示返回两两交换后的链表的首指针
step3:
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
if head==None:
return head
elif head.next==None:
return head
elif head.next.next==None:
tmp=head.next
tmp.next=head
head.next=None
return tmp
else:
b=head.next
head.next=self.swapPairs(b.next)
b.next=head
return b
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
#kernel:返回值的设置非常巧妙
#若不是平衡树,返回-1
#若是平衡树,返回max(left,right)+1
def depth(root):
if root ==None:
return 0
if root:
left=depth(root.left)
if left==-1:
return -1
right=depth(root.right)
if right==-1:
return -1
return (max(left,right)+1) if abs(left-right)<2 else -1
return False if depth(root)==-1 else True '
例三:二叉树的层次遍历 leetcode 102
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
#len(ans)表示当前队列的高度 正常情况在某一层遍历时 len(ans)=level+1
#刚刚到某一层时 要在ans后新添一个[]
def dfs(root,level):
if root==None:
return
if len(ans)==level:
ans.append([])
ans[level].append(root.val)
if root.left:
dfs(root.left,level+1)
if root.right:
dfs(root.right,level+1)
ans=[]
dfs(root,0)
return ans
例四:“leetcode 337 打家劫舍”
递归+记忆化 可以大大缩减父子节点的重复计算
比如用Fibonacci计算 F(3) 其中F(2)会被重复计算
那么我们可以在递归的头部写上哈希表特判,如果在哈希表中,则无需计算
比如这道树形动态规划也可以用递归暴力解 加上记忆化 速度不会太慢
#一开始的思路是一层全部加起来 发现不行 只能用递归写
#首先写个最傻逼的递归 #然后递归加上记忆化
class Solution:
def __init__(self):
self.hashmap={
}
def rob(self, root: TreeNode) -> int:
if root==None:
return 0
#记忆化 加速200倍
if root in self.hashmap:
return self.hashmap[root]
if root:
method1=root.val
if root.left:
method1+=self.rob(root.left.left)+self.rob(root.left.right)
if root.right:
method1+=self.rob(root.right.left)+self.rob(root.right.right)
ans=max(method1,self.rob(root.left)+self.rob(root.right))
self.hashmap[root]=ans
return ans
另外一种解法
返回值是一个含两个元素的list
list[0]表示当前节点不偷,list[1]表示当前节点偷
注:max(list)返回list中最大元素
class Solution:
def rob(self, root: TreeNode) -> int:
return max(self.dp(root))
#[0]表示不偷 [1]表示偷
def dp(self,root):
if root==None:
return [0,0]
left=self.dp(root.left)
right=self.dp(root.right)
return [max(left)+max(right),root.val+left[0]+right[0]]
例五:复原IP地址 leetcode93
坑点:要补上 s[0]!=‘0’ 不然IP地址会出现前置0
class Solution:
def __init__(self):
self.ans=[]
def restoreIpAddresses(self, s: str) -> List[str]:
self.dfs(0,s,"")
return self.ans
#index表示目前加的位数
def dfs(self,count,s,IP):
if count==4:
if s=='':
self.ans.append(IP[1:])
return
n=len(s)
if n>0:
self.dfs(count+1,s[1:],IP+'.'+s[0])
if n>1 and s[0]!='0':
self.dfs(count+1,s[2:],IP+'.'+s[0:2])
if n>2:
if int(s[0:3])<256 and s[0]!='0':
self.dfs(count+1,s[3:],IP+'.'+s[0:3])
特别注意一个细节
我自己在记录全局回溯的答案时很喜欢返回 self.ans
注意要用 self.ans.append(tmp[:])
或者 self.ans.append(tmp.copy())
不能用引用传递 可变对象要拷贝出来
还有一个python list排序经常要用的易混点
L.sort()
原来的L改变 没有返回值
sorted(L)
原来L不变 返回一个排好序的副本
很好理解和记忆 L.sort()调用类里面的方法 所以会改变这个对象 没有返回值
回溯 去重操作
nums.sort()
if nums[i]==nums[i-1]: continue
若是限定了每个元素的使用次数
方法一:加入一个元素后 下一次递归传入参数要减去元素
self.dfs(nums[:i]+nums[i+1:], tmpList)
方法二:
if i>preIndex and candidates[i]==candidates[i-1]: continue