Esempio n. 1
0
#         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)
Esempio n. 2
0
                    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))
Esempio n. 3
0
                    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))
    
Esempio n. 4
0
# 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)
Esempio n. 5
0
                    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))
Esempio n. 6
0
                    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])
Esempio n. 7
0
    # 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))
Esempio n. 8
0
            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))
Esempio n. 9
0
        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)
Esempio n. 11
0
            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))
Esempio n. 12
0
    # 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)
Esempio n. 14
0
# 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))