代码随想录day20
目录
654、最大二叉树
给定一个不含重复元素的整数数组,构建一个最大二叉树。一个以题目给出的数组构建的最大二叉树定义如下:
1、二叉树的根是数组中的最大元素。
2、左子树是通过数组中最大值的左边部分构造出来的最大二叉树。
3、右子树是通过数组中最大值的右边部分构造出来的最大二叉树。
通过给定的数组构建出最大二叉树,并输出这个树的根节点。
示例:
思路:
最大二叉树的构建过程如下:
构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。
1、确定递归函数的参数和返回值:
参数就是传入的是存放元素的数组,返回该数组构造的二叉树的头结点,返回类型是指向节点的指针。
func constructMaximumBinaryTree(nums []int) *TreeNode {
}
2、确定终止条件
题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了。
那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。 这表示一个数组大小是1的时候,构造了一个新的节点,并返回。
node := &TreeNode{}
if len(nums) == 1 {
node.Val = nums[0]
return node
}
3、确定单层递归的逻辑:
这里有三步工作
- 先要找到数组中最大的值和对应的下标, 最大的值构造根节点,下标用来下一步分割数组。
2. 找到最大值所在的下标左区间 构造左子树。
3. 找到最大值所在的下标右区间 构造右子树。
这样我们就分析完了,整体代码如下:(详细注释)
func constructMaximumBinaryTree(nums []int) *TreeNode {
// 不需要这个判断,因为题目说了nums长度大于等于1
if len(nums) < 1 {
return nil
}
// 找到数组中最大的值和对应的下标
max_value_index := findMax(nums)
max_value := nums[max_value_index]
root := &TreeNode{Val:max_value,
// 最大值所在的下标左区间 构造左子树
Left:constructMaximumBinaryTree(nums[:max_value_index]),
// 最大值所在的下标右区间 构造右子树
Right:constructMaximumBinaryTree(nums[max_value_index+1:]),
}
return root
}
// 找一个数组的最大值的下标位置
func findMax(nums []int) int {
help_i := 0
for i:=0;i<len(nums);i++{
if nums[i] > nums[help_i] {
help_i = i
}
}
return help_i
}
617. 合并二叉树
给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
示例:
思路:
相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢?其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作
递归解法:
二叉树使用递归,就要想使用前中后哪种遍历方式?本题使用哪种遍历都是可以的!
我们下面以前序遍历为例。
动画如下:
那么我们来按照递归三部曲来解决:
1、确定递归函数的参数和返回值:
首先那么要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。
代码如下:
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
}
2、确定终止条件:
因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了啊(如果t2也为NULL也无所谓,合并之后就是NULL)。
反过来如果t2 == NULL,那么两个数合并就是t1(如果t1也为NULL也无所谓,合并之后就是NULL)。
if root1 == nil {
return root2
}
if root2 == nil {
return root1
}
3、确定单层递归的逻辑:
单层递归的逻辑就比较好些了,这里我们用重复利用一下t1这个树,t1就是合并之后树的根节点(就是修改了原来树的结构)。
那么单层递归中,就要把两棵树的元素加到一起。
root1.Val += root2.Val
接下来t1 的左子树是:合并 t1左子树 t2左子树之后的左子树。t1 的右子树:是 合并 t1右子树 t2右子树之后的右子树。最终t1就是合并之后的根节点。
root1.Left = mergeTrees(root1.Left,root2.Left)
root1.Right = mergeTrees(root1.Right,root2.Right)
return root1
此时前序遍历,完整代码就写出来了,如下:
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
if root1 == nil { // 如果t1为空,合并之后就应该是t2
return root2
}
if root2 == nil { // 如果t2为空,合并之后就应该是t1
return root1
}
// 修改了t1的数值和结构
root1.Val += root2.Val // 中
root1.Left = mergeTrees(root1.Left,root2.Left) // 左
root1.Right = mergeTrees(root1.Right,root2.Right) // 右
return root1
}
700.二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
例如:
在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。
思路:
二叉搜索树是一个有序树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉搜索树
这就决定了,二叉搜索树,递归遍历和迭代遍历和普通二叉树都不一样。
这道题为二叉搜索树的【搜索】,其实也就是二叉搜索树的【查找】操作。
根据二叉搜索树的性质,在二叉搜索树中查找一个节点,其实就 3 步:
1、将查找的节点根节点比较,如果相等,则直接返回。
2、如果查找的节点 < 根节点,则在左子树中递归查找。
3、如果查找的节点 > 根节点,则在右子树中递归查找。
本题,其实就是在二叉搜索树中搜索一个节点。那么我们来看看应该如何遍历
递归法:
1、确定递归函数的参数和返回值:
递归函数的参数传入的就是根节点和要搜索的数值,返回的就是以这个搜索数值所在的节点。
func searchBST(root *TreeNode, val int) *TreeNode {
}
2、确定终止条件:
如果root为空,或者找到这个数值了,就返回root节点。
if root == nil || root.Val == val { return root }
3、确定单层递归的逻辑
看看二叉搜索树的单层递归逻辑有何不同。因为二叉搜索树的节点是有序的,所以可以有方向的去搜索。
如果root.val > val,搜索左子树,如果root.val < val,就搜索右子树,最后如果都没有搜索到,就返回NULL。
代码如下:
if root.Val > val {
return searchBST(root.Left, val) //注意这里加了return
}
if root.Val < val {
return searchBST(root.Right, val)
}
这里可能会疑惑,在递归遍历的时候,什么时候直接return 递归函数的返回值,什么时候不用加这个 return呢。
我们在二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?中讲了,如果要搜索一条边,递归函数就要加返回值,这里也是一样的道理。
因为搜索到目标节点了,就要立即return了,这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。
整体代码如下:
func searchBST(root *TreeNode, val int) *TreeNode{
if root == nil || root.Val == val {
return root
}
if root.Val > val {
return searchBST(root.Left, val)
}
if root.Val < val {
return searchBST(root.Right, val)
}
return nil
}
98、验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
思路:
要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了
递归法:
可以递归中序遍历将二叉搜索树转变成一个数组,代码如下:
递归调用节点就行了 ,主要是判断节点值所处的范围合不合法。
func isValidBST(root *TreeNode) bool {
if root == nil {
return true
}
return isBST(root, math.MinInt64, math.MaxInt64)
}
func isBST(root *TreeNode, min, max int) bool {
if root == nil {
return true
}
if min >= root.Val || max <= root.Val { // 检查节点合法性,根节点的值处于(-∞,+∞)
return false
}
// 检查左右节点的合法性。
// 左节点的值处于(-∞,root.Val)
// 右节点的值处于(root.Val,+∞)
return isBST(root.Left, min, root.Val) && isBST(root.Right, root.Val, max)
}
一天天,一天天,真快啊!