返回

用独到见解剖析 LeetCode 109:有序链表转化二叉搜索树

见解分享

在 LeetCode 的殿堂里,109 号问题犹如一颗璀璨的明珠,考验着程序员的算法能力和数据结构功底。它要求我们根据一个有序链表,构建一棵平衡二叉搜索树。乍一看,这似乎是一项艰巨的任务,但深入剖析后,你会发现其中蕴含着算法之美。

本篇博文将以独树一帜的视角,为你揭示 LeetCode 109 的精髓。我们将跳脱传统的解题思路,从问题本质出发,探索两种截然不同的方法。在纵横交错的算法迷宫中,我们将寻觅最优路径,拨开迷雾,直击问题的核心。

方法一:遍历链表,构建有序数组

回忆 LeetCode 108,我们曾将有序数组转化为二叉搜索树。因此,我们可以先遍历链表,将每个节点的数据存入一个有序数组中。然后,再运用 LeetCode 108 的方法,将有序数组转化为二叉搜索树。这种方法简单直观,但时间复杂度为 O(n),其中 n 为链表的长度。

def sortedListToBST(head):
  # 遍历链表,将数据存入数组
  nums = []
  while head:
    nums.append(head.val)
    head = head.next

  # 将有序数组转化为二叉搜索树
  return sortedArrayToBST(nums)

def sortedArrayToBST(nums):
  if not nums:
    return None

  # 寻找中点,将数组分为左右两部分
  mid = len(nums) // 2
  root = TreeNode(nums[mid])

  # 递归构建左右子树
  root.left = sortedArrayToBST(nums[:mid])
  root.right = sortedArrayToBST(nums[mid+1:])

  return root

方法二:中序遍历链表,构建平衡二叉搜索树

方法一虽然简单,但时间复杂度较高。有没有一种更优的方法呢?答案是肯定的。我们可以利用有序链表的特性,直接构建一棵平衡二叉搜索树,而无需借助额外的数组。

中序遍历一个二叉搜索树,得到的序列是一个有序序列。因此,我们可以中序遍历链表,将节点一个一个插入到二叉搜索树中。由于每次插入都是保持树的平衡性的,因此这种方法的时间复杂度为 O(n log n),比方法一要优。

def sortedListToBST(head):
  def helper(left, right):
    if left > right:
      return None

    # 找到中点
    mid = (left + right) // 2

    # 递归构建左子树
    left_node = helper(left, mid - 1)

    # 构建当前节点
    curr_node = TreeNode(head.val)
    curr_node.left = left_node

    # 移动指针
    head.next = head.next.next

    # 递归构建右子树
    curr_node.right = helper(mid + 1, right)

    return curr_node

  # 初始化左右指针
  left = 0
  right = 0

  # 计算链表长度
  while head:
    right += 1
    head = head.next

  return helper(left, right - 1)

结语

LeetCode 109 问题看似复杂,但通过巧妙的算法设计,我们可以找到高效的解决方案。方法一简单直观,而方法二则利用了有序链表的特性,实现了更优的时间复杂度。

无论选择哪种方法,重要的是理解算法背后的原理,并能够灵活运用数据结构。LeetCode 的魅力在于,它不仅是一系列编程题目,更是一场算法思维的盛宴。通过不断地挑战和探索,我们才能真正领悟算法之美。