一、题目描述
给定一个单链表的头节点 head
,其中的元素 按升序排序 ,将其转换为平衡二叉搜索树。
示例 1:
输入: head = [-10,-3,0,5,9] 输出: [0,-3,9,-10,null,5] 解释: 一个可能的答案是[0,-3,9,-10,null,5],它表示所示的高度平衡的二叉搜索树。
示例 2:
输入: head = [] 输出: []
提示:
head
中的节点数在[0, 2 * 10^4]
范围内-10^5 <= Node.val <= 10^5
二、解题思路
为了将一个升序链表转换为平衡的二叉搜索树(BST),我们可以利用链表的特性以及BST的性质。由于链表是升序的,我们可以通过快慢指针的方法找到链表的中间节点,这个中间节点就可以作为BST的根节点。然后,我们可以递归地将链表的前半部分转换为根节点的左子树,将链表的后半部分转换为根节点的右子树。
具体步骤如下:
-
使用快慢指针找到链表的中间节点,慢指针所在的位置即为中间位置,快指针用于确定慢指针的位置。
-
将慢指针指向的节点作为当前子树的根节点。
-
将链表从中间节点断开,分成左右两部分。
-
递归地将左半部分链表转换为根节点的左子树,将右半部分链表转换为根节点的右子树。
-
返回根节点,完成构建。
三、具体代码
class Solution {
public TreeNode sortedListToBST(ListNode head) {
if (head == null) {
return null;
}
// Step 1: Find the middle of the list
ListNode mid = findMiddle(head);
// Step 2: The middle node will be the root of the BST
TreeNode root = new TreeNode(mid.val);
// Base case when there is only one element in the list
if (head == mid) {
return root;
}
// Step 3: Recursively build the left and right subtrees
root.left = sortedListToBST(head);
root.right = sortedListToBST(mid.next);
return root;
}
// Helper function to find the middle of the list
private ListNode findMiddle(ListNode head) {
ListNode prevPtr = null;
ListNode slowPtr = head;
ListNode fastPtr = head;
// Iterate until fastPr doesn't reach the end of the list
while (fastPtr != null && fastPtr.next != null) {
prevPtr = slowPtr;
slowPtr = slowPtr.next;
fastPtr = fastPtr.next.next;
}
// If the slow pointer is not the first node, disconnect it from the list
if (prevPtr != null) {
prevPtr.next = null;
}
return slowPtr;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 查找中间节点:对于长度为 n 的链表,找到中间节点需要 O(n) 时间。
- 递归构建左右子树:由于每次递归链表长度减半,所以递归的次数为 log(n)。
- 每次递归中,除了递归调用本身,没有其他额外的操作。
综上所述,总的时间复杂度为 O(nlog(n))。
2. 空间复杂度
- 递归栈空间:由于构建的是平衡二叉搜索树,递归的深度为 O(log(n)),因此递归栈的空间复杂度为 O(log(n))。
- 辅助空间:除了递归栈之外,没有使用额外的空间。
综上所述,总的空间复杂度为 O(log(n))。
五、总结知识点
1. 链表操作:
- 遍历链表:使用while循环和指针遍历链表。
- 快慢指针技巧:用于找到链表的中间节点,快指针每次移动两步,慢指针每次移动一步。
2. 递归:
- 递归是函数自己调用自己的过程,用于解决分而治之的问题,如这里的链表转换为二叉搜索树。
3. 二叉搜索树(BST)的性质:
- BST是一种特殊的二叉树,其中每个节点的左子树只包含小于当前节点的值,右子树只包含大于当前节点的值。
4. 平衡二叉树的构建:
- 通过选择链表的中间元素作为子树的根节点,可以保证构建出的二叉搜索树是平衡的。
5. 函数定义与调用:
sortedListToBST
是主函数,负责启动递归过程。findMiddle
是辅助函数,负责找到链表的中间节点并断开链表。
6. 指针和引用:
- 在Java中,虽然没有指针的概念,但是对象引用可以类比指针,用于操作对象。
7. 链表与树的转换:
- 将一个有序链表转换为平衡二叉搜索树,涉及到数据结构之间的转换。
8. 递归的终止条件:
- 当链表为空时,递归结束,返回
null
。
9. 节点断开:
- 在找到中间节点后,需要将中间节点的前一个节点的
next
引用设置为null
,从而断开链表。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。