【LeetCode算法修炼+动画演示】【二叉树的序列化】—— 96.不同的二叉搜索树、95.不同的二叉搜索树II

1. 96.不同的二叉搜索树

原题链接
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
示例:

输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

1.1. 要点

  1. 掌握二叉搜索树
  2. 假定每一个数字为根结点,并将剩余的数组分配到左右两边继续构造子树。
    此时每一个根结点构成二叉树的数量总和为左边*右边

1.2. 递归

万变不离其宗

1.2.1. 代码片段
//96.不同的二叉搜索树
public int numTrees(int n) {
	if (n == 0) {//1.终止条件,如果传入0或1直接可以返回结果了。
		return n;
	}
	return numsTreesH(1, n);//2.计算[1...n]这个区间数字一共能够组成多少情况
}
public int numsTreesH(int start, int end) {
	if (start >= end) {//3.请注意区间[1...n],所以[1...1]是可以参与运算,而[1...2]也就是越界了直接返回一种可能。又因为 [1...1]本身也只存在一种可能 所以这里 start>end 与 start>=end 是一样的 并且能够提前终止一次运算。
		return 1;
	}
	int sum = 0;//4.初始化计算的总和
	for (int i = start; i <= end; i++) {//6.分割 start 到 end 这个区间的数组:①[start...i1-1]、[i1+1...end],②[start...i2-1]、[i2+1...end]
		int lNums = numsTreesH(start, i - 1);//7.计算左边的和
		int rNums = numsTreesH(i + 1, end);//8.计算左边的和
		sum += lNums * rNums;//将每一组 ①[start...i1-1]、[i1+1...end],②[start...i2-1]、[i2+1...end] 的结果相加得到最后的结果。
	}
	return sum;//5.返回结果
}

加入 HasMap 来直接将重复计算的结果返回

public int numTrees(int n) {
		if (n == 0) {
			return 0;
		}
		return numsTreesH(1, n, new HashMap<Integer, Integer>());
	}

	public int numsTreesH(int start, int end, HashMap<Integer, Integer> hashMap) {
		if (start > end) {
			return 1;
		}
		int length = end - start + 1;//1.[1..3]、[2..4]这种等距离的区间,计算的可能性是一样的,所以这里用它们之间的距离表示运算和。
		Integer integer = hashMap.get(length);
		if (integer != null) {//2.获取到结果直接返回
			return integer;
		}
		int sum = 0;
		for (int i = start; i <= end; i++) {
			int lNums = numsTreesH(start, i - 1, hashMap);
			int rNums = numsTreesH(i + 1, end, hashMap);
			sum += lNums * rNums;

		}
		hashMap.put(length, sum);//3.更新这种区间距离的计算结果。
		return sum;
	}
1.2.2. 代码解释
  1. 右边有n中可能,此时可能性是 1 ∗ n 1*n 1n,如果左子树有m种就是 m ∗ n m*n mn 这个乘法的过程称为组合(即笛卡尔积)

1.3. 动态规划

组合
举例而言, F ( 3 , 7 ) F(3, 7) F(3,7),以 3为根的不同二叉搜索树个数。为了以 3为根从序列 [1, 2, 3, 4, 5, 6, 7] 构建二叉搜索树,我们需要从左子序列 [1, 2] 构建左子树,从右子序列 [4, 5, 6, 7] 构建右子树,然后将它们组合(即笛卡尔积)。
巧妙之处在于,我们可以将 [1,2] 构建不同左子树的数量表示为 G ( 2 ) G(2) G(2) , 从 [4, 5, 6, 7] ` 构建不同右子树的数量表示为 G ( 4 ) G(4) G(4)。这是由于 G ( n ) G(n) G(n) 和序列的内容无关,只和序列的长度有关。于是, F ( 3 , 7 ) = G ( 2 ) ⋅ G ( 4 ) F(3,7)=G(2)⋅G(4) F(3,7)=G(2)G(4)。 概括而言,我们可以得到以下公式:

假设现在的根节点是3
F ( 3 , 7 ) = G ( 2 ) ⋅ G ( 4 ) ( 1 ) F(3,7) =G(2)⋅G(4) \qquad(1) F(3,7)=G(2)G(4)(1)
F ( i , n ) = G ( i − 1 ) ⋅ G ( n − i ) ( 2 ) F(i,n)=G(i−1)⋅G(n−i) \qquad (2) F(i,n)=G(i1)G(ni)(2)
G ( n ) = ∑ n = 1 n G ( i − 1 ) ⋅ G ( n − i ) ( 3 ) G(n)= \sum^n_{n=1}G(i−1)⋅G(n−i)\qquad (3) G(n)=n=1nG(i1)G(ni)(3)

1.3.2. 代码片段
public int numTrees2(int n) {
	int[] G = new int[n + 1];//1.创建一个n+1容量的数组,额外存放一个0元素。
	G[0] = 1;//2.  0,1都默认返回一种情况。
	G[1] = 1;
	for (int j = 2; j <= n; j++) {//3.计算 2之后的结果
		for (int i = 1; i <= j; i++) {//4.计算公式
			G[j] += G[i - 1] * G[j - i];
		}
	}
	return G[n];
}

1.4. 数学推导“卡塔兰数”

移除了这部分内容,算法公式跟一般解法的思路不一样,对其它题目并不具有一般性暂不考虑。


95.不同的二叉搜索树II

原题链接
给定一个整数 n,生成所有由 1 … n 为节点所组成的二叉搜索树。
示例:

输入: 3
输出:
[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]

解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:

1         3     3      2      1
 \       /     /      / \      \
  3     2     1      1   3      2
 /     /       \                 \
2     1         2                 3

要点

掌握二叉搜索树的构建过程,先切分数组,后创建根结点。

将有序数组转换为二叉搜索树

递归

利用递归过程中保存变量的性质,当整个调用过程结束的时候恰好是构造完树。

代码片段
func generateTreesHelper(start, end int) []*TreeNode {
	all_trees := []*TreeNode{}
	if start > end {
		all_trees = append(all_trees, nil)
		return all_trees
	}
	for i := start; i <= end; i++ {
		left_trees := generateTreesHelper(start, i-1)
		right_trees := generateTreesHelper(i+1, end)
		for _, v_l := range left_trees {
			for _, v_r := range right_trees {
				node := &TreeNode{Val: i}
				node.Left = v_l
				node.Right = v_r
				all_trees = append(all_trees, node)
			}
		}
	}
	return all_trees
}
func generateTrees(n int) []*TreeNode {
	if n == 0 {
		return nil
	}
	return generateTreesHelper(1, n)
}

😁😁😁制作动画过程不易,请大家顺手点赞收藏咯 谢谢~😁😁😁
有其它题目不理解的也可以一起学习,如有错误欢迎指出~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值