From 4b9f37222d637e7f3329694d37a39dfd2203693f Mon Sep 17 00:00:00 2001 From: Paridhi Malviya Date: Sun, 8 Feb 2026 19:05:22 -0600 Subject: [PATCH] Complete Trees-3 --- PathSumTwo.swift | 129 +++++++++++++++++++++++++++++ SymmetricTree.swift | 195 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 PathSumTwo.swift create mode 100644 SymmetricTree.swift diff --git a/PathSumTwo.swift b/PathSumTwo.swift new file mode 100644 index 00000000..c779141d --- /dev/null +++ b/PathSumTwo.swift @@ -0,0 +1,129 @@ +// +// RootToLeafSumPathTwo.swift +// DSA-Practice +// +// Created by Paridhi Malviya on 1/27/26. +// + +class RootToLeafSumPathTwo { + + init() { + + } + /* + In worst case, h will be n. left skewed tree. O(n2) + 2.Space complexity - O(n *h) - everytime, creating a new list while deep copying. + + Backtrack - To be back to the previous state. Whatever we did before the recursion call, we have to remove it. To backtrack the action that we took before recursion. time complexity - O(n), space compelxity - O(h) + */ + //using global result variable + + var result = [[Int]]() + func pathSum(_ root: TreeNode?, _ targetSum: Int) -> [[Int]] { + var path: [Int] = [] + getTargetSumPaths(root, targetSum: targetSum, path: path, currentSum: 0) + return result + } + + func getTargetSumPaths(_ root: TreeNode?, targetSum: Int, path: [Int], currentSum: Int) { + + //base + guard let root = root else { + return + } + + //logic + let currSum = currentSum + root.val + + //action + var p = path + p.append(root.val) + if (root.left == nil && root.right == nil) { + if (currSum == targetSum) { + result.append(p) + } + return + } + + //recurse + getTargetSumPaths(root.left, targetSum: targetSum, path: p, currentSum: currSum) + getTargetSumPaths(root.right, targetSum: targetSum, path: p, currentSum: currSum) + + //backtrack - not needed because list is value type in Swift, not reference type + } + + //MARK: without using global result variable + /* + time complexityt - O(n * h), in worst case scenario - n can become n - O(n^2) + copying all elements from path to p is taking O(h) time complexity. worst case O(n) + space compelxity -> O(n * h), at all nodes, we are copying elements from one list to another, we are keeping auxilliary list.. It's time inefficient. + */ + func getTargetSumPathsTwo(_ root: TreeNode?, targetSum: Int, path: [Int], currentSum: Int) -> [[Int]] { + //base + guard let root = root else { + return [[Int]]() + } + + //logic + let currSum = currentSum + root.val + var resultArray = [[Int]]() + //action + //deep copy of path. append the new element + var p = path + p.append(root.val) + if (root.left == nil && root.right == nil) { + if (currSum == targetSum) { + resultArray.append(p) + return resultArray + } + } + + //recurse + let resultFromLeft = getTargetSumPathsTwo(root.left, targetSum: targetSum, path: p, currentSum: currSum) + let resultFromRight = getTargetSumPathsTwo(root.right, targetSum: targetSum, path: p, currentSum: currSum) + resultArray.append(contentsOf: resultFromLeft) + resultArray.append(contentsOf: resultFromRight) + return resultArray + } + + /* + For better time and space complexity, use refernce type and use backtracking. Remove the last element which was added to get back to the previous state. + When we got the path, then copy the path in the result by changing the refernce (to not mutate it again when changing something else). + when backtracking - removal from the end is O(1) + time compelxity - O(n). just traversing over all the nodes. Not creating deep copies. + space complexity -> O(h). because the auxilliary list can't go beyond height of the tree. Recursion call stack will also be O(h) + */ + + //backtrack + func getTargetSumPathsUsingBacktracking(_ root: TreeNode?, targetSum: Int, path: inout [Int], currentSum: Int) { + var path = path + //base + guard let root = root else { + return + } + + //logic + let currSum = currentSum + root.val + print("path \(path)") + //action + path.append(root.val) + print("after path \(path)") + if (root.left == nil && root.right == nil) { + if (currSum == targetSum) { + let p = path + result.append(path) + } + return + } + + //recurse + getTargetSumPathsUsingBacktracking(root.left, targetSum: targetSum, path: &path, currentSum: currSum) + getTargetSumPathsUsingBacktracking(root.right, targetSum: targetSum, path: &path, currentSum: currSum) + + //backtrack + print("while backtracking \(path)") + path.removeLast() + print("after backtracking \(path)") + } + +} diff --git a/SymmetricTree.swift b/SymmetricTree.swift new file mode 100644 index 00000000..2c115cd0 --- /dev/null +++ b/SymmetricTree.swift @@ -0,0 +1,195 @@ +// +// SymmetricTree.swift +// DSA-Practice +// +// Created by Paridhi Malviya on 1/23/26. +// +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ + +class QueueImplUsingLLWithNull { + + private class LinkedListNode { + var value: T? + var next: LinkedListNode? + init(value: T?) { + self.value = value + } + } + + private var front: LinkedListNode? + private var rear: LinkedListNode? + private var count: Int = 0 + + func enqueue(_ val: T?) { + let newNode = LinkedListNode(value: val) + if(rear == nil) { + //empty linked List + rear = newNode + front = newNode + } else { + rear?.next = newNode + rear = newNode + } + count += 1 + } + + func dequeue() -> T? { + guard let frontNode = front else { + return nil + } + let value = frontNode.value + front = frontNode.next + if (front == nil) { + rear = nil + } + count -= 1 + return value + } + + var size: Int { + return count + } + + var isEmpty: Bool { + return count == 0 + } + + var peek: T? { + return front?.value + } +} + +//MARK: Symmetric tree using queue +class SymmetricTree { + func isSymmetric(_ root: TreeNode?) -> Bool { + guard let root = root else { + return true + } + var queue = QueueImplUsingLLWithNull() + queue.enqueue(root.left) + queue.enqueue(root.right) + while(!queue.isEmpty) { + + let leftNode = queue.dequeue() + let rightNode = queue.dequeue() + + if (leftNode == nil && rightNode == nil) { + continue + } + if (leftNode == nil || rightNode == nil || leftNode?.val != rightNode?.val) { + return false + } + queue.enqueue(leftNode?.left) + queue.enqueue(rightNode?.right) + queue.enqueue(leftNode?.right) + queue.enqueue(rightNode?.left) + + } + return true + } +} + +//MARK: Implement stack using linked list +class StackImp { + + final class LinkedListNode { + var value: T? + var next: LinkedListNode? + init(value: T?, next: LinkedListNode?) { + self.value = value + self.next = next + } + } + + var head: LinkedListNode? + + func push(_ val: T?) { + let newNode = LinkedListNode(value: val, next: head) + head = newNode + } + + @discardableResult + func pop() -> T? { + guard let currentHead = head else { + return nil + } + head = currentHead.next + return currentHead.value + } + + func peek() -> T? { + return head?.value + } + + func isEmpty() -> Bool { + return head == nil + } + + } + +class SymmetricTreeWithStack { + func isSymmetric(_ root: TreeNode?) -> Bool { + guard let root = root else { + return true + } + var stack = StackImp() + stack.push(root.left) + stack.push(root.right) + + while(!stack.isEmpty()) { + let leftNode = stack.pop() + let rightNode = stack.pop() + if (leftNode == nil && rightNode == nil) { + continue + } + if (leftNode == nil || rightNode == nil || leftNode?.val != rightNode?.val) { + return false + } + stack.push(leftNode?.left) + stack.push(rightNode?.right) + stack.push(leftNode?.right) + stack.push(rightNode?.left) + } + return true + } + + //MARK: isSymmetric with under the hood stack + //time complexity - O(n) & space complexity - O(n) + func isSymmetricUsinfRecursionStack(_ root: TreeNode?) -> Bool { + return dfsUsingRecursion(root?.left, root?.right) + } + + + func dfsUsingRecursion(_ left: TreeNode?, _ right: TreeNode?) -> Bool { + + //Base + if (left == nil && right == nil) { + return true + } + //Logic + if (left == nil || right == nil || left?.val != right?.val) { + return false + } + let isOuterSymmetric = dfsUsingRecursion(left?.left, right?.right) + var isInnerSymmetric = false + if (isOuterSymmetric) { + isInnerSymmetric = dfsUsingRecursion(left?.right, right?.left) + } + return isOuterSymmetric && isInnerSymmetric + } + +}