diff --git a/Algorithms/Algorithms/Algorithms/Searching/BinarySearch.swift b/Algorithms/Algorithms/Algorithms/Searching/BinarySearch.swift index 022a56f4..a009044e 100644 --- a/Algorithms/Algorithms/Algorithms/Searching/BinarySearch.swift +++ b/Algorithms/Algorithms/Algorithms/Searching/BinarySearch.swift @@ -22,7 +22,7 @@ struct BinarySearch { //If `values[mid]` matches `target` then we have found our target and can return; if `values[mid]` does not match `target` //then we half the search space by moving either `left` to the right (if `values[mid]` was smaller than `target`) or moving //`right` to the left (if `values[mid]` was larger than `target`). We can reduce the search space like this because `values` - //sorted so we know that if `values[mid]` was smaller than `target` then any index less than `mid` will contain an even + //is sorted so we know that if `values[mid]` was smaller than `target` then any index less than `mid` will contain an even //smaller value than `values[mid]` so searching those other indexes would be pointless (the opposite is true for reducing //the search space to the right of `mid`). // diff --git a/LeetCode/LeetCode.xcodeproj/project.pbxproj b/LeetCode/LeetCode.xcodeproj/project.pbxproj index 6f367470..0c6dd0c3 100644 --- a/LeetCode/LeetCode.xcodeproj/project.pbxproj +++ b/LeetCode/LeetCode.xcodeproj/project.pbxproj @@ -626,6 +626,8 @@ 4335FB112B88F04500416BB6 /* ValidateBinaryTreeNodesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4335FB102B88F04500416BB6 /* ValidateBinaryTreeNodesTests.swift */; }; 4335FB132B8944D500416BB6 /* CheckIfNAndItsDoubleExist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4335FB122B8944D500416BB6 /* CheckIfNAndItsDoubleExist.swift */; }; 4335FB152B89455100416BB6 /* CheckIfNAndItsDoubleExistTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4335FB142B89455100416BB6 /* CheckIfNAndItsDoubleExistTests.swift */; }; + 4335FB172B894E9D00416BB6 /* ClosestNodesQueriesInABinarySearchTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4335FB162B894E9D00416BB6 /* ClosestNodesQueriesInABinarySearchTree.swift */; }; + 4335FB192B894ED700416BB6 /* ClosestNodesQueriesInABinarySearchTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4335FB182B894ED700416BB6 /* ClosestNodesQueriesInABinarySearchTreeTests.swift */; }; 4336E5542B236C520060A25D /* MakeCostsOfPathsEqualInABinaryTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4336E5532B236C520060A25D /* MakeCostsOfPathsEqualInABinaryTree.swift */; }; 4336E5562B236C810060A25D /* MakeCostsOfPathsEqualInABinaryTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4336E5552B236C810060A25D /* MakeCostsOfPathsEqualInABinaryTreeTests.swift */; }; 4336E5582B23CCA50060A25D /* EvenOddTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4336E5572B23CCA50060A25D /* EvenOddTree.swift */; }; @@ -1547,6 +1549,8 @@ 4335FB102B88F04500416BB6 /* ValidateBinaryTreeNodesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidateBinaryTreeNodesTests.swift; sourceTree = ""; }; 4335FB122B8944D500416BB6 /* CheckIfNAndItsDoubleExist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckIfNAndItsDoubleExist.swift; sourceTree = ""; }; 4335FB142B89455100416BB6 /* CheckIfNAndItsDoubleExistTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckIfNAndItsDoubleExistTests.swift; sourceTree = ""; }; + 4335FB162B894E9D00416BB6 /* ClosestNodesQueriesInABinarySearchTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosestNodesQueriesInABinarySearchTree.swift; sourceTree = ""; }; + 4335FB182B894ED700416BB6 /* ClosestNodesQueriesInABinarySearchTreeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosestNodesQueriesInABinarySearchTreeTests.swift; sourceTree = ""; }; 4336E5532B236C520060A25D /* MakeCostsOfPathsEqualInABinaryTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MakeCostsOfPathsEqualInABinaryTree.swift; sourceTree = ""; }; 4336E5552B236C810060A25D /* MakeCostsOfPathsEqualInABinaryTreeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MakeCostsOfPathsEqualInABinaryTreeTests.swift; sourceTree = ""; }; 4336E5572B23CCA50060A25D /* EvenOddTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvenOddTree.swift; sourceTree = ""; }; @@ -1972,6 +1976,7 @@ 3D5C90B827A7F7620035C399 /* CloneGraph.swift */, 3D5C90E227A7F7620035C399 /* ClosestBinarySearchTreeValue.swift */, 43643B1C2B0E3E7800B9D711 /* ClosestLeafInABinaryTree.swift */, + 4335FB162B894E9D00416BB6 /* ClosestNodesQueriesInABinarySearchTree.swift */, 433E55BF29DF145E00974B73 /* CoinChange .swift */, 3D46242C27E359F800A888C1 /* Combinations.swift */, 3D30553427C631AE00294BC7 /* CombinationSum.swift */, @@ -2428,6 +2433,7 @@ 3D5C922D27A7F76B0035C399 /* CloneGraphTests.swift */, 3D5C928827A7F76C0035C399 /* ClosestBinarySearchTreeValueTests.swift */, 43643B1E2B0E3EAD00B9D711 /* ClosestLeafInABinaryTreeTests.swift */, + 4335FB182B894ED700416BB6 /* ClosestNodesQueriesInABinarySearchTreeTests.swift */, 433E55C129DF14D600974B73 /* CoinChangeTests.swift */, 3D46242E27E35A4100A888C1 /* CombinationsTests.swift */, 3D193ABC27C78D9600E49F08 /* CombinationSumIIITests.swift */, @@ -2944,6 +2950,7 @@ 4347A1C429E2158300501CEE /* ContainsDuplicate.swift in Sources */, 43EE0BDA2A55A5CD003D23E0 /* PartitionLabels.swift in Sources */, 43F240692A67067200FB5964 /* BoatsToSavePeople.swift in Sources */, + 4335FB172B894E9D00416BB6 /* ClosestNodesQueriesInABinarySearchTree.swift in Sources */, 43C8CB4F2A5426F200FAA411 /* HandOfStraights.swift in Sources */, 43EF3C212AFB7FB60085445E /* DetermineIfACellIsReachableAtAGivenTime.swift in Sources */, 431F3D582A634CAB00500C0E /* NumberOfZeroFilledSubarrays.swift in Sources */, @@ -3434,6 +3441,7 @@ 3D5C932027A7F76C0035C399 /* SortListTests.swift in Sources */, 3D1D0C85280645C7006DA350 /* GameOfLifeTests.swift in Sources */, 3D5C92B827A7F76C0035C399 /* UniqueBinarySearchTreesTests.swift in Sources */, + 4335FB192B894ED700416BB6 /* ClosestNodesQueriesInABinarySearchTreeTests.swift in Sources */, 3D5C92D427A7F76C0035C399 /* NumberOfProvincesTests.swift in Sources */, 431E35732AF15A1F0019BB1F /* NumberOfClosedIslandsTests.swift in Sources */, 433953052B041833009F067C /* SumOfNodesWithEvenValuedGrandparentTests.swift in Sources */, diff --git a/LeetCode/LeetCode/Challenges/CheckIfNAndItsDoubleExist.swift b/LeetCode/LeetCode/Challenges/CheckIfNAndItsDoubleExist.swift index 6f8de2f3..898e753b 100644 --- a/LeetCode/LeetCode/Challenges/CheckIfNAndItsDoubleExist.swift +++ b/LeetCode/LeetCode/Challenges/CheckIfNAndItsDoubleExist.swift @@ -24,7 +24,7 @@ struct CheckIfNAndItsDoubleExist { func checkIfExist(_ arr: [Int]) -> Bool { var seen = Set() - for (index, value) in arr.enumerated() { + for value in arr { //have we seen double this value already if seen.contains((value * 2)) { return true diff --git a/LeetCode/LeetCode/Challenges/ClosestNodesQueriesInABinarySearchTree.swift b/LeetCode/LeetCode/Challenges/ClosestNodesQueriesInABinarySearchTree.swift new file mode 100644 index 00000000..7e1f0219 --- /dev/null +++ b/LeetCode/LeetCode/Challenges/ClosestNodesQueriesInABinarySearchTree.swift @@ -0,0 +1,96 @@ +// +// ClosestNodesQueriesInABinarySearchTree.swift +// LeetCode +// +// Created by William Boles on 23/02/2024. +// + +import Foundation + +//https://leetcode.com/problems/closest-nodes-queries-in-a-binary-search-tree/ +struct ClosestNodesQueriesInABinarySearchTree { + + //Time: O( + //Space: O( + //binary tree + //binary search tree + //binary search + //in-order + //DFS + //recursive + //inout + //array + // + //Solution Description: + //As this is binary search tree, we know that an in-order traversal will produce a sorted array. With a sorted array we can + //then perform two binary searches to find both the upper and lower bounds of each query item. + func closestNodes(_ root: TreeNode?, _ queries: [Int]) -> [[Int]] { + guard let root = root else { + return [[Int]]() + } + + var values = [Int]() + inorder(root, &values) + + var result = [[Int]]() + + for query in queries { + let min = findMax(values, query) + let max = findMin(values, query) + + result.append([min, max]) + } + + return result + } + + private func inorder(_ root: TreeNode?, _ order: inout [Int]) { + guard let root = root else { + return + } + + inorder(root.left, &order) + order.append(root.val) + inorder(root.right, &order) + } + + private func findMax(_ values: [Int], _ target: Int) -> Int { + var left = 0 + var right = values.count - 1 + + var result = -1 + + while left <= right { + let mid = left + (right - left) / 2 //to avoid overflow + + if values[mid] <= target { //find the upper bounds that is less than or equal to target + result = values[mid] //we know that values[mid] is a valid result so store it and check again + left = mid + 1 + } else { + right = mid - 1 + } + } + + return result + } + + private func findMin(_ values: [Int], _ target: Int) -> Int { + var left = 0 + var right = values.count - 1 + + var result = -1 + + while left <= right { + let mid = left + (right - left) / 2 //to avoid overflow + + if values[mid] >= target { //find the lower bounds that is greater than or equal to target + result = values[mid] //we know that values[mid] is a valid result so store it anad check again + right = mid - 1 + } else { + left = mid + 1 + } + } + + return result + } +} diff --git a/LeetCode/LeetCodeTests/Tests/ClosestNodesQueriesInABinarySearchTreeTests.swift b/LeetCode/LeetCodeTests/Tests/ClosestNodesQueriesInABinarySearchTreeTests.swift new file mode 100644 index 00000000..602d7bb8 --- /dev/null +++ b/LeetCode/LeetCodeTests/Tests/ClosestNodesQueriesInABinarySearchTreeTests.swift @@ -0,0 +1,70 @@ +// +// ClosestNodesQueriesInABinarySearchTreeTests.swift +// LeetCodeTests +// +// Created by William Boles on 23/02/2024. +// + +import XCTest + +@testable import LeetCode + +final class ClosestNodesQueriesInABinarySearchTreeTests: XCTestCase { + + //MARK: - Tests + + func test_A() { + let data = [6,2,13,1,4,9,15,nil,nil,nil,nil,nil,nil,14] + let queries = [2,5,16] + + let root = TreeNode.deserialize(data) + + let result = ClosestNodesQueriesInABinarySearchTree().closestNodes(root, queries) + + XCTAssertEqual(result, [[2,2],[4,6],[15,-1]]) + } + + func test_B() { + let data = [4,nil,9] + let queries = [3] + + let root = TreeNode.deserialize(data) + + let result = ClosestNodesQueriesInABinarySearchTree().closestNodes(root, queries) + + XCTAssertEqual(result, [[-1,4]]) + } + + func test_C() { + let data = [16,8,18,1,12,nil,20,nil,2,9,nil,nil,nil,nil,7] + let queries = [8,14,285508,6] + + let root = TreeNode.deserialize(data) + + let result = ClosestNodesQueriesInABinarySearchTree().closestNodes(root, queries) + + XCTAssertEqual(result, [[8,8],[12,16],[20,-1],[2,7]]) + } + + func test_D() { + let data = [16,14,nil,4,15,1] + let queries = [10,6,2,9] + + let root = TreeNode.deserialize(data) + + let result = ClosestNodesQueriesInABinarySearchTree().closestNodes(root, queries) + + XCTAssertEqual(result, [[4,14],[4,14],[1,4],[4,14]]) + } + + func test_E() { + let data = [9,6,14,nil,nil,13,20,12] + let queries = [19,10,9,17,19,6,10,19,13,6] + + let root = TreeNode.deserialize(data) + + let result = ClosestNodesQueriesInABinarySearchTree().closestNodes(root, queries) + + XCTAssertEqual(result, [[14,20],[9,12],[9,9],[14,20],[14,20],[6,6],[9,12],[14,20],[13,13],[6,6]]) + } +}