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

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

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

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵
平衡二叉搜索树。

平衡二叉树: 是指该树所有节点的左右子树的深度相差不超过 1

示例 1:
在这里插入图片描述

输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:

在这里插入图片描述

示例 2:
在这里插入图片描述

输入:nums = [1,3]
输出:[3,1]
解释:[1,null,3][3,1] 都是高度平衡二叉搜索树。

提示:

  • 1 <= nums.length <= 10^4
    -10^4 <= nums[i] <= 10^4
  • nums 按 严格递增 顺序排列

思路

做这道题目之前大家可以了解一下这几道(知道如何划分左右子区间去,并用上层的LeftRight接住上来的节点):

106. 从中序与后序遍历序列构造二叉树

654.最大二叉树中其实已经讲过了,如果根据数组构造一棵二叉树。

701.二叉搜索树中的插入操作

450.删除二叉搜索树中的节点

进入正题:

题目中说要转换为一棵高度平衡二叉搜索树。为什么强调要平衡呢?

因为只要给我们一个有序数组,如果不强调平衡,都可以以线性结构来构造二叉搜索树。

例如 有序数组[-10,-3,0] 就可以构造成这样的二叉搜索树,如图。

在这里插入图片描述

上图中,是符合二叉搜索树的特性吧,如果要这么做的话,是不是本题意义就不大了,所以才强调是平衡二叉搜索树。

其实数组构造二叉树,构成平衡树是自然而然的事情,因为大家默认都是从数组中间位置取值作为节点元素,一般不会随机取。所以想构成不平衡的二叉树是自找麻烦。

654.最大二叉树中其实已经讲过了,如果根据数组构造一棵二叉树。

本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间。

本题其实要比654.最大二叉树简单一些,因为有序数组构造二叉搜索树,寻找分割点就比较容易了。

分割点就是数组中间位置的节点。

那么为问题来了,如果数组长度为偶数,中间节点有两个,取哪一个?

取哪一个都可以,只不过构成了不同的平衡二叉搜索树。

例如:输入:[-10,-3,0,5,9]

如下两棵树,都是这个数组的平衡二叉搜索树:
在这里插入图片描述

如果要分割的数组长度为偶数的时候,中间元素为两个,是取左边元素 就是树1,取右边元素就是树2

这也是题目中强调答案不是唯一的原因。 理解这一点,这道题目算是理解到位了。

递归

递归三部曲:

1.确定递归函数返回值及其参数
删除二叉树节点,增加二叉树节点,都是用递归函数的返回值来完成,这样是比较方便的。

相信大家如果仔细看下面两篇文章后

701.二叉搜索树中的插入操作
450.删除二叉搜索树中的节点

一定会对递归函数返回值的作用深有感触。

那么本题要构造二叉树,依然用递归函数的返回值来构造中节点的左右孩子。

再来看参数,首先是传入数组,然后就是左下标left和右下标right,我们在106. 从中序与后序遍历序列构造二叉树中提过,在构造二叉树的时候尽量不要重新定义左右区间数组,而是用下标来操作原数组。

所以代码如下:

// 左闭右闭区间[left, right]
func dfs(nums []int,left,right int)*TreeNode{}

这里注意,我这里定义的是左闭右闭区间,在不断分割的过程中,也会坚持左闭右闭的区间,这又涉及到我们讲过的循环不变量(即开始定好了左闭右闭,则在递归的过程中要全程坚持这个区间划分原则,否则区间会划乱,导致计算结果不对)。

2.确定递归终止条件
这里定义的是左闭右闭的区间,所以当区间 left > right时,说明区间没有元素了,就可以返回空节点了。

代码如下:

// 当前区间已经没有元素,可以递归结束返回nil,让上层递归的Left或者Right接住它
    if left > right {
        return nil
    }

3.确定单层递归的逻辑
首先取数组中间元素的位置,不难写出mid := (left + right) / 2;,这么写其实有一个问题,就是数值越界,例如leftright都是最大int,这么操作就越界了,在二分法 中尤其需要注意!

