diff --git a/leetcode_105.py b/leetcode_105.py new file mode 100644 index 00000000..37a07bab --- /dev/null +++ b/leetcode_105.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Mar 15 00:00:00 2026 + +@author: rishigoswamy + + LeetCode 105: Construct Binary Tree from Preorder and Inorder Traversal + Link: https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + + Problem: + Given two integer arrays preorder and inorder where preorder is the preorder + traversal of a binary tree and inorder is the inorder traversal of the same tree, + construct and return the binary tree. + + Note: You may assume that duplicates do not exist in the tree. + + Approach: + Use a hashmap for O(1) inorder index lookup and a global index pointer into preorder. + The first element of preorder is always the root. Its position in inorder splits + the tree into left and right subtrees. + + 1️⃣ Build inorderMap: value → index for O(1) lookup. + 2️⃣ self.idx tracks the current root in preorder (starts at 0). + 3️⃣ createTree(left, right) defines the valid inorder window. + 4️⃣ Pick preorder[self.idx] as root, increment self.idx. + 5️⃣ Recurse left on inorder[left : nodeIdx-1], then right on inorder[nodeIdx+1 : right]. + + // Time Complexity : O(n) + Each node is visited once; hashmap gives O(1) index lookup. + // Space Complexity : O(n) + O(n) for the hashmap + O(h) recursive call stack (h = height of tree). + +""" + +from typing import List, Optional + +# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + self.inorderMap = {val: idx for idx, val in enumerate(inorder)} # Map value to index for O(1) lookup + self.idx = 0 + return self.createTree(preorder, 0, len(inorder) - 1) + + def createTree(self, preOrder, left, right): + if left > right: + return None + + nodeValue = preOrder[self.idx] + self.idx += 1 + + nodeIdx = self.inorderMap[nodeValue] + + node = TreeNode(nodeValue) + + node.left = self.createTree(preOrder, left, nodeIdx - 1) + node.right = self.createTree(preOrder, nodeIdx + 1, right) + + return node + + ''' + def createTree(self, preorder: List[int], inorder: List[int], inorderMap: dict) -> Optional[TreeNode]: + if not preorder or not inorder: + return None # Base case: stop recursion if preorder or inorder list is empty + + root_val = preorder[0] + root = TreeNode(root_val) # Create the root node with the first value in preorder + + indexroot = -1 + for item in inorder: + indexroot += 1 + if item == root_val: + break + + leftin = inorder[:indexroot] + rightin = inorder[indexroot + 1:] + + leftpre = preorder[1: len(leftin) + 1] + rightpre = preorder[len(leftin) + 1:] + + root.left = self.createTree(leftpre, leftin, inorderMap) if leftin else None + root.right = self.createTree(rightpre, rightin, inorderMap) if rightin else None + + return root + + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + inorderMap = {val: idx for idx, val in enumerate(inorder)} # Map value to index for O(1) lookup + return self.createTree(preorder, inorder, inorderMap) + ''' + + ''' + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + if not preorder: + return None + + root = TreeNode(preorder[0]) + mid = inorder.index(preorder[0]) + root.left = self.buildTree(preorder[1:mid+1], inorder[:mid]) + root.right = self.buildTree(preorder[mid+1:], inorder[mid+1:]) + return root + ''' diff --git a/leetcode_98.py b/leetcode_98.py new file mode 100644 index 00000000..77657456 --- /dev/null +++ b/leetcode_98.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sat Mar 15 00:00:00 2026 + +@author: rishigoswamy + + LeetCode 98: Validate Binary Search Tree + Link: https://leetcode.com/problems/validate-binary-search-tree/ + + Problem: + Given a binary tree, determine if it is a valid binary search tree (BST). + A BST is defined as follows: + - The left subtree of a node contains only nodes with keys less than the node's key. + - The right subtree of a node contains only nodes with keys greater than the node's key. + - Both the left and right subtrees must also be binary search trees. + + Approach: + Inorder traversal with a running prev pointer. + In a valid BST, inorder traversal always yields a strictly increasing sequence. + Track self.prev (initialized to -inf) and self.flag for early termination. + + 1️⃣ Recurse left subtree. + 2️⃣ Check if self.prev >= current node's value → invalid, set flag = False. + 3️⃣ Update self.prev to current node's value. + 4️⃣ Recurse right subtree. + + // Time Complexity : O(n) + Every node is visited once. + // Space Complexity : O(h) + Recursive call stack depth equals the height of the tree. + +""" + +import math +from typing import Optional + +# 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 isValidBST(self, root: Optional[TreeNode]) -> bool: + self.flag = True + self.prev = -math.inf + def validate(root): + if not self.flag: # This is optimization. Can be removed + return False + if not root: + return True + validate(root.left) + if self.prev >= root.val: + self.flag = False + self.prev = root.val + validate(root.right) + + validate(root) + return self.flag + + + ''' + self.prev = -math.inf + def validate(root): + + if not root: + return True + left = True + left = validate(root.left) + + if not left: + return False + if self.prev >= root.val: + self.flag = False + return False + self.prev = root.val + right = validate(root.right) + + return left and right + + return validate(root) + + ''' + ''' + if not root: + return True + + def validate(root, low = -math.inf, high = math.inf): + if not root: + return True + if root.val <= low or root.val >= high: + return False + + return validate(root.left, low, root.val) and validate(root.right, + root.val, high) + return validate(root) + ''' + ''' + if not root: + return True + + + def traverse(root, res): + if root.left: + traverse(root.left, res) + res.append(root.val) + if root.right: + traverse(root.right, res) + + res = [] + traverse(root, res) + + for i in range(1, len(res)): + if res[i-1] >= res[i]: + return False + + return True + '''