今日内容
● 654.最大二叉树
● 617.合并二叉树
● 700.二叉搜索树中的搜索
● 98.验证二叉搜索树
1. 最大二叉树
关联 leetcode 654.最大二叉树
-
思路
- 构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。
-
题解
- 二叉树构造
func constructMaximumBinaryTree(nums []int) *TreeNode { if len(nums) == 0 { return nil } maxVal := slices.Max(nums) maxIdx :=slices.Index(nums,maxVal) lSons,rSons:=nums[:maxIdx],nums[maxIdx+1:] return &TreeNode{ Val: maxVal, Left: constructMaximumBinaryTree(lSons), Right: constructMaximumBinaryTree(rSons), } }
2. 合并二叉树
关联 leetcode 617.合并二叉树
-
思路:
- 和遍历单棵二叉树一样,只是这次同时遍历两棵二叉树
- 同时操作两棵二叉树的相对同一位置的节点
- 和遍历单棵二叉树一样,只是这次同时遍历两棵二叉树
-
题解
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode { if root1 == nil { return root2 } if root2 == nil { return root1 } // 走到这里的两个节点, 都不是空节点了, 所以也不会产生 取 nil.Val 这种问题 return &TreeNode{ Val: root1.Val + root2.Val, Left: mergeTrees(root1.Left, root2.Left), Right: mergeTrees(root1.Right, root2.Right), } }
3. 二叉搜索树中的搜索
关联 leetcode 700.二叉搜索树中的搜索
- 二叉搜索树是一个有序树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉搜索树
- 二叉搜索树自带顺序
- 不用纠结 前中后序 遍历了
- 题解
-
递归
func searchBST(root *TreeNode, val int) *TreeNode { if root == nil || root.Val == val { return root } //用一个变量接受结果 //此时root不为空,所以不能直接用root接受 //会存在整棵树里,不存在搜索结果的可能,所以 res 默认为 nil var res *TreeNode if root.Val > val { res = searchBST(root.Left, val) } if root.Val < val { res = searchBST(root.Right, val) } return res }
-
迭代
- 二叉搜索树本身有序
- 可以不使用辅助栈或者队列就可以写出迭代
- 不需要回溯
func searchBST(root *TreeNode, val int) *TreeNode { for root != nil { if root.Val > val { root = root.Left } else if root.Val < val { root = root.Right } else { return root } } return nil }
- 二叉搜索树本身有序
-
4. 验证二叉搜索树
关联 leetcode 98.验证二叉搜索树
- 中序遍历下,输出的二叉搜索树节点的数值就是有序序列
- 二叉搜索树特性
- 二叉搜索树中不能有重复元素
- 思路
- 不能单纯的比较左右孩子节点和父节点的关系
- 整个右子树的所有元素都必须大于其父节点元素值,左子树同理
- 不能单纯的比较左右孩子节点和父节点的关系
- 题解
-
中序遍历二叉搜索树得到一个有序数组
- 左 < 中 < 右
-
递归
func isValidBST(root *TreeNode) bool { rets := make([]int, 0) //中序遍历二叉搜索树 var traversal func(node *TreeNode) traversal = func(node *TreeNode) { if node == nil { return } //中序遍历 左中右 traversal(node.Left) rets = append(rets, node.Val) traversal(node.Right) } traversal(root) for i := 1; i < len(rets); i++ { if rets[i] <= rets[i-1] { return false } } return true }
-
9. 题外话
- 注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,这样可以节约时间和空间上的开销。
- 什么时候递归函数前面加if,什么时候不加if,这个问题我在最后也给出了解释。
- 其实就是不同代码风格的实现,一般情况来说:
- 如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。
- 其实就是不同代码风格的实现,一般情况来说: