# self.left = left # self.right = right class Solution: # DFS saving the depth and parents of nodes x and y # Time: O(n) # Space: O(h) - The height of the tree kept in the stack def isCousins(self, root: TreeNode, x: int, y: int) -> bool: if not root: return False stack = [(root, 0, None)] vals = {} while stack and (x not in vals or y not in vals): node, depth, parent = stack.pop() if node.val == x or node.val == y: vals[node.val] = (depth, parent) if node.right: stack.append((node.right, depth + 1, node)) if node.left: stack.append((node.left, depth + 1, node)) if x not in vals or y not in vals: return False x_depth, x_parent = vals[x] y_depth, y_parent = vals[y] return x_depth == y_depth and x_parent != y_parent if __name__ == '__main__': obj = Solution() assert(obj.isCousins(deserialize('[1,2,3,4]'), 4, 3) == False) assert(obj.isCousins(deserialize('[1,2,3,null,4,null,5]'), 5, 4) == True) assert(obj.isCousins(deserialize('[1,2,3,null,4]'), 2, 3) == False)
nodeDeque.append(node.left) if node.right: nodeDeque.append(node.right) return depth # Time: O(n) # Space: O(h) - where h is the height of the tree # Maybe more Pythonic iterative solution? def maxDepth4(self, root): if root is None: return 0 depth = 0 level = [root] while level: depth += 1 level = [ child for node in level for child in (node.left, node.right) if child ] return depth if __name__ == "__main__": root = deserialize('[3,9,20,null,null,15,7]') # root = deserialize('[37,-34,-48,null,-100,-100,48,null,null,null,null,-54,null,-71,-22,null,null,null,8]') # root = deserialize('[1,2,3,4,5]') # root = deserialize('[1,2,7,3,4,null,8,null,null,5,6,9]') # root = deserialize('[1]') obj = Solution() print(obj.maxDepth4(root))
node = node.right # If we get to the bottom of a node on the left side of the tree, repoint the right property # to the parent node if it's a left node. # If it's a right node it will either point to the root or # to the parent of the right node's parent (the grandparent) if node.right is None: treeData.append(curNode.val) node.right = curNode curNode = curNode.left # When we get to the bottom and we've been here before, reset the node # we changed previously so we could traverse upwards and make our current # node go right else: node.right = None curNode = curNode.right else: treeData.append(curNode.val) curNode = curNode.right return treeData if __name__ == "__main__": # root = deserialize('[1,null,2,3,4]') # root = deserialize('[1,2,null,4,5,6,7]') root = deserialize('[1,2,null,null,7,8,9]') # root = deserialize('[1,2,3,4,5,6,7]') # root = deserialize("[37,-34,-48,null,-100,-100,48,null,null,null,null,-54,null,-71,-22,null,null,null,8]") obj = Solution() print(obj.preorderMorrisTraversal(root))
# 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: # Time: O(n) # Space: O(h) - where h is the height of the tree def isValidBST(self, root: TreeNode) -> bool: if root is None: return True stack = [(root, float('-inf'), float('inf'))] while stack: node, lower_bound, upper_bound = stack.pop() if node.val <= lower_bound or node.val >= upper_bound: return False if node.right: stack.append((node.right, node.val, upper_bound)) if node.left: stack.append((node.left, lower_bound, node.val)) return True if __name__ == '__main__': obj = Solution() assert (obj.isValidBST(deserialize('[2,1,3]')) == True) assert (obj.isValidBST(deserialize('[3,1,5,0,2,4,6]')) == True) assert (obj.isValidBST(deserialize('[5,1,4,null,null,3,6]')) == False) assert (obj.isValidBST(deserialize('[1,0,1]')) == False) assert (obj.isValidBST(deserialize('[10,5,15,null,null,6,20]')) == False)
treeData += traceBack(curNode.left, node) node.right = None curNode = curNode.right else: curNode = curNode.right return treeData # Time: O(n) # Space: O(h) - the stack execution will hold the height # of the tree in memory def postorderTraversalRecursion(self, root): """ :type root: TreeNode :rtype: List[int] """ def _postorderTraversal(treeNode, treeData): if treeNode: _postorderTraversal(treeNode.left, treeData) _postorderTraversal(treeNode.right, treeData) treeData.append(treeNode.val) treeData = [] _postorderTraversal(root, treeData) return treeData if __name__ == "__main__": # root = deserialize('[1,null,2,3]') root = deserialize('[1,2,7,3,4,null,8,null,null,5,6,9]') obj = Solution() print(obj.postorderTraversal2(root))
if not is_second_pass: self.tree_max = max(self.tree_max, count) count = 1 if is_second_pass and count == self.tree_max: self.modes.append(node.val) prev = node.val node = node.right if not is_second_pass: self.tree_max = max(self.tree_max, count) if not root: return [] self.modes = [] self.tree_max = 0 inorder(root, False) # first pass to get the mode inorder(root, True) # second pass to get values that have mode return self.modes if __name__ == '__main__': obj = Solution() assert (sorted(obj.findMode(deserialize('[1,null,2,2]'))) == [2]) assert (sorted( obj.findMode( deserialize('[3,1,5,1,2,5,6,1,null,null,null,5]'))) == [1, 5]) assert (sorted(obj.findMode(deserialize('[2,1,2]'))) == [2]) assert (sorted(obj.findMode(deserialize('[1,null,2]'))) == [1, 2]) assert (sorted(obj.findMode(deserialize('[1,null,3,2,3]'))) == [3]) assert (sorted(obj.findMode( deserialize('[6,2,8,0,4,7,9,null,null,2,6]'))) == [2, 6])
# Time: O(n) # Space: O(h) - where h is the height of the tree # Similar to post-order traversal, but instead of revisitng nodes, # it stores the difference of the target and the node's value in order # to search for the desired path sum at the leaf nodes def hasPathSum3(self, root, sum): """ :type root: TreeNode :type sum: int :rtype: bool """ stack = [(root, sum)] while stack: node, val = stack.pop() if node: if not node.left and not node.right and node.val == val: return True stack.append((node.right, val - node.val)) stack.append((node.left, val - node.val)) return False if __name__ == "__main__": from treeVisualizer import deserialize root = deserialize( '[5,4,8,11,null,13,4,7,2,null,null,null,1]') # 22 - true # root = deserialize('[1,-2,-3,1,3,-2,null,-1]') # 3 - false # root = deserialize('[2,-1,null,null,-5,7,0,null,-5,null,-1,null,-4,-5]') # -10 - true obj = Solution() print(obj.hasPathSum3(root, 22))
stack.append(left.left) return True # Time: O(n) # Space: O(h) - where h is the height of the tree stored on the stack execution # Recursive solution def isSymmetric3(self, root): def _isSymmetricHelper(leftNode, rightNode): if leftNode is None and rightNode is None: return True if leftNode is None or rightNode is None or leftNode.val != rightNode.val: return False return _isSymmetricHelper(leftNode.left, rightNode.right) and _isSymmetricHelper( leftNode.right, rightNode.left) if not root: return True return _isSymmetricHelper(root.left, root.right) if __name__ == "__main__": # root = deserialize('[1]') # root = deserialize('[1,2,2,null,3,null,3]') # False # root = deserialize('[1,2,2,3,4,4,3]') # True # root = deserialize('[1,2,2,3,4,5,3]') # False # root = deserialize('[1,2,2,3,null,null,3]') # True root = deserialize('[1,2,2,null,3,3]') # True # root = deserialize('[1,2,3,3,null,2,null]') # This case shows why you can't use inorder traversal in this problem - should return False obj = Solution() print(obj.isSymmetric2(root))
return -1 class Solution2: # Time: O(n) - traverses all the elements in the tree # Space: O(h) def kthSmallest(self, root: TreeNode, k: int) -> int: def inorder(node: TreeNode): if node: inorder(node.left) self.count -= 1 if self.count == 0: self.res = node.val return inorder(node.right) self.count = k self.res = -1 inorder(root) return self.res if __name__ == '__main__': obj = Solution() root = deserialize('[3,1,4,null,2]') assert (obj.kthSmallest(root, 1) == 1) root2 = deserialize('[5,3,6,2,4,null,null,1]') assert (obj.kthSmallest(root2, 3) == 3) assert (obj.kthSmallest(root2, 6) == 6) root3 = deserialize('[5,null,6,null,7]') assert (obj.kthSmallest(root3, 2) == 6)
if node.left is not None: stack.append(node.left) if stack: node.right = stack[-1] node.left = None return # Time: O(nlog(n)) - ? # Space: O(1) def flatten2(self, root: TreeNode) -> None: now = root while now: if now.left: # Find current node's prenode that links to current node's right subtree pre = now.left while pre.right: pre = pre.right pre.right = now.right # Use current node's left subtree to replace its right subtree (original right) # sub is already linked by current node's prenode now.right = now.left now.left = None now = now.right if __name__ == '__main__': root = deserialize('[1,2,5,3,4,null,6]') obj = Solution() obj.flatten2(root) drawtree(root)
cur = q.popleft() if cur[1] is None: continue cur[0].val += cur[1].val if cur[0].left is None: cur[0].left = cur[1].left else: q.append([cur[0].left, cur[1].left]) if cur[0].right is None: cur[0].right = cur[1].right else: q.append([cur[0].right, cur[1].right]) return t1 # Time: O(n) # Space: O(h) def mergeTreesRecur(self, t1: TreeNode, t2: TreeNode) -> TreeNode: if t1 and t2: root = TreeNode(t1.val + t2.val) root.left = self.mergeTreesRecur(t1.left, t2.left) root.right = self.mergeTreesRecur(t1.right, t2.right) return root else: return t1 or t2 if __name__ == '__main__': t1 = deserialize('[1,3,2,5]') t2 = deserialize('[2,1,3,null,4,null,7]') obj = Solution() drawtree(obj.mergeTrees(t1, t2))
# Time: O(n) # Space: O(h) - since the stack execution will hold the height # of the tree in memory def inorderTraversalRecursion(self, root): """ :type root: TreeNode :rtype: List[int] """ treeData = [] self._inorderTraversal(root, treeData) return treeData def _inorderTraversal(self, treeNode, treeData): if treeNode: self._inorderTraversal(treeNode.left, treeData) treeData.append(treeNode.val) self._inorderTraversal(treeNode.right, treeData) if __name__ == "__main__": # root = deserialize('[1,null,2,3]') # root = deserialize('[1,2,3,4,5,null,6,null,null,7,8,9]') # root = deserialize('[1,2,2,null,3,null,3]') # root = deserialize('[1,2,2,3,4,4,3]') # root = deserialize('[1,2,2,3,null,null,3]') # root = deserialize('[1,2,2,3,4,4,3]') root = deserialize('[1,2,3,3,null,2,null]') obj = Solution() print(obj.inorderTraversal(root))
class Solution3: # Time: O(n) - Worst case visit all nodes # Space: O(n) def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode: if not root: return None stack = [root] parent = {root: None} # Find both nodes p and q in the tree, along with their parent nodes while p not in parent or q not in parent: node = stack.pop() if node.left: parent[node.left] = node stack.append(node.left) if node.right: parent[node.right] = node stack.append(node.right) ancestors = set() while p: # find all ancestors for p ancestors.add(p) p = parent[p] while q not in ancestors: q = parent[q] return q if __name__ == '__main__': obj = Solution3() root = deserialize('[3,5,1,6,2,0,8,null,null,7,4]') assert(obj.lowestCommonAncestor(root, root.left, root.right) == root) assert(obj.lowestCommonAncestor(root, root.left, root.left.right.right) == root.left)
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None from treeVisualizer import deserialize, TreeNode class Solution: # Time: O(n) - Have to visit every node # Space: O(n) - Size of the call stack during the DFS def diameterOfBinaryTree(self, root: TreeNode) -> int: self.diameter = 0 def maxDepth(node: TreeNode) -> int: if node is None: return 0 left_depth = maxDepth(node.left) right_depth = maxDepth(node.right) self.diameter = max(self.diameter, left_depth + right_depth) return max(left_depth, right_depth) + 1 maxDepth(root) return self.diameter if __name__ == "__main__": root = deserialize('[4,-7,-3,null,null,-9,-3,9,-7,-4,null,6,null,-6,-6,null,null,0,6,5,null,9,null,null,-1,-4,null,null,null,-2]') obj = Solution() # root = deserialize('[1,2,3,4,5]') # root = deserialize('[]') print(obj.diameterOfBinaryTree(root))