核心思想:
重复将问题分解为同类的子问题而解决问题的方法,其核心思想是分治策略。
使用条件:
1.需要解决的问题可以转化为一个或多个子问题来求解,而这些子问题的求解方法与原问题完全相同,只是在数量和规模上不同。
2.递归调用的次数必须是有限的。
3.必须有结束递归的条件来终止递归。
举例(LeetCode 894):
满二叉树是一类二叉树,其中每个结点恰好有 0 或 2 个子结点。
返回包含 N 个结点的所有可能满二叉树的列表。 答案的每个元素都是一个可能树的根结点。
答案中每个树的每个结点都必须有 node.val=0。
你可以按任何顺序返回树的最终列表。
思路:
构造二叉树的思路是对于二叉树的每一层来说,我们都是先初始化一个根节点,
然后分别定义它的左子树和右子树,这个过程很明显可以递归实现。
本题要求构造满二叉树,也就是每一个结点它的左节点和右节点必须同时存在
或同时不存在,假设左子树有X个节点,总节点数为N,右子树则包含N-1-X个
节点,X数目从1开始,增加到N - 1 - 1,共计(N - 1 ) / 2中情况
对于每一种子情况我们采取递归求解:
结束条件:
当N为偶数时,无法构造二叉树,返回空数组。
当N为1时,树只有一个节点,为了不破坏递归,同样返回一个数组,包含这个节
点
左右子树构造采用递归的方法。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def allPossibleFBT(self, N: int) -> List[TreeNode]:
if N == 1: #终止条件
return [TreeNode(0)]
if N % 2 == 0:
return []
forest = []
#初始化子问题1
left = 1
right = N - 1 - 1
while right > 0:
left_tree = self.allPossibleFBT(left)
right_tree = self.allPossibleFBT(right)
for i in left_tree:
for j in right_tree:
root = TreeNode(0)
root.left = i
root.right = j
forest.append(root)
left += 2
right -= 2
return forest
举例二:(leetcode698)
给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。
示例 1:
输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
输出: True
说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和
思路:
终止条件:
1.当nums % k != 0时,直接返回错误,不能构成k个非空子集
2.当nums为空,即nums中的数字全部填充进入子集,那么我们的
搜索就成功了。
递归过程:
每次向子集填充数字遵循数字大者优先,这样可以较快组合数量
较小的子集。对于 nums 中的每个数字,我们可以将其添加到
一个group,只要该子集的和不会超过目标值,否则添加下一个。
对于每一个选择,我们都递归重复上述过程。
class Solution:
def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
nums.sort() #排序,实现较小集合优先找到
div, mod = divmod(sum(nums), k)
#不合题意,直接排除
if mod: return False
#判断是否有超出平均值的数字
if nums[-1] > target: return False
#简化计算复杂度,直接将大小等于div的数字排除
while nums and nums[-1] == div:
nums.pop()
k -= 1
def reverse(groups, tar):
if not nums: return True #成功放置所有数字
count = nums.pop() #获取当前nums最大的数字
for i, group in enumerate(groups):
if group + count <= tar: #当前子集小于平均值,则将数字加入该子集
groups[i] += count
if reverse(groups, tar): return True #找到最终结果
groups[i] -= count #当前子集不合适,选用新的子集
if not group: break #优化,0的时候只填充一次,剩下的跳过
nums.append(count) #回溯
return False
return reverse([0] * k, div)
来源:CSDN
作者:qq_41174940
链接:https://blog.csdn.net/qq_41174940/article/details/104112793