From e975f45cc292de4011d67c1280896681e67f39a5 Mon Sep 17 00:00:00 2001 From: Mohshinsha Shahmadar Date: Tue, 10 Dec 2024 17:45:33 +0800 Subject: [PATCH 1/3] Add `OverlappingRectangles` fixes #38 --- Sources/Geometry/OverlappingRectangles.swift | 137 ++++++++++++++++++ .../Geometry/MinAreaRectangleTests.swift | 79 ++++++++++ .../Geometry/OverlappingRectangleTests.swift | 31 ++++ 3 files changed, 247 insertions(+) create mode 100644 Sources/Geometry/OverlappingRectangles.swift create mode 100644 Tests/SwiftyAlgorithmsTests/Geometry/MinAreaRectangleTests.swift create mode 100644 Tests/SwiftyAlgorithmsTests/Geometry/OverlappingRectangleTests.swift diff --git a/Sources/Geometry/OverlappingRectangles.swift b/Sources/Geometry/OverlappingRectangles.swift new file mode 100644 index 0000000..6e3b6d6 --- /dev/null +++ b/Sources/Geometry/OverlappingRectangles.swift @@ -0,0 +1,137 @@ +// +// OverlappingRectangles.swift +// +// +// Created by Mohshinsha Shahmadar on 2024-12-10. +// + +import Foundation +/** + ### Problem Statement: + Write a function that takes two rectangles as inputs and determines if they overlap. Each rectangle is represented by its bottom-left corner (x1, y1) and top-right corner (x2, y2). The function should return `true` if the rectangles overlap and `false` otherwise. + + ### Input: + - Each rectangle is represented by a tuple of four integers: `(x1, y1, x2, y2)`, where: + - `(x1, y1)` represents the coordinates of the bottom-left corner. + - `(x2, y2)` represents the coordinates of the top-right corner. + + ### Output: + - Return `true` if the two rectangles overlap, `false` otherwise. + + ### Constraints: + - You may assume that the edges of the rectangles are aligned with the X and Y axes (i.e., they are not rotated). + - Rectangles that share an edge or a corner are **not** considered to be overlapping. + + ### Test Cases: + + 1. **Test Case 1: Partially Overlapping** + ```swift + Input: rect1 = (0, 0, 4, 4), rect2 = (2, 2, 6, 6) + Output: true + ``` + + 2. **Test Case 2: Non-Overlapping** + ```swift + Input: rect1 = (0, 0, 2, 2), rect2 = (3, 3, 5, 5) + Output: false + ``` + + 3. **Test Case 3: One Rectangle Inside Another** + ```swift + Input: rect1 = (0, 0, 6, 6), rect2 = (2, 2, 4, 4) + Output: true + ``` + + 4. **Test Case 4: Touching at Edge (Non-Overlapping)** + ```swift + Input: rect1 = (0, 0, 4, 4), rect2 = (4, 0, 6, 3) + Output: true + ``` + + 5. **Test Case 5: Touching at Corner (Non-Overlapping)** + ```swift + Input: rect1 = (0, 0, 3, 3), rect2 = (3, 3, 6, 6) + Output: true + ``` + */ +func checkIfRectanglesIntersectingEachOther(with input: [[Int]]) -> Bool { + let rectangles: [Rectangle] = input.map(Rectangle.init) + guard !rectangles.isEmpty else { return false } + + var resultRectangle = rectangles[0] + + for i in 1.. Rectangle.OverlappingType { + switch renderedType { + case .horizotalLine, .verticalLine: return .touchingEdge + case .rectangle: return .intersecting + case .point: return .touchingPoint + case .invalid: return .farFromEachOther + } +} + +fileprivate struct Rectangle { + enum OverlappingType: String { + case overlapping + case intersecting + case touchingEdge + case touchingPoint + case farFromEachOther + } + + enum RenderedType { + case horizotalLine + case verticalLine + case rectangle + case point + case invalid + } + + init(bottomLeft: (x: Int, y: Int), topRight: (x: Int, y: Int)) { + self.bottomLeft = bottomLeft + self.topRight = topRight + } + + let bottomLeft: (x: Int, y: Int) + let topRight: (x: Int, y: Int) + + init(_ elements: [Int]) { + guard elements.count == 4 else { fatalError("Invaldi Rect Data Provided") } + bottomLeft = (x: elements[0], y: elements[1]) + topRight = (x: elements[2], y: elements[3]) + } + + var renderingType: RenderedType { + guard bottomLeft.x <= topRight.x && bottomLeft.y <= topRight.y else { return .invalid } + if bottomLeft.x == topRight.x && bottomLeft.y < topRight.y { + return .verticalLine + } else if bottomLeft.x < topRight.x && bottomLeft.y == topRight.y { + return .horizotalLine + } else if bottomLeft.x == topRight.x && bottomLeft.y == topRight.y { + return .point + } else if bottomLeft.x < topRight.x && bottomLeft.y < topRight.y { + return .rectangle + } else { + return .invalid + } + } + + func intersect(with rect2: Rectangle) -> Rectangle { + let rect1 = self + return Rectangle( + bottomLeft: ( + x: max(rect1.bottomLeft.x, rect2.bottomLeft.x), + y: max(rect1.bottomLeft.y, rect2.bottomLeft.y) + ), + topRight: ( + x: min(rect1.topRight.x, rect2.topRight.x), + y: min(rect1.topRight.y, rect2.topRight.y) + ) + ) + } +} diff --git a/Tests/SwiftyAlgorithmsTests/Geometry/MinAreaRectangleTests.swift b/Tests/SwiftyAlgorithmsTests/Geometry/MinAreaRectangleTests.swift new file mode 100644 index 0000000..8d3d9b6 --- /dev/null +++ b/Tests/SwiftyAlgorithmsTests/Geometry/MinAreaRectangleTests.swift @@ -0,0 +1,79 @@ +// +// MinAreaRectangle.swift +// +// +// Created by Mohshinsha Shahmadar on 2024-10-20. +// + +import Foundation +import XCTest +@testable import SwiftyAlgorithms + +final class MinAreaRectangleTests: XCTestCase { + + func testIntersectingRectangles() { + let testCases: [AlgorithmTestCase] = [ + .init([[1, 1],[1, 3],[3, 1], [3, 3], [4,1], [4,3]], 2), + ] + + testAlgorithm( + name: "MinAreaRectangleTests", + with: testCases) { input in + minAreaFreeRect(input) + } + } +} + +/** + You are given an array of points in the X-Y plane points where points[i] = [xi, yi]. + Return the minimum area of any rectangle formed from these points, with sides not necessarily parallel to the X and Y axes. If there is not any such rectangle, return 0. + + Answers within 10-5 of the actual answer will be accepted. + + Example 1: + Input: points = [[1,2],[2,1],[1,0],[0,1]] + Output: 2 + Explanation: The minimum area rectangle occurs at [1,2],[2,1],[1,0],[0,1], with an area of 2. + + Example 2: + Input: points = [[0,1],[2,1],[1,1],[1,0],[2,0]] + Output: 1 + Explanation: The minimum area rectangle occurs at [1,0],[1,1],[2,1],[2,0], with an area of 1. + + Example 3: + Input: points = [[0,3],[1,2],[3,1],[1,3],[2,1]] + Output: 0 + Explanation: There is no possible rectangle to form from these points. + */ +func minAreaFreeRect(_ points: [[Int]]) -> Int { + struct Point: Hashable { + let x: Int + let y: Int + } + + let points: [Point] = points.map { Point(x: $0[0], y: $0[1]) } + + var xPointsMap = [Int: Set]() + + for point in points { + var existingSet: Set = xPointsMap[point.x] ?? .init() + existingSet.insert(point.y) + xPointsMap[point.x] = existingSet + } + + var result = Int.max + for point1 in points { + for point2 in points { + if point1.x == point2.x || point1.y == point2.y { + continue + } + + if xPointsMap[point1.x]?.contains(point2.y) == true, + xPointsMap[point2.x]?.contains(point1.y) == true { + let area = abs(point2.x - point1.x) * abs(point2.y - point1.y) + result = min(result, area) + } + } + } + return result == .max ? 0 : result +} diff --git a/Tests/SwiftyAlgorithmsTests/Geometry/OverlappingRectangleTests.swift b/Tests/SwiftyAlgorithmsTests/Geometry/OverlappingRectangleTests.swift new file mode 100644 index 0000000..f2082bb --- /dev/null +++ b/Tests/SwiftyAlgorithmsTests/Geometry/OverlappingRectangleTests.swift @@ -0,0 +1,31 @@ +// +// OverlappingRectangleTests.swift +// +// +// Created by Mohshinsha Shahmadar on 2024-10-20. +// + +import Foundation +import XCTest +@testable import SwiftyAlgorithms + +final class OverlappingRectangleTests: XCTestCase { + + func testIntersectingRectangles() { + let testCases: [AlgorithmTestCase] = [ + .init([[0, 0, 4, 4], [2, 2, 6, 6]], true, "Partially Overlapping"), + .init([[0, 0, 2, 2], [3, 3, 5, 5]], false, "Non-Overlapping"), + .init([[0, 0, 6, 6], [2, 2, 4, 4]], true, "One Rectangle Inside Another"), + .init([[0, 0, 4, 4], [4, 0, 6, 3]], false, "Touching at Edge (Non-Overlapping)"), + .init([[0, 0, 3, 3], [3, 3, 6, 6]], false, "Touching at Corner (Non-Overlapping)"), + .init([[0, 0, 2, 2], [3, 3, 4, 4]], false, "Inverted Rectangle") + ] + + testAlgorithm( + name: "IntersectingRectangle", + with: testCases + ) { input in + checkIfRectanglesIntersectingEachOther(with: input) + } + } +} From 34798771a6efa171370d86e3e36033ccd43dceb2 Mon Sep 17 00:00:00 2001 From: Mohshinsha Shahmadar Date: Tue, 10 Dec 2024 17:46:06 +0800 Subject: [PATCH 2/3] Refactor file structure to organize the algorithms --- .../ShortestDistanceFromAllBuildings.swift | 124 ++++++++++++++++++ .../DynamicProgramming/ClimbStairsTests.swift | 36 +++++ ...hortestDistanceFromAllBuildingsTests.swift | 29 ++++ 3 files changed, 189 insertions(+) create mode 100644 Sources/Graph-DFS/ShortestDistanceFromAllBuildings.swift create mode 100644 Tests/SwiftyAlgorithmsTests/DynamicProgramming/ClimbStairsTests.swift create mode 100644 Tests/SwiftyAlgorithmsTests/Graph-DFS/ShortestDistanceFromAllBuildingsTests.swift diff --git a/Sources/Graph-DFS/ShortestDistanceFromAllBuildings.swift b/Sources/Graph-DFS/ShortestDistanceFromAllBuildings.swift new file mode 100644 index 0000000..f29af4e --- /dev/null +++ b/Sources/Graph-DFS/ShortestDistanceFromAllBuildings.swift @@ -0,0 +1,124 @@ +// +// ShortestDistanceFromAllBuildings.swift +// +// +// Created by Mohshinsha Shahmadar on 2024-10-19. +// + +import Foundation + +/** + You are given an m x n grid of values 0, 1, or 2, where: + + Each 0 marks an empty land that you can pass through. + Each 1 marks a building that you cannot pass through. + Each 2 marks an obstacle that you cannot pass through. + You want to build a house on an empty land that reaches all buildings in the shortest total travel distance. You can only move up, down, left, and right. + + Return the shortest travel distance for such a house. If it is not possible to build such a house according to the above rules, return -1. + + The total travel distance is the sum of the distances between the house and the buildings. + + The distance is calculated using Manhattan Distance, where: `distance(p1,p2)=∣p2.x−p1.x∣+∣p2.y−p1.y∣` + + For the given grid + 1 0 2 0 1 + 0 0 0 0 0 + 0 0 1 0 0 + Output: 7 + + */ +func findShortestDistanceFromAllBuildings(grid: [[Int]]) -> Int { + struct Pair { + let row: Int + let col: Int + let distance: Int + var key: String { + "\(row)#\(col)" + } + + var left: Pair { .init(row: row - 1, col: col, distance: distance + 1) } + var right: Pair { .init(row: row + 1, col: col, distance: distance + 1) } + var top: Pair { .init(row: row, col: col - 1, distance: distance + 1) } + var bottom: Pair { .init(row: row, col: col + 1, distance: distance + 1) } + } + + guard !grid.isEmpty, !grid[0].isEmpty else { return -1 } + + let rows = grid.count + let cols = grid[0].count + + let land = 0 + let building = 1 + var numberOfBuildings = 0 + + for i in 0.. Bool { + return pair.col >= 0 && + pair.col < cols && + pair.row >= 0 && + pair.row < rows + } + + func performDFS(row: Int, col: Int) { + var queue = [Pair]() + let startPair = Pair(row: row, col: col, distance: 0) + var visited = Set() + queue.append(startPair) + visited.insert(startPair.key) + + var queueHead = 0 + + while queueHead < queue.endIndex { + let headPair = queue[queueHead] + distanceToBuildings[headPair.row][headPair.col].append(headPair.distance) + + for pair in [ + headPair.left, + headPair.right, + headPair.top, + headPair.bottom + ] where !visited.contains(pair.key) && isValid(pair: pair) && grid[pair.row][pair.col] == land { + queue.append(pair) + visited.insert(pair.key) + } + + queueHead += 1 + } + } + + + for i in 0.. Date: Wed, 9 Apr 2025 12:27:42 +0800 Subject: [PATCH 3/3] Fix build failures --- .../DynamicProgramming/ClimbStairsTests.swift | 36 ------------------- .../Geometry/OverlappingRectangleTests.swift | 14 ++++---- 2 files changed, 7 insertions(+), 43 deletions(-) delete mode 100644 Tests/SwiftyAlgorithmsTests/DynamicProgramming/ClimbStairsTests.swift diff --git a/Tests/SwiftyAlgorithmsTests/DynamicProgramming/ClimbStairsTests.swift b/Tests/SwiftyAlgorithmsTests/DynamicProgramming/ClimbStairsTests.swift deleted file mode 100644 index 296e43a..0000000 --- a/Tests/SwiftyAlgorithmsTests/DynamicProgramming/ClimbStairsTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// ClimbStairsTests.swift -// -// -// Created by Mohshinsha Shahmadar on 2024-09-09. -// - -import Foundation -import XCTest -@testable import SwiftyAlgorithms - -final class ClimbStairsTests: XCTestCase { - func testClimbStairs() { - - let testCases: [AlgorithmTestCase] = [ - .init(0, 0), - .init(1, 1), - .init(2, 2), - .init(3, 3), - .init(4, 5), - .init(5, 8), - .init(6, 13), - .init(7, 21), - .init(8, 34), - .init(9, 55), - // 1,2,3,5,8,13,21,34,55 - ] - - testAlgorithm( - name: "Ways to ClimbStairs to reach `n`", - with: testCases - ) { input in - findNumberOfWaysToClimbStairs(toReach: input) - } - } -} diff --git a/Tests/SwiftyAlgorithmsTests/Geometry/OverlappingRectangleTests.swift b/Tests/SwiftyAlgorithmsTests/Geometry/OverlappingRectangleTests.swift index f2082bb..9c31136 100644 --- a/Tests/SwiftyAlgorithmsTests/Geometry/OverlappingRectangleTests.swift +++ b/Tests/SwiftyAlgorithmsTests/Geometry/OverlappingRectangleTests.swift @@ -13,19 +13,19 @@ final class OverlappingRectangleTests: XCTestCase { func testIntersectingRectangles() { let testCases: [AlgorithmTestCase] = [ - .init([[0, 0, 4, 4], [2, 2, 6, 6]], true, "Partially Overlapping"), - .init([[0, 0, 2, 2], [3, 3, 5, 5]], false, "Non-Overlapping"), - .init([[0, 0, 6, 6], [2, 2, 4, 4]], true, "One Rectangle Inside Another"), - .init([[0, 0, 4, 4], [4, 0, 6, 3]], false, "Touching at Edge (Non-Overlapping)"), - .init([[0, 0, 3, 3], [3, 3, 6, 6]], false, "Touching at Corner (Non-Overlapping)"), - .init([[0, 0, 2, 2], [3, 3, 4, 4]], false, "Inverted Rectangle") + AlgorithmTestCase([[0, 0, 4, 4], [2, 2, 6, 6]], 1, "Partially Overlapping"), + AlgorithmTestCase([[0, 0, 2, 2], [3, 3, 5, 5]], 0, "Non-Overlapping"), + AlgorithmTestCase([[0, 0, 6, 6], [2, 2, 4, 4]], 1, "One Rectangle Inside Another"), + AlgorithmTestCase([[0, 0, 4, 4], [4, 0, 6, 3]], 0, "Touching at Edge (Non-Overlapping)"), + AlgorithmTestCase([[0, 0, 3, 3], [3, 3, 6, 6]], 0, "Touching at Corner (Non-Overlapping)"), + AlgorithmTestCase([[0, 0, 2, 2], [3, 3, 4, 4]], 0, "Inverted Rectangle") ] testAlgorithm( name: "IntersectingRectangle", with: testCases ) { input in - checkIfRectanglesIntersectingEachOther(with: input) + return checkIfRectanglesIntersectingEachOther(with: input) ? 1 : 0 } } }