【每日力扣Leetcode】109-将有序链表转换为二叉搜索树

力扣题库109,将有序链表转换为二叉搜索树。

题目链接点这里

题目描述

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

解题思路

这一题和108题非常相似,不同的是108中的数据类型为数组,而109这里变成了链表。

108题可以参考《【每日力扣Leetcode】108-将有序数组转换为二叉搜索树》

思路也类似,通过寻找中点来进行赋值,同时利用递归完成目标。但是这里遇到的一个问题就是链表的中点不像列表那么容易寻找

通过快慢指针的方式可以找到链表的中点。一开始有两个指针slow和fast分别指向链表的头,之后slow每移动一位,fast移动两位,一直到fast不能再移动为止,slow指向的就是链表的中点。

找到中点之后,还需要将中点前一位的next指向None,才能再带入递归,以避免无限循环下去。

同样需要注意的是递归的退出条件。

初始答案

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def sortedListToBST(self, head: ListNode) -> TreeNode:
        if not head:
            return None
        if head.next is None:
            return TreeNode(val=head.val)
        if head.next.next is None:
            node = TreeNode(head.next.val)
            node.left = TreeNode(val=head.val)
            return node
        slow = head 
        fast = head 
        pre = head
        while fast.next and fast.next.next:
            pre=slow
            slow=slow.next 
            fast=fast.next.next
        mid = slow 
        pre.next = None
        node = TreeNode(val=mid.val)
        node.left = self.sortedListToBST(head)
        node.right = self.sortedListToBST(slow.next) 
        return node

这里的快慢指针代码如下

slow = head 
fast = head 
pre = head
while fast.next and fast.next.next:
    pre=slow
    slow=slow.next 
    fast=fast.next.next
mid = slow 
pre.next = None

而递归的退出条件有三个

if not head:
    return None
if head.next is None:
    return TreeNode(val=head.val)
if head.next.next is None:
    node = TreeNode(head.next.val)
    node.left = TreeNode(val=head.val)
    return node

当传入的是空链表时,返回None。当传入的链表只有1位或者2位时,就不需要再进行快慢指针的操作了,直接赋值返回就行了。只有当链表的位数打过2位时才进行底下的一系列操作。当然,后两种退出条件也可以在后面通过slow==fast来处理。

因为每一轮递归都对所有元素遍历来寻找中点,一共寻找了logN轮,所以时间复杂度为O(NlogN)。提交以后执行用时: 152 ms,击败了46%的提交

改进答案

时间复杂度为O(NlogN)显然并不是太理想,看到解题区的大神又给出了下面这种思路。

既然是按照升序排列的链表,那其实就和BST按照中序遍历的返回结果是一样的。那么同样是进行递归,不过这次递归的时候是按照中序遍历的方式来进行,每一次都是先处理左子树,然后是根节点,然后是右子树。

注意,下面是关键。递归第一次退出之后,开始对最左边的叶子节点的根节点赋值,此时赋值链表的第一个值。然后在递归栈不停回退的时候,链表依次往下对每次的根节点进行赋值。

这么说可能不够直观,直接看代码。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def sortedListToBST(self, head: ListNode) -> TreeNode:
        def getLength(head:ListNode) -> int:
            len = 0
            while head:
                len+=1
                head=head.next 
            return len 

        def buildBST(start:int,end:int) -> TreeNode:
            if start > end:
                return None
            mid = (start + end+1)//2
            node = TreeNode()
            node.left = buildBST(start,mid-1)
            nonlocal head 
            node.val = head.val 
            head=head.next
            node.right = buildBST(mid+1,end)
            return node
        
        length = getLength(head)
        return buildBST(1,length)

首先是两个辅助函数,一个是计算链表的长度,另一个是递归的主体。

重点看递归的主体部分,每次都是先执行node.left = buildBST(start,mid-1),也就是说还没等到真正的赋值,就已经递归到下一层了。一直到退出递归,开始第一次执行node.val = head.val的时候是在最左侧的叶节点。再执行node.right = buildBST(mid+1,end)对最左侧叶节点的右子节点进行同样的操作。返回以后是倒数第二层的根节点,之后是倒数第二层的右子节点,一直到真正的根节点。每次需要赋值的时候就获取链表的内容,同时往前移动一位。注意因为需要操作外层变量,所以要用nonlocal head进行外部变量声明。这是python3中引入的操作。

因为只是计算了一次总长度,所以时间复杂度为O(N)。不过提交以后执行时间还是152ms,没有什么提升。

注意事项

不管是108还是109,答案都并不唯一,例如在改进答案中用mid = (start + end+1)//2或者mid=(start+end)//2都是正确的,重点关注的是解题思路。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值