【LeetCode】95. 不同的二叉搜索树 II

91 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

题目

给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。

示例 1

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

示例 2

输入:n = 1
输出:[[1]]

提示:

  • 1 <= n <= 8

题解

思路

先定义一个子过程F,这个过程会递归调用。既然是递归调用就要考虑下层怎么调边界是什么主函数怎么调

下层怎么调用:

  • 既然是二叉搜索树,那就有左小右大的特点,那么左右两个数是否就能作为下层调用的参数呢
  • 为了方便,用数组[3,4,5,6,7]来举例。
  • 遍历数组
  • 以3为头节点的时候,[4,5,6,7]为右子树结点集合生成,此时会调用F(4,7),左子树为空,会调用F(3,2),此时参数不合法返回空集合
  • 以4为头节点的时候,[3,]为左树集合的生成,会调用F(3,3),右树[5,6,7],会调用F(5,7)
  • 以5为头节点的时候,[3,4]为左树集合的生成,会调用F(3,4),右树[6,7],会调用F(6,7)
  • 以6为头节点的时候,[3,4,5]为左树集合的生成,会调用F(3,5),右树[7],会调用F(7,7)
  • 以7为头节点的时候,[3,4,5,6]为左子树结点集合生成,此时会调用F(3,6),左子树为空,会调用F(8,7),此时参数不合法返回空集合
  • 为什么不用1开头的数组来举例,是希望你能在过程中理解递归,[3,4,5,6,7]其实是长度为7的数组以2为头节点的时候的一个右子树过程

边界是什么:

  • 通过上面的分析,传进来的参数如果左大于右(l>r)那就直接定义为不合法
  • 如果传进来的参数有l==r,那么直接返回由一个值形成的集合

主函数怎么调用

  • f(1,n)
  • 从1到n作为根结点得到的树集合递归算好往上发给我

代码

func generateTrees(n int) []*TreeNode {
	return getRestNumRoot(1, n)
}

// 参数说明:l,r表示在[l,r]范围上,每个数字作为根节点时能得到的树 对应的根节点集合
// 以下举例都用[3,4,5,6,7]来举例,不用从1开始是为了让你更直观理解递归过程
func getRestNumRoot(l, r int) []*TreeNode {
	if l > r {
		// l都跑到r旁边了自然返回的是空集合 这种情况有两种可能
		//	①遍历到3结点作为根,此时左子树为空,因为这是递归调用是getRestNumRoot(3,2)
		//  ②遍历到7结点作为根,此时右子树为空,递归调用是getRestNumRoot(8,7)
		return []*TreeNode{}
	}
	if l == r {
		// 这种情况也好理解,不需要挨个去创建根节点,直接将只包含一个值的数组返回即可
		// 举例:以4为根节点 左子树调用getRestNumRoot(3,3)
		return []*TreeNode{
			&TreeNode{Val: l},
		}
	}
	ret := []*TreeNode{}
	// 循环尝试将[l,r]范围上每个数字都作为头节点
	for m := l; m <= r; m++ {

		leftTreeSet := getRestNumRoot(l, m-1)	// 左边则是[l..m-1] 自己递归算好传上来给我	
		rightTreeSet := getRestNumRoot(m+1, r)	// 右边则是[m+1..r] 自己递归算好传上来给我

		if len(leftTreeSet) == 0 {
			// 左树为空的时候说明右数一定有内容 挨个拼就行了
			for i := 0; i < len(rightTreeSet); i++ {
				root := &TreeNode{
					Val:   m,
					Left:  nil,
					Right: nil,
				}
				root.Right = rightTreeSet[i]
				ret = append(ret, root)
			}
		} else if len(rightTreeSet) == 0 {
			// 同理 
			for i := 0; i < len(leftTreeSet); i++ {
				root := &TreeNode{
					Val:   m,
					Left:  nil,
					Right: nil,
				}
				root.Left = leftTreeSet[i]
				ret = append(ret, root)
			}
		} else {
			// 左右根节点集合都有内容 左边集合拿一个 右边集合拿一个 拼一起就好了
			for i := 0; i < len(leftTreeSet); i++ {
				for j := 0; j < len(rightTreeSet); j++ {
					root := &TreeNode{
						Val:   m,
						Left:  nil,
						Right: nil,
					}
					root.Left = leftTreeSet[i]
					root.Right = rightTreeSet[j]
					ret = append(ret, root)
				}
			}
		}
	}
	return ret
}

结语

业精于勤,荒于嬉;行成于思,毁于随。