I am working on below interview question:
Given a singly linked list where elements are sorted in ascending
order, convert it to a height balanced BST.
For this problem, a height-balanced binary tree is defined as a binary
tree in which the depth of the two subtrees of every node never differ
by more than 1.
I am trying to understand below solution and its complexity? Can someone help me understand how it works? Is below solution O(n) time complexity and O(log n) space complexity?
Also is below algorithm better than "counting the number of nodes in the given Linked List. Let that be n. After counting nodes, we take left n/2 nodes and recursively construct the left subtree. After left subtree is constructed, we allocate memory for root and link the left subtree with root. Finally, we recursively construct the right subtree and link it with root. While constructing the BST, we also keep moving the list head pointer to next so that we have the appropriate pointer in each recursive call"
public TreeNode toBST(ListNode head) {
if(head==null) return null;
return helper(head,null);
}
public TreeNode helper(ListNode head, ListNode tail){
ListNode slow = head;
ListNode fast = head;
if(head==tail) return null;
while(fast!=tail && fast.next!=tail){
fast = fast.next.next;
slow = slow.next;
}
TreeNode thead = new TreeNode(slow.val);
thead.left = helper(head,slow);
thead.right = helper(slow.next,tail);
return thead;
}
BST-construction
A balanced tree can be constructed from a sorted list by subdividing the list into two equally long lists with one element in the middle being used as a root. E.g.:
1. [1, 2, 3, 4, 5, 6, 7]
2. 4
/ \
[1, 2, 3] [5, 6, 7]
3. 4
/ \
2 6
/ \ / \
1 3 5 7
Even if the two sublists differ by one element, they can at most differ by 1 in their height, thus making the tree balanced. By taking the middle element of the list the resulting tree is guaranteed to be a BST, since all smaller elements are part of the left subtree and all larger elements of the right subtree.
slow
and fast
Your code works using two iterators, where one (fast
) iterates over nodes twice as fast as the other (slow
). So when fast
has reached either the tail or the node right before the tail of the list, slow
must be at the node in the middle of the list, thus dividing the list into two sublists of same length (up to at most one element difference), which can then be recursively processed as shown in the above diagramm.
Runtime Complexity
The algorithm runs in O(n lg n)
. Let's start with the recurrence of helper:
T(n) = n / 2 + 2 * T(n / 2)
T(1) = 1
In each call of helper
, we must find the middle-node of the linkedlist defined by the two parameters passed to helper
. This can only be done in n / 2
steps, since we can only walk linearly through the list. In addition, the helper
is called recursively twice on linkedlists of half the size of the original list to build the left and right subtree.
Applying the Master-theorem(case 2) to the above recurrence, we get O(n lg n)
.
Space complexity
Space-complexity also needs to take the produced output-structure into account. Since each element of the input-linked list is converted into a node in the BST, the complexity is O(n)
.
EDIT
If the output is ignored, the space-complexity is solely dependent on the recursion-depth, which in turn is O(lg n)
, thus making the space-complexity O(lg n)
.