Skip to content

Commit 55fcab7

Browse files
committed
LeetCode 109. Convert Sorted List to Binary Search Tree
1 parent 9bbfd2a commit 55fcab7

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
9797
| [104. Maximum Depth of Binary Tree][lc104] | 🟢 Easy | [![python](res/py.png)][lc104py] [![rust](res/rs.png)][lc104rs] |
9898
| [105. Construct Binary Tree from Preorder and Inorder Traversal][lc105] | 🟠 Medium | [![python](res/py.png)][lc105py] |
9999
| [108. Convert Sorted Array to Binary Search Tree][lc108] | 🟢 Easy | [![python](res/py.png)][lc108py] |
100+
| [109. Convert Sorted List to Binary Search Tree][lc109] | 🟠 Medium | [![python](res/py.png)][lc109py] [![rust](res/rs.png)][lc109rs] |
100101
| [110. Balanced Binary Tree][lc110] | 🟢 Easy | [![python](res/py.png)](leetcode/balanced-binary-tree.py) |
101102
| [112. Path Sum][lc112] | 🟢 Easy | [![python](res/py.png)][lc112py] |
102103
| [113. Path Sum II][lc113] | 🟠 Medium | [![python](res/py.png)][lc113py] |
@@ -611,6 +612,9 @@ Solutions to LeetCode problems. The first column links to the problem in LeetCod
611612
[lc105py]: leetcode/construct-binary-tree-from-preorder-and-inorder-traversal.py
612613
[lc108]: https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/
613614
[lc108py]: leetcode/convert-sorted-array-to-binary-search-tree.py
615+
[lc109]: https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/
616+
[lc109py]: leetcode/convert-sorted-list-to-binary-search-tree.py
617+
[lc109rs]: leetcode/convert-sorted-list-to-binary-search-tree.rs
614618
[lc110]: https://leetcode.com/problems/balanced-binary-tree/
615619
[lc112]: https://leetcode.com/problems/path-sum/
616620
[lc112py]: leetcode/path-sum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# 109. Convert Sorted List to Binary Search Tree
2+
# 🟠 Medium
3+
#
4+
# https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/
5+
#
6+
# Tags: Linked List - Divide and Conquer - Tree - Binary Search Tree - Binary Tree
7+
8+
import timeit
9+
from typing import Optional
10+
11+
from utils.binary_tree import BinaryTree, TreeNode
12+
from utils.linked_list import LinkedList, ListNode
13+
14+
15+
# Use a divide and conquer approach, use two pointers to find the middle
16+
# of the linked list, use the middle node as the root of the BST, then
17+
# use two recursive calls with the unused left and right linked lists to
18+
# generate the left and right subtrees.
19+
#
20+
# Time complexity: O(n) - Each node will be visited one, two or three
21+
# times.
22+
# Space complexity: O(log(n)) - The height of the call stack.
23+
#
24+
# Runtime 126 ms Beats 63.28%
25+
# Memory 17.7 MB Beats 97.19%
26+
class Solution:
27+
def sortedListToBST(self, head: Optional[ListNode]) -> Optional[TreeNode]:
28+
if not head:
29+
return
30+
# Use two pointers to find the middle of the list.
31+
slow, fast = ListNode(0, next=head), head
32+
while fast and fast.next:
33+
slow, fast = slow.next, fast.next.next
34+
middle = slow.next
35+
root = TreeNode(middle.val)
36+
if middle is not head:
37+
# Slice the list
38+
slow.next = None
39+
root.left = self.sortedListToBST(head)
40+
if middle.next:
41+
root.right = self.sortedListToBST(middle.next)
42+
return root
43+
44+
45+
def test():
46+
executors = [Solution]
47+
tests = [
48+
[[], []],
49+
[[-10, -3, 0, 5, 9], [0, -3, 9, -10, None, 5]],
50+
]
51+
for executor in executors:
52+
start = timeit.default_timer()
53+
for _ in range(1):
54+
for col, t in enumerate(tests):
55+
sol = executor()
56+
head = LinkedList.fromList(t[0]).getHead()
57+
result = sol.sortedListToBST(head)
58+
result = BinaryTree(result).toList()
59+
exp = t[1]
60+
assert result == exp, (
61+
f"\033[93m» {result} <> {exp}\033[91m for"
62+
+ f" test {col} using \033[1m{executor.__name__}"
63+
)
64+
stop = timeit.default_timer()
65+
used = str(round(stop - start, 5))
66+
cols = "{0:20}{1:10}{2:10}"
67+
res = cols.format(executor.__name__, used, "seconds")
68+
print(f"\033[92m» {res}\033[0m")
69+
70+
71+
test()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// 109. Convert Sorted List to Binary Search Tree
2+
// 🟠 Medium
3+
//
4+
// https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/
5+
//
6+
// Tags: Linked List - Divide and Conquer - Tree - Binary Search Tree - Binary Tree
7+
8+
// Definition for singly-linked list.
9+
use std::cell::RefCell;
10+
use std::rc::Rc;
11+
#[derive(PartialEq, Eq, Clone, Debug)]
12+
pub struct ListNode {
13+
pub val: i32,
14+
pub next: Option<Box<ListNode>>,
15+
}
16+
17+
impl ListNode {
18+
#[inline]
19+
fn new(val: i32) -> Self {
20+
ListNode { next: None, val }
21+
}
22+
}
23+
// Definition for a binary tree node.
24+
#[derive(Debug, PartialEq, Eq)]
25+
pub struct TreeNode {
26+
pub val: i32,
27+
pub left: Option<Rc<RefCell<TreeNode>>>,
28+
pub right: Option<Rc<RefCell<TreeNode>>>,
29+
}
30+
31+
impl TreeNode {
32+
#[inline]
33+
pub fn new(val: i32) -> Self {
34+
TreeNode {
35+
val,
36+
left: None,
37+
right: None,
38+
}
39+
}
40+
}
41+
struct Solution;
42+
impl Solution {
43+
// Use a divide and conquer approach, use two pointers to find the middle
44+
// of the linked list, use the middle node as the root of the BST, then
45+
// use two recursive calls with the unused left and right linked lists to
46+
// generate the left and right subtrees. Solution inspired by:
47+
// https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/solutions/3282679
48+
// Even though I find the two pointer solution more elegant, in Rust it
49+
// seems like converting to array and using the same logic as in 108
50+
// performs better.
51+
//
52+
// Time complexity: O(n) - Each node will be visited one, two or three
53+
// times.
54+
// Space complexity: O(log(n)) - The height of the call stack.
55+
//
56+
// Runtime 466 ms Beats 14.29%
57+
// Memory 4 MB Beats 42.86%
58+
pub fn sorted_list_to_bst(head: Option<Box<ListNode>>) -> Option<Rc<RefCell<TreeNode>>> {
59+
match head {
60+
Some(_) => Self::slice_to_bst(head.as_ref(), None),
61+
None => None,
62+
}
63+
}
64+
65+
// An auxiliary function that converts a segment of a linked list to a BST
66+
// and returns the root.
67+
fn slice_to_bst(
68+
first: Option<&Box<ListNode>>,
69+
last: Option<&Box<ListNode>>,
70+
) -> Option<Rc<RefCell<TreeNode>>> {
71+
// If the segment is empty, return an empty tree.
72+
if first == last {
73+
return None;
74+
}
75+
// Use a fast and slow pointers.
76+
let (mut slow, mut fast) = (first, first);
77+
let mut fast_next: Option<&Box<ListNode>>;
78+
// Iterate over the list while fast is valid, i.e. not null or the last node
79+
// on the slice.
80+
while fast != last {
81+
// Move the auxiliary pointer one step and check if we could move another step.
82+
fast_next = fast.and_then(|node| node.next.as_ref());
83+
if fast_next == last {
84+
break;
85+
}
86+
// Move the slow pointer one step.
87+
slow = slow.and_then(|node| node.next.as_ref());
88+
// Fast needs to move two steps.
89+
fast = fast_next.and_then(|node| node.next.as_ref());
90+
}
91+
Some(Rc::new(RefCell::new(TreeNode {
92+
val: slow?.val,
93+
left: Self::slice_to_bst(first, slow),
94+
right: Self::slice_to_bst(slow?.next.as_ref(), last),
95+
})))
96+
}
97+
}
98+
99+
// Tests.
100+
fn main() {
101+
let tests = [
102+
(vec![3], 10, 1),
103+
(vec![3, 6, 7, 11], 8, 4),
104+
(vec![30, 11, 23, 4, 20], 5, 30),
105+
(vec![30, 11, 23, 4, 20], 6, 23),
106+
(
107+
vec![
108+
332484035, 524908576, 855865114, 632922376, 222257295, 690155293, 112677673,
109+
679580077, 337406589, 290818316, 877337160, 901728858, 679284947, 688210097,
110+
692137887, 718203285, 629455728, 941802184,
111+
],
112+
823855818,
113+
14,
114+
),
115+
];
116+
for test in tests {
117+
unimplemented!();
118+
}
119+
println!("No tests for this file!")
120+
}

0 commit comments

Comments
 (0)