一、【LeetCode 108】有序数组转成二叉搜索树
1.题目描述
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
2.解题思路
(1)由二叉树的相关知识可知:
- 二叉树的三种深度优先遍历中的任何一种(前序/中序/后序)都无法唯一确定一个二叉树;
- 只有(中序+前序)或(中序+后序)才能唯一确定一个二叉树。
(2)所以本题只给了一个有序数组,相当于二叉搜索树的中序遍历,没法确定一个唯一的结果,虽然添加了附加条件(要求左右子树的高度差不超过1),仍有多种情况。
(3)故根据此附加条件,最简便的方法就是取数组的中间值为根节点,将数组分成近乎相等的两个部分(若数组的个数是奇数个,则左右部分刚好相等;若数组个数是偶数个,左子树和右子树相差为1),然后递归用同样的方法去重建左子树和右子树,最后返回根节点即可。
3.代码
# 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:
if not nums:
return None
mid = len(nums) // 2
root = TreeNode(nums[mid])
root.left = self.sortedArrayToBST(nums[:mid])
root.right = self.sortedArrayToBST(nums[mid+1:])
return root
二、【LeetCode 109】有序链表转换为二叉搜索树
1.题目描述
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
2.解题思路
有序链表找中间节点使用快慢指针,fast指针比slow指针多走一步,fast指针走到尾时,slow指针刚好指向近似中间节点。找到中间节点就可以利用类似于【有序数组转换二叉搜索树】的方法来进行递归了。
3.代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def sortedListToBST(self, head: ListNode) -> TreeNode:
return self.helper(head, None)
def findmid(self, head, tail):
slow = head
fast = head
while fast != tail and fast.next != tail:
slow = slow.next
fast = fast.next.next
return slow
def helper(self, head, tail):
if head == tail:
return
node = self.findmid(head, tail)
root = TreeNode(node.val)
root.left = self.helper(head, node)
root.right = self.helper(node.next, tail)
return root
三、补充总结
1.有序数组找中点
数组找中点很简便,计算出数组的长度,直接定位即可:mid = len(nums) // 2
2.有序链表找中点
由于链表不能直接定位到中间节点,获取整个链表的长度需要从头到尾遍历一遍。
- 有一种方法是遍历整个链表,将链表每个节点的值保存到数组中,再通过数组的直接定位获取中间节点的值。————这种方法虽然可行,但是不太符合链表的形式。若需要改变节点,而不是节点的值,整个方法就不管用了!
- 最经典的还是快慢指针,最初 slow 和 fast 同在起始位置,slow 指针每次走一步,fast 指针每次走两步,当 fast 走到末尾时,slow 恰好指向中间节点!
while fast != None and fast.next != None:
while循环里表示了两种情况:
(1)当链表节点数为偶数时,fast 指向空时,slow 恰好指向中间(这种情况的循环条件就是 fast != None)
(2)当链表节点数为奇数时,fast 刚好走到末尾,slow 恰好指向中间节点(这种情况的循环条件就是 fast.next != None)