Last active
March 13, 2025 18:25
-
-
Save linminhtoo/d9c7573dc6a74b0a145de0df2a0eb247 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Definition for a binary tree node. | |
# class TreeNode: | |
# def __init__(self, val=0, left=None, right=None): | |
# self.val = val | |
# self.left = left | |
# self.right = right | |
class Solution: | |
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: | |
''' | |
we can imagine some logN process, which we need to call K times, giving us KlogN time complexity. | |
or maybe even O(1) process * K, but that sounds like asking for too much. | |
with in-order traversal, worst case is O(N), best case is O(logN) | |
0 ms, beats 100% with early-stop mechanism. | |
the space complexity is probably around O(k + logn), since you need to traverse on average logn nodes to get to the leftmost value with in-order traversal, | |
and then you need to go through an additional k-1 more nodes on average. | |
The reason why it's important to account for this aspect of space complexity is because this solution can be performed via a stack (type of DFS). | |
So the stack in that solution should count as space complexity just as much as a recursive solution. | |
how to optimise for frequent modifications to the BST: | |
- turn it into a heap? maybe that's too much wasted compute. each heappop is O(logN) so time complexity is O(KlogN). for each insert, it can be done in O(logN) time. but deleting arbitrary nodes is trickier... | |
- if K is fixed, then first find the original Kth smallest node, and just keep track of which nodes got deleted or inserted and whether they're above or below the original kth smallest value? hmm | |
- if K is variable, then it's trickier... | |
''' | |
self.cnt = 0 | |
self.res = None | |
# in order traversal | |
def visit(node: Optional[TreeNode]): | |
if self.res is not None: | |
# found already, early stop | |
return | |
if node is None: | |
return | |
visit(node.left) | |
# using just `cnt` didnt work, strange... | |
self.cnt += 1 | |
if self.cnt == k: | |
self.res = node.val | |
return | |
visit(node.right) | |
return | |
visit(root) | |
return self.res |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion/
in order traversal without recursion but using stack, so O(N) memory cost regardless just without recursion stack
https://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/
O(1) memory using Morris Traversal