代码随想录day20|654. 最大二叉树|617. 合并二叉树| 700.二叉搜索树中的搜索| 98.验证二叉搜索树|Golang

代码随想录day20

目录

654、最大二叉树

617. 合并二叉树

700.二叉搜索树中的搜索

98、验证二叉搜索树


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、确定单层递归的逻辑:

这里有三步工作

  1. 先要找到数组中最大的值和对应的下标, 最大的值构造根节点,下标用来下一步分割数组。

     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)
}

一天天,一天天,真快啊!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值