力扣 - 109. 有序链表转换为二叉搜索树
题目
思路1(分治+前序)
- 二叉搜索树就是左孩子比根节点小,右孩子比根节点大,而且左右两个子树的高度差不大于1称为二叉搜索树
- 通过观察这个链表转换成的搜索树可以发现,根节点其实就是链表的中间的结点,左孩子就是左边一半链表的中间的结点,右孩子就是右边一半链表的中间的结点
- 所以可以利用递归(自顶向下)方法来解题,递归的截止条件就是遍历到本段链表末尾
代码
class Solution {
public TreeNode sortedListToBST(ListNode head) {
if (head == null) {
return null;
}
if (head.next == null) {
return new TreeNode(head.val);
}
ListNode fast = head;
ListNode slow = head;
ListNode pre = null;
while (fast != null && fast.next != null) {
pre = slow;
slow = slow.next;
fast = fast.next.next;
}
pre.next = null;
TreeNode root = new TreeNode(slow.val);
root.left = sortedListToBST(head);
root.right = sortedListToBST(slow.next);
return root;
}
}
复杂度分析
- 时间复杂度:\(O(NlogN)\),其中 N 为链表的长度。
- 空间复杂度:\(O(logN)\),其中logN为树的深度,递归时所创建的空间
思路2(分治+中序)
- 我们只需要在分治的过程中不断进行分治,直到
left>right
,且在这过程中不需要找出链表的中位结点,而是先用一个空值的节点栈为构建一颗二叉树雏形 - 等到中序遍历遍历到该结点时,再进行值的填充
- 因为链表的结果是升序的,而二叉搜索树的中序遍历也是升序的,所以
globalHead = globalHead.next
就可以有序地填充树的节点的值
代码
class Solution {
ListNode globalHead;
public TreeNode sortedListToBST(ListNode head) {
globalHead = head;
// 获取链表长度
int length = 0;
while (head != null) {
length++;
head = head.next;
}
// 进行递归中序遍历构建树
return inorderBuild(0, length-1);
}
public TreeNode inorderBuild(int left, int right) {
if (left > right) {
return null;
}
// 获取中位数
int mid = (left + right + 1) / 2;
// 先构建空值的节点占位
TreeNode root = new TreeNode();
// 左子树遍历
root.left = inorderBuild(left, mid - 1);
// 进行节点的填充
root.val = globalHead.val;
globalHead = globalHead.next;
// 右子树遍历
root.right = inorderBuild(mid + 1, right);
return root;
}
}
复杂度分析
- 时间复杂度:\(O(N)\),其中 N 是链表长度
- 空间复杂度:\(O(logN)\)
我走得很慢,但我从不后退!