所以可以这么写:mid := left + ((right - left) / 2

取了中间位置,就开始以中间位置的元素构造节点,代码: root := &TreeNode{Val:nums[mid]}

接着划分区间,root的左孩子接住下一层左区间的构造节点,右孩子接住下一层右区间构造的节点。最后返回root节点

单层递归整体代码如下:

	mid := left + (right - left) / 2
    root := &TreeNode{Val:nums[mid]}
    root.Left  = dfs(nums,left,mid - 1)
    root.Right = dfs(nums,mid+1,right)
    return root

这里mid := left + ((right - left) / 2)的写法相当于是如果数组长度为偶数,中间位置有两个元素,取靠左边的。

递归整体Go代码如下:

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func sortedArrayToBST(nums []int) *TreeNode {
    // 由于是要构造平衡的二叉搜索树,所以两边的高度最多相差1,因此我们应该选数组的中间位置元素作为根节点
    // 这样左右子树的元素才接近,且满足根节点大于左子树所有节点,小于右子树所有节点
    if len(nums) == 0 {
        return nil
    }
    // 左闭右闭
    root := dfs(nums,0,len(nums) - 1)
    return root

}

func dfs(nums []int,left,right int)*TreeNode{
    // 当前区间已经没有元素,可以递归结束返回nil,让上层递归的Left或者Right接住它
    if left > right {
        return nil
    }
    mid := left + (right - left) / 2
    root := &TreeNode{Val:nums[mid]}
    root.Left  = dfs(nums,left,mid - 1)
    root.Right = dfs(nums,mid+1,right)
    return root
}

注意:在调用dfs的时候传入的leftright为什么是0len(nums) - 1,因为定义的区间为左闭右闭
在这里插入图片描述

总结

在二叉树专题中,已经写了多道构造二叉树的题目了,其实思路也是一样的,不断中间分割,然后递归处理左区间,右区间,也可以说是分治。

此时相信大家应该对通过递归函数的返回值来增删二叉树很熟悉了,这也是常规操作。

在定义区间的过程中我们又一次强调了循环不变量的重要性。

以下是使用 Kotlin 实现将有序数组转换二叉搜索树的代码: ```kotlin class TreeNode(var `val`: Int) { var left: TreeNode? = null var right: TreeNode? = null } fun sortedArrayToBST(nums: IntArray): TreeNode? { return buildBST(nums, 0, nums.size - 1) } fun buildBST(nums: IntArray, left: Int, right: Int): TreeNode? { if (left > right) { return null } val mid = (left + right) / 2 val root = TreeNode(nums[mid]) root.left = buildBST(nums, left, mid - 1) root.right = buildBST(nums, mid + 1, right) return root } ``` 这段代码中,我们首先定义了一个 `TreeNode` 类,用于表示二叉树的节点。然后,我们定义了一个 `sortedArrayToBST` 函数,该函数接受一个有序整数数组 `nums`,并返回一个高度平衡的二叉搜索树。 在 `sortedArrayToBST` 函数中,我们调用了 `buildBST` 函数,该函数用于递归构建二叉搜索树。`buildBST` 函数接受三个参数:有序整数数组 `nums`、左边界 `left` 和右边界 `right`。我们首先判断左边界是否大于右边界,若是,则返回 `null`。否则,我们选取中间位置 `mid`,将 `nums[mid]` 作为当前节点的值,然后递归构建当前节点的左子树和右子树,并将它们分别赋值给当前节点的 `left` 和 `right` 属性。最后,返回当前节点。 这样,我们就完成了将有序数组转换二叉搜索树的实现。在上述示例中,我们可以调用 `sortedArrayToBST(intArrayOf(-10, -3, 0, 5, 9))`,得到一个高度平衡的二叉搜索树 `[0, -3, 9, -10, null, 5]`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值