【小白用python刷Leetcode】108. 将有序数组转换为二叉搜索树

718. 最长重复子数组

题目描述

(好吧我还是截图题目描述了,主要还有个数据结构的图,打出来的话我不得疯。别说截图之后感觉也不那么丑。。。自我安慰ing)

二叉搜索树

拿到题目,首先需要清楚的一点是:什么是二叉搜索树???(刷题还能再学学数据结构,也挺好是吧。)

二叉搜索树(二叉查找树、二叉排序树):这三种数据结构都是一个东西,简单来说就是每个节点的左子节点的值小于当前节点的值,而右子节点的值大于当前节点的值

按照定义,如果给定一个数组,生成其二叉搜索树,那么这棵树并不唯一。不妨考虑猎奇一点,即便一棵树只有右子节点,那么只要按升序排列一路填充进去就可以了。所以题目里要求高度平衡,当然,即便这样要求了,生成的树其实也并不唯一。

根据定义,二叉搜索树有个很重要的性质:二叉搜索树的中序遍历的数组是单调递增的。根据这个性质,我们可以判断一棵树是不是二叉搜索树

关于二叉搜索树的一些操作,比如查询啊插入啊什么的,在本文末尾给出。

官方思路

那么今天为啥一上来就是官方思路呢?因为我没想出来啊,一道简单题,没有想出来啊。。。而且看提交记录,我居然刷过这道题,表示一点印象都没有。唉,没有一点长进,小白实锤无疑了。题目给出了一个升序排列的数组,根据二叉搜索树的性质,这道题目可以理解成给出了一棵树的中序遍历,要求还原这棵树。当然还有一个限定条件,就是要高度平衡。也就是说要求每个根节点(整棵树和各个子树的根结点)上的值,都是整个数组和相应子数组的中间位置的数。那么要实现这些要求,而且还是树数据结构,很容易就可以想到用递归的方式构造树。在这里我必须要澄清一下,其实我也不是完全没有想出来(想找补点),上面这些思路我也都意识到了,也想到了要用递归,但是奈何这个递归我构造不出来啊!构造的递归函数总是有这样那样的问题,唉,感觉还不如不澄清呢,感觉自己更菜了。。。

我觉得递归函数构造比较难的点在确定截止条件传入变量。其实也不是说这两个比较难确定,就是即便逻辑上理顺了,但是一落到代码上就很难如愿实现。我就是这种情况,感觉自己想的挺好,也知道哪里用递归,怎么停,边界是啥都知道,就是写不顺,唉,心累,还是写的少。具体到这道题上,递归函数的作用就是找到传入数组中间位置上的数,作为节点加入到树中,并且在左子数组和右子数组中分别再找中间位置的数,作为左子节点和右子节点,依次递归。递归的传入变量(也就是我们在递归时候不断变化的边界)很明显就是每个子数组。截止条件也很明显,就是当没有子数组时停止递归。这些清楚以后,就来构造递归函数吧,构造这部分我真是没法具体说,因为我自己很菜,看了答案才搞对。

题解代码:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        def helper(nums):
            if not nums:
                return 
            
            mid = len(nums) //2
            root = TreeNode(nums[mid])
            root.left = helper(nums[:mid])
            root.right = helper(nums[mid+1:])  # 这里需要注意,因为nums[mid]已经被加入到树中了,要跳过
            return root
        return helper(nums)

就这个不到十行的递归,我居然没有写出来。。。练吧少年orz

运行结果:

二叉搜索树的操作

既然遇到了二叉搜索树,就简单了解下它的各种操作该怎么做。注意:没有代码示例!就只是简单说说。

查找

在二叉搜索树中,查找是很简单的一种操作,毕竟就是以这个目的来构造的。具体来说很像二分,先把数和根节点比,如果比根节点大,说明在右子树中;如果比根节点小,说明在左子树中;如果相等,这不就找到了,返回当前节点。

找最值

算是查找的一个特殊情况,以找最小值为例:一直找当前节点的左子节点,直到没有左子节点,则当前节点的值即为最小值。最大值同理,一直找右子节点即可。

插入

由于给定一个数组,其二叉搜索树是不确定的,所以在二叉搜索树中插入一个节点的方式也不唯一。比如根节点是5,左子节点是3,右子节点是7的子树,插入值为4的节点,可以用4代替3的位置,并将3作为4的左子节点。不过这种方式就比较麻烦,另一种比较简便就是把4作为7的左子节点加入,或者把4作为3的右子节点加入。因为是直接添加最底层的叶子节点,所以只需要添加节点而不需要改动原先的树结构。所以在插入操作的时候,我们一般选用这种方式。

具体可以将当前节点的值node与待插入值target,比较node与target,如果target > node,说明target应该插入当前节点的右子树中;反之,如果target < node,target插入左子树中;如果相等就不插了。再在左子树或者右子树中比较下去,直到找到某个节点,其值node < target且当前节点没有右子节点,则将target加入当前节点的右子节点位置;反之,当前节点的值node > target且当前节点没有左子节点,则将target加入当前节点的左子节点位置。

删除

删除操作就比较麻烦了,删除之前要先找到待删除节点的位置,具体情况有三种:1. 待删除的节点是叶子节点,其下没有左子节点和右子节点,直接删除,将父节点的指针返回空即可;2. 待删除的节点只有左子节点或右子节点,以待删除节点只有右子节点为例。从当前节点的右子节点开始,一直找左子节点,即在右子树中找最小值,然后将最小值赋值给当前节点,再删除最小值节点。需要注意的是最小值节点不一定没有右子节点(右子树),如果有,还需将右子树接在最小值节点的左子节点上。此外,还有可能待删除节点的右子节点就是最小值,这种情况说明右子节点是单向的,只有右子树,此时直接把待删除节点绕过去,指针直接指向右子节点即可。待删除节点只有左子节点是一样的;3. 待删除的节点一应俱全左右子节点都有,这样既可以从左子树中找到最大值节点来取代当前节点,也可以从右子树中找到最小值节点来取代当前节点,都可以,具体就转化成情况2处理。

 

以上就是我,一个正儿八经的小白(大神们通过看代码应该也感觉出来了),对这道题的理解,欢迎诸位指正讨论,感谢阅读。

原题链接:

https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值