From 6ae8e603a86936d6c090910597da7bcd341120b7 Mon Sep 17 00:00:00 2001 From: Subhodip Banerjee Date: Wed, 6 Nov 2019 22:00:23 +0530 Subject: [PATCH 01/75] [String] Solution for License Key Formatting. --- README.md | 4 +++- String/LicenseKeyFormatting.swift | 35 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 String/LicenseKeyFormatting.swift diff --git a/README.md b/README.md index e96ef029..4b4ee207 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![Leetcode](./logo.png?style=centerme) ## Progress -[Problem Status](#problem-status) shows the latest progress to all 1000+ questions. Currently we have 292 completed solutions. Note: questions with ♥ mark means that you have to **Subscript to premium membership** of LeetCode to unlock them. +[Problem Status](#problem-status) shows the latest progress to all 1000+ questions. Currently we have 293 completed solutions. Note: questions with ♥ mark means that you have to **Subscript to premium membership** of LeetCode to unlock them. ## Contributors @@ -130,6 +130,8 @@ [Longest Substring with At Most Two Distinct Characters](https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/)| [Swift](./String/LongestSubstringMostTwoDistinctCharacters.swift)| Hard| O(n)| O(n)| [Longest Substring with At Most K Distinct Characters](https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/)| [Swift](./String/LongestSubstringMostKDistinctCharacters.swift)| Hard| O(n)| O(n)| [Text Justification](https://leetcode.com/problems/text-justification/)| [Swift](./String/TextJustification.swift)| Hard| O(n)| O(n)| +[Swift](./String/LicenseKeyFormatting.swift)| Easy| O(n)| O(1)| +[Text Justification](https://leetcode.com/problems/license-key-formatting/)| [Swift](./String/LicenseKeyFormatting.swift)| Easy| O(n)| O(1)| ## Linked List | Title | Solution | Difficulty | Time | Space | diff --git a/String/LicenseKeyFormatting.swift b/String/LicenseKeyFormatting.swift new file mode 100644 index 00000000..62a09706 --- /dev/null +++ b/String/LicenseKeyFormatting.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/license-key-formatting/ + * Primary idea: traverse the array formatted string from end. Insert the dash into the String as per given K, else insert the elements at index 0. + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class Solution { + func licenseKeyFormatting(_ S: String, _ K: Int) -> String { + guard S.count > 0 && K > 0 else{ + return "" + } + + let upperCaseString = Array(S.uppercased()) + var result = [Character](), n = K + + for i in stride(from: S.count - 1, through: 0, by: -1){ + let aCharacter = upperCaseString[i] + guard aCharacter != "-" else{ + continue + } + + if n == 0{ + result.insert("-", at: 0) + n = K + } + + result.insert(aCharacter, at: 0) + n -= 1 + } + + return String(result) + } +} From b7122cba0a62fe99e5afcefb14588fe804a51777 Mon Sep 17 00:00:00 2001 From: Subhodip Banerjee Date: Wed, 6 Nov 2019 22:10:49 +0530 Subject: [PATCH 02/75] Formatting Changed in readme.md for Design Tic-Tac-Toe --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b4ee207..1fcabc20 100644 --- a/README.md +++ b/README.md @@ -375,7 +375,7 @@ | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | [Shuffle an Array](https://leetcode.com/problems/shuffle-an-array/)| [Swift](./Design/ShuffleAnArray.swift)| Easy| O(n)| O(1)| -[Design Tic-Tac-Toe](https://leetcode.com/problems/design-tic-tac-toe/| [Swift](./Design/DesignTicTacToe.swift)| Medium| O(1)| O(n)| +[Design Tic-Tac-Toe](https://leetcode.com/problems/design-tic-tac-toe/)| [Swift](./Design/DesignTicTacToe.swift)| Medium| O(1)| O(n)| [Flatten 2D Vector](https://leetcode.com/problems/flatten-2d-vector/)| [Swift](./Design/Vector2D.swift)| Medium | O(n)| O(n)| [Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/)| [Swift](./Design/ImplementTrie.swift)| Medium | O(n)| O(n)| [Insert Delete GetRandom O(1)](https://leetcode.com/problems/insert-delete-getrandom-o1/)| [Swift](./Design/InsertDeleteGetRandom.swift)| Medium| O(1)| O(n)| From fb1bbd5400a143c710849957876a124e4ac1e3bb Mon Sep 17 00:00:00 2001 From: Subhodip Banerjee Date: Mon, 24 Feb 2020 06:52:33 +0530 Subject: [PATCH 03/75] [Code updated] As requested. --- String/LicenseKeyFormatting.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/String/LicenseKeyFormatting.swift b/String/LicenseKeyFormatting.swift index 62a09706..d7502269 100644 --- a/String/LicenseKeyFormatting.swift +++ b/String/LicenseKeyFormatting.swift @@ -6,7 +6,7 @@ * */ -class Solution { +class LicenseKeyFormatting { func licenseKeyFormatting(_ S: String, _ K: Int) -> String { guard S.count > 0 && K > 0 else{ return "" From e1c5966437bc0a3f26155a1a0b540857ca1f2574 Mon Sep 17 00:00:00 2001 From: sunset wan Date: Thu, 9 Apr 2020 15:19:02 +0800 Subject: [PATCH 04/75] Fix `Fatal error: Index out of range` issue when matrix is empty --- Array/NumMatrix.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Array/NumMatrix.swift b/Array/NumMatrix.swift index 66bec545..c06f637f 100644 --- a/Array/NumMatrix.swift +++ b/Array/NumMatrix.swift @@ -7,9 +7,18 @@ class NumMatrix { fileprivate var sum: [[Int]] + fileprivate var m: Int + fileprivate var n: Int init(_ matrix: [[Int]]) { - let m = matrix.count, n = matrix[0].count + m = matrix.count + + if m == 0 { + n = 0 + } else { + n = matrix[0].count + } + sum = Array(repeating: Array(repeating: 0, count: n), count: m) for i in 0.. Date: Tue, 28 Apr 2020 15:01:28 +0800 Subject: [PATCH 05/75] Fix typo --- String/ValidPalindrome.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/String/ValidPalindrome.swift b/String/ValidPalindrome.swift index e6c3c90a..07c64474 100644 --- a/String/ValidPalindrome.swift +++ b/String/ValidPalindrome.swift @@ -33,7 +33,7 @@ class ValidPalindrome { } extension Character { - var isValid: Bool { + var isAlphanumeric: Bool { return isLetter || isNumber } } \ No newline at end of file From babc029aceb19945c31d839bc13c4a7e4830a6bc Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Sat, 13 Aug 2022 16:24:42 -0700 Subject: [PATCH 06/75] Refactor code to offer better performance and readability --- DFS/WordSearchII.swift | 157 ++++++++++++++--------------------------- Sort/SortColors.swift | 30 ++++---- 2 files changed, 68 insertions(+), 119 deletions(-) diff --git a/DFS/WordSearchII.swift b/DFS/WordSearchII.swift index 0e6f6ba8..7209ef94 100644 --- a/DFS/WordSearchII.swift +++ b/DFS/WordSearchII.swift @@ -11,131 +11,78 @@ class WordSearchII { func findWords(_ board: [[Character]], _ words: [String]) -> [String] { - var res = [String]() - - let m = board.count - let n = board[0].count - - let trie = _convertToTrie(words) - var visited = [[Bool]](repeating: Array(repeating: false, count: n), count: m) - - for i in 0 ..< m { - for j in 0 ..< n { - _dfs(board, m, n, i, j, &visited, &res, trie, "") + let trie = Trie(words), m = board.count, n = board[0].count + var isVisited = Array(repeating: Array(repeating: false, count: n), count: m), res = Set() + + for i in 0.., _ i: Int, _ j: Int, _ isVisited: inout [[Bool]], _ currentNode: TrieNode, _ currentStr: String, _ m: Int, _ n: Int) { guard i >= 0 && i < m && j >= 0 && j < n else { return } - - // check visited - guard !visited[i][j] else { + + guard !isVisited[i][j] else { return } - - // check is word prefix - let str = str + "\(board[i][j])" - guard trie.isWordPrefix(str) else { + + guard let child = currentNode.children[board[i][j]] else { return } - - // check word exist - if trie.isWord(str) && !res.contains(str) { - res.append(str) + + isVisited[i][j] = true + + let str = currentStr + "\(board[i][j])" + + if child.isEnd { + res.insert(str) } - - // check four directions - visited[i][j] = true - _dfs(board, m, n, i + 1, j, &visited, &res, trie, str) - _dfs(board, m, n, i - 1, j, &visited, &res, trie, str) - _dfs(board, m, n, i, j + 1, &visited, &res, trie, str) - _dfs(board, m, n, i, j - 1, &visited, &res, trie, str) - visited[i][j] = false + + search(board, trie, &res, i + 1, j, &isVisited, child, str, m, n) + search(board, trie, &res, i - 1, j, &isVisited, child, str, m, n) + search(board, trie, &res, i, j + 1, &isVisited, child, str, m, n) + search(board, trie, &res, i, j - 1, &isVisited, child, str, m, n) + + isVisited[i][j] = false } - func _convertToTrie(_ words: [String]) -> Trie { - let trie = Trie() - - for str in words { - trie.insert(str) + class Trie { + var root: TrieNode + + init(_ words: [String]) { + root = TrieNode() + + words.forEach { insert($0) } } - - return trie - } -} - - -class Trie { - var root: TrieNode - - init() { - root = TrieNode() - } - - func insert(_ word: String) { - var node = root - var word = [Character](word.characters) - - for i in 0 ..< word.count { - let c = word[i] - - if node.children[c] == nil { - node.children[c] = TrieNode() + + private func insert(_ word: String) { + var node = root + + for char in word { + if node.children[char] == nil { + node.children[char] = TrieNode() + } + + node = node.children[char]! } - - node = node.children[c]! + + node.isEnd = true } - - node.isEnd = true } - - func isWord(_ word: String) -> Bool { - var node = root - var word = [Character](word.characters) - - for i in 0 ..< word.count { - let c = word[i] - - if node.children[c] == nil { - return false - } - - node = node.children[c]! - } - return node.isEnd - } + class TrieNode { + var isEnd: Bool + var children: [Character: TrieNode] - func isWordPrefix(_ prefix: String) -> Bool { - var node = root - var prefix = [Character](prefix.characters) - - for i in 0 ..< prefix.count { - let c = prefix[i] - - if node.children[c] == nil { - return false - } - - node = node.children[c]! + init() { + isEnd = false + children = [Character: TrieNode]() } - - return true } } - -class TrieNode { - var isEnd: Bool - var children: [Character:TrieNode] - - init() { - isEnd = false - children = [Character:TrieNode]() - } -} \ No newline at end of file diff --git a/Sort/SortColors.swift b/Sort/SortColors.swift index 89beee51..cf2ec298 100644 --- a/Sort/SortColors.swift +++ b/Sort/SortColors.swift @@ -6,23 +6,25 @@ class SortColors { func sortColors(_ nums: inout [Int]) { - var red = 0, blue = nums.count - 1, i = 0 + var redIdx = 0, blueIdx = nums.count - 1, currentIdx = 0 - while i <= blue { - if nums[i] == 0 { - _swap(&nums, i, red) - red += 1 - i += 1 - } else if nums[i] == 1 { - i += 1 - } else { - _swap(&nums, i, blue) - blue -= 1 + while currentIdx <= blueIdx { + let num = nums[currentIdx] + + if num == 0 { + swap(&nums, redIdx, currentIdx) + redIdx += 1 + } else if num == 2 { + swap(&nums, currentIdx, blueIdx) + blueIdx -= 1 + currentIdx -= 1 } + + currentIdx += 1 } } - fileprivate func _swap(_ nums: inout [T], _ p: Int, _ q: Int) { - (nums[p], nums[q]) = (nums[q], nums[p]) + private func swap(_ nums: inout [Int], _ left: Int, _ right: Int) { + (nums[left], nums[right]) = (nums[right], nums[left]) } -} \ No newline at end of file +} From eefe4bc55f76359a5611f6b9d9b92da6c77a2315 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Sun, 14 Aug 2022 19:40:05 -0700 Subject: [PATCH 07/75] Refactor code structure to make solutions more readable --- BFS/WordLadder.swift | 58 +++++++++++++++++++++++-------------------- DP/EditDistance.swift | 25 ++++++++----------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/BFS/WordLadder.swift b/BFS/WordLadder.swift index b4f296d9..3346c53c 100644 --- a/BFS/WordLadder.swift +++ b/BFS/WordLadder.swift @@ -10,41 +10,45 @@ class WordLadder { func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { - guard beginWord.count == endWord.count else { - return 0 - } - - var queue = [(beginWord, 1)], wordSet = Set(wordList) + var wordSet = Set(wordList), wordStepQueue = [(beginWord, 1)] - while !queue.isEmpty { - let (word, step) = queue.removeFirst() + while !wordStepQueue.isEmpty { + let (currentWord, currentStep) = wordStepQueue.removeFirst() - if word == endWord { - return step + if currentWord == endWord { + return currentStep } - // transform word - for i in 0..) -> [String] { + var res = [String]() + + // change character at every offset of the word + for i in 0.. Int { - let aChars = [Character](word1.characters) - let bChars = [Character](word2.characters) - let aLen = aChars.count - let bLen = bChars.count + let word1Chars = Array(word1), word2Chars = Array(word2), m = word1.count, n = word2.count + var distances = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1) - var dp = Array(count: aLen + 1, repeatedValue:(Array(count: bLen + 1, repeatedValue: 0))) - - for i in 0...aLen { - for j in 0...bLen { + for i in 0...m { + for j in 0...n { if i == 0 { - dp[i][j] = j + distances[i][j] = j } else if j == 0 { - dp[i][j] = i - } else if aChars[i - 1] == bChars[j - 1] { - dp[i][j] = dp[i - 1][j - 1] + distances[i][j] = i + } else if word1Chars[i - 1] == word2Chars[j - 1] { + distances[i][j] = distances[i - 1][j - 1] } else { - dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1 + distances[i][j] = min(distances[i - 1][j - 1], distances[i - 1][j], distances[i][j - 1]) + 1 } + } } - return dp[aLen][bLen] + return distances[m][n] } } \ No newline at end of file From 0b5b5ee0e09665719536554b35631729785a1034 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Sun, 14 Aug 2022 20:52:18 -0700 Subject: [PATCH 08/75] Refactor code style to the latest Swift version and fix time & space complexity analysis --- DFS/Combinations.swift | 19 ++++++++----------- DFS/Subsets.swift | 19 +++++++------------ DFS/SubsetsII.swift | 18 ++++++++---------- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/DFS/Combinations.swift b/DFS/Combinations.swift index a8b17aee..93d076a6 100644 --- a/DFS/Combinations.swift +++ b/DFS/Combinations.swift @@ -2,30 +2,27 @@ * Question Link: https://leetcode.com/problems/combinations/ * Primary idea: Classic Depth-first Search, another version of Subsets * - * Time Complexity: O(n^n), Space Complexity: O(n) + * Time Complexity: O(n * 2^n), Space Complexity: O(n * 2^n) * */ class Combinations { - func combine(n: Int, _ k: Int) -> [[Int]] { - var res = [[Int]]() - var path = [Int]() - let nums = [Int](1...n) + func combine(_ n: Int, _ k: Int) -> [[Int]] { + var res = [[Int]](), path = [Int]() - _dfs(nums, &res, &path, 0, k) + dfs(&res, &path, 0, Array(1...n), k) return res } - private func _dfs(nums: [Int], inout _ res: [[Int]], inout _ path: [Int], _ index: Int, _ k: Int) { + private func dfs(_ res: inout [[Int]], _ path: inout [Int], _ idx: Int, _ nums: [Int], _ k: Int) { if path.count == k { - res.append([Int](path)) - return + res.append(path) } - for i in index.. [[Int]] { - var res = [[Int]]() - var path = [Int]() + func subsets(_ nums: [Int]) -> [[Int]] { + var res = [[Int]](), path = [Int]() - let nums = nums.sorted(by: <) - - _dfs(&res, &path, nums, 0) + dfs(&res, &path, 0, nums) return res } - private func _dfs(inout res: [[Int]], inout _ path: [Int], _ nums: [Int], _ index: Int) { - // termination case - + private func dfs(_ res: inout [[Int]], _ path: inout [Int], _ idx: Int, _ nums: [Int]) { res.append(path) - for i in index.. [[Int]] { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { var res = [[Int]](), path = [Int]() - let nums = nums.sorted(by: <) - - _dfs(&res, &path, nums, 0) + dfs(&res, &path, 0, nums.sorted()) return res } - private func _dfs(inout res: [[Int]], inout _ path:[Int], _ nums: [Int], _ index: Int) { + private func dfs(_ res: inout [[Int]], _ path: inout [Int], _ idx: Int, _ nums: [Int]) { res.append(path) - for i in index.. 0 && nums[i] == nums[i - 1] && i != index { - continue + for i in idx.. 0 && nums[i] == nums[i - 1] && i != idx { + continue } path.append(nums[i]) - _dfs(&res, &path, nums, i + 1) + dfs(&res, &path, i + 1, nums) path.removeLast() } } From cd52319974991f737cbcbeb268c853906558aee9 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Sun, 14 Aug 2022 21:42:19 -0700 Subject: [PATCH 09/75] Refactor code style to Permutations II --- DFS/PermutationsII.swift | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/DFS/PermutationsII.swift b/DFS/PermutationsII.swift index 800e88be..f2747052 100644 --- a/DFS/PermutationsII.swift +++ b/DFS/PermutationsII.swift @@ -10,29 +10,26 @@ class PermutationsII { func permuteUnique(_ nums: [Int]) -> [[Int]] { var res = [[Int]](), path = [Int](), visited = [Bool](repeating: false, count: nums.count) - let nums = nums.sorted(by: <) - - _dfs(&res, &path, nums, &visited) + dfs(&res, &path, &visited, nums.sorted()) return res } - private func _dfs(inout res: [[Int]], inout _ path: [Int], _ nums: [Int], inout _ visited: [Bool]) { - // termination case + private func dfs(_ res: inout [[Int]], _ path: inout [Int], _ isVisited: inout [Bool], _ nums: [Int]) { if path.count == nums.count { res.append(path) return } - for i in 0.. 0 && nums[i] == nums[i - 1] && visited[i - 1]) { - continue + for (i, num) in nums.enumerated() where !isVisited[i] { + if i > 0 && nums[i] == nums[i - 1] && !isVisited[i - 1] { + continue } - path.append(nums[i]) - visited[i] = true - _dfs(&res, &path, nums, &visited) - visited[i] = false + path.append(num) + isVisited[i] = true + dfs(&res, &path, &isVisited, nums) + isVisited[i] = false path.removeLast() } } From b3b7f0321f65d872bc7edadaa0dda30cff7b90ba Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Tue, 16 Aug 2022 20:29:15 -0700 Subject: [PATCH 10/75] Fix the time and space complexity --- DFS/CombinationSum.swift | 2 +- DFS/CombinationSumII.swift | 2 +- DFS/CombinationSumIII.swift | 2 +- DFS/Combinations.swift | 2 +- README.md | 14 +++++++------- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/DFS/CombinationSum.swift b/DFS/CombinationSum.swift index 50933144..ac076d1b 100644 --- a/DFS/CombinationSum.swift +++ b/DFS/CombinationSum.swift @@ -2,7 +2,7 @@ * Question Link: https://leetcode.com/problems/combination-sum/ * Primary idea: Classic Depth-first Search * - * Time Complexity: O(n^n), Space Complexity: O(2^n - 1) + * Time Complexity: O(2^n), Space Complexity: O(n) * */ diff --git a/DFS/CombinationSumII.swift b/DFS/CombinationSumII.swift index 2e16a537..9372fd9b 100644 --- a/DFS/CombinationSumII.swift +++ b/DFS/CombinationSumII.swift @@ -2,7 +2,7 @@ * Question Link: https://leetcode.com/problems/combination-sum-ii/ * Primary idea: Classic Depth-first Search * - * Time Complexity: O(n^n), Space Complexity: O(2^n - 2) + * Time Complexity: O(2^n), Space Complexity: O(n) * */ diff --git a/DFS/CombinationSumIII.swift b/DFS/CombinationSumIII.swift index 1d411e09..ec1d840c 100644 --- a/DFS/CombinationSumIII.swift +++ b/DFS/CombinationSumIII.swift @@ -2,7 +2,7 @@ * Question Link: https://leetcode.com/problems/combination-sum-iii/ * Primary idea: Classic Depth-first Search * - * Time Complexity: O(n^n), Space Complexity: O(nCk) + * Time Complexity: O(2^n), Space Complexity: O(n) * */ diff --git a/DFS/Combinations.swift b/DFS/Combinations.swift index 93d076a6..4e01de52 100644 --- a/DFS/Combinations.swift +++ b/DFS/Combinations.swift @@ -2,7 +2,7 @@ * Question Link: https://leetcode.com/problems/combinations/ * Primary idea: Classic Depth-first Search, another version of Subsets * - * Time Complexity: O(n * 2^n), Space Complexity: O(n * 2^n) + * Time Complexity: O(2^n), Space Complexity: O(n) * */ diff --git a/README.md b/README.md index 97a0cc4c..c83a7d8a 100644 --- a/README.md +++ b/README.md @@ -271,14 +271,14 @@ ## Depth-first search | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | -[Permutations](https://leetcode.com/problems/permutations/)| [Swift](./DFS/Permutations.swift)| Medium| O(n^n)| O(n)| -[Permutations II](https://leetcode.com/problems/permutations-ii/)| [Swift](./DFS/PermutationsII.swift)| Medium| O(n^n)| O(n)| +[Permutations](https://leetcode.com/problems/permutations/)| [Swift](./DFS/Permutations.swift)| Medium| O(2^n)| O(n)| +[Permutations II](https://leetcode.com/problems/permutations-ii/)| [Swift](./DFS/PermutationsII.swift)| Medium| O(2^n)| O(n)| [Subsets](https://leetcode.com/problems/subsets/)| [Swift](./DFS/Subsets.swift)| Medium| O(n^n)| O(n)| -[Subsets II](https://leetcode.com/problems/subsets-ii/)| [Swift](./DFS/SubsetsII.swift)| Medium| O(n^n)| O(n)| -[Combinations](https://leetcode.com/problems/combinations/)| [Swift](./DFS/Combinations.swift)| Medium| O(n^n)| O(n)| -[Combination Sum](https://leetcode.com/problems/combination-sum/)| [Swift](./DFS/CombinationSum.swift)| Medium| O(n^n)| O(2^n - 1)| -[Combination Sum II](https://leetcode.com/problems/combination-sum-ii/)| [Swift](./DFS/CombinationSumII.swift)| Medium| O(n^n)| O(2^n - 2)| -[Combination Sum III](https://leetcode.com/problems/combination-sum-iii/)| [Swift](./DFS/CombinationSumIII.swift)| Medium| O(n^n)| O(nCk)| +[Subsets II](https://leetcode.com/problems/subsets-ii/)| [Swift](./DFS/SubsetsII.swift)| Medium| O(2^n)| O(n)| +[Combinations](https://leetcode.com/problems/combinations/)| [Swift](./DFS/Combinations.swift)| Medium| O(2^n)| O(n)| +[Combination Sum](https://leetcode.com/problems/combination-sum/)| [Swift](./DFS/CombinationSum.swift)| Medium| O(2^n)| O(n)| +[Combination Sum II](https://leetcode.com/problems/combination-sum-ii/)| [Swift](./DFS/CombinationSumII.swift)| Medium| O(2^n)| O(n)| +[Combination Sum III](https://leetcode.com/problems/combination-sum-iii/)| [Swift](./DFS/CombinationSumIII.swift)| Medium| O(2^n)| O(n)| [Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)| [Swift](./DFS/LetterCombinationsPhoneNumber.swift)| Medium| O(4^n)| O(n)| [Factor Combinations](https://leetcode.com/problems/factor-combinations/)| [Swift](./DFS/FactorCombinations.swift)| Medium| O(n^n))| O(2^n - 1)| [Strobogrammatic Number II](https://leetcode.com/problems/strobogrammatic-number-ii/)| [Swift](./DFS/StrobogrammaticNumberII.swift)| Medium| O(m^n)| O(n)| From 11d07a956aca63985e00ed2a60484613e21e2dc7 Mon Sep 17 00:00:00 2001 From: Igor Sorokin Date: Fri, 19 Aug 2022 11:41:37 +0300 Subject: [PATCH 11/75] Fix Binary Tree Paths titles and links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c83a7d8a..765333cb 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ [Path Sum](https://leetcode.com/problems/path-sum/)| [Swift](./Tree/PathSum.swift)| Easy| O(n)| O(n)| [Path Sum II](https://leetcode.com/problems/path-sum-ii/)| [Swift](./Tree/PathSumII.swift)| Medium| O(n)| O(n)| [Path Sum III](https://leetcode.com/problems/path-sum-iii/)| [Swift](./Tree/PathSumIII.swift)| Easy| O(n^2)| O(1)| -[Bnary Tree Paths](https://leetcode.com/problems/binary-tree-paths/)| [Swift](./Tree/BnaryTreePaths.swift)| Easy| O(n)| O(n)| +[Binary Tree Paths](https://leetcode.com/problems/binary-tree-paths/)| [Swift](./Tree/BinaryTreePaths.swift)| Easy| O(n)| O(n)| [Binary Tree Maximum Path Sum](https://leetcode.com/problems/binary-tree-maximum-path-sum/)| [Swift](./Tree/BinaryTreeMaximumPathSum.swift)| Hard| O(n)| O(1)| [House Robber III](https://leetcode.com/problems/house-robber-iii/)| [Swift](./Tree/HouseRobberIII.swift)| Medium| O(n)| O(1)| [Unique Binary Search Trees](https://leetcode.com/problems/unique-binary-search-trees/)| [Swift](./Tree/UniqueBinarySearchTrees.swift)| Medium| O(n^2)| O(n)| @@ -441,7 +441,7 @@ [Remove Invalid Parentheses](https://leetcode.com/problems/remove-invalid-parentheses/)| [Swift](./DFS/RemoveInvalidParentheses.swift)| Hard| ★★★★★★| [Add Binary](https://leetcode.com/problems/add-binary/)| [Swift](./Math/AddBinary.swift)| Easy| ★★★★★| [Two Sum](https://leetcode.com/problems/two-sum/)| [Swift](./Array/TwoSum.swift)| Easy| ★★★★★| -[Bnary Tree Paths](https://leetcode.com/problems/binary-tree-paths/)| [Swift](./Tree/BnaryTreePaths.swift)| Easy| ★★★★| +[Binary Tree Paths](https://leetcode.com/problems/binary-tree-paths/)| [Swift](./Tree/BinaryTreePaths.swift)| Easy| ★★★★| [Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)| [Swift](./DFS/LetterCombinationsPhoneNumber.swift)| Medium| ★★★★| [Merge k Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/)| [Swift](./LinkedList/MergeKSortedLists.swift)| Hard| ★★★★| [Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/)| [Swift](./LinkedList/ReverseLinkedList.swift)| Easy| ★★★| @@ -661,7 +661,7 @@ | | 260 | [Single Number III](https://leetcode.com/problems/single-number-iii/) | Medium | | | 259 | [3Sum Smaller](https://leetcode.com/problems/3sum-smaller/) ♥ | Medium | | [Swift](./Math/AddDigits.swift) | 258 | [Add Digits](https://leetcode.com/problems/add-digits/) | Easy | -| [Swift](./Tree/BnaryTreePaths.swift) | 257 | [Binary Tree Paths](https://leetcode.com/problems/binary-tree-paths/) | Easy | +| [Swift](./Tree/BinaryTreePaths.swift) | 257 | [Binary Tree Paths](https://leetcode.com/problems/binary-tree-paths/) | Easy | | [Swift](./DP/PaintHouse.swift) | 256 | [Paint House](https://leetcode.com/problems/paint-house/) ♥ | Medium | | | 255 | [Verify Preorder Sequence in Binary Search Tree](https://leetcode.com/problems/verify-preorder-sequence-in-binary-search-tree/) ♥ | Medium | | [Swift](./DFS/FactorCombinations.swift) | 254 | [Factor Combinations](https://leetcode.com/problems/factor-combinations/) ♥ | Medium | From 23f48fb9ad07864f634d0119619ef32c9c9e1c81 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Sat, 20 Aug 2022 20:55:40 -0700 Subject: [PATCH 12/75] Add a solution to Pour Water --- Math/PourWater.swift | 50 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 5 +++-- 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 Math/PourWater.swift diff --git a/Math/PourWater.swift b/Math/PourWater.swift new file mode 100644 index 00000000..70967aa0 --- /dev/null +++ b/Math/PourWater.swift @@ -0,0 +1,50 @@ +/** + * Question Link: https://leetcode.com/problems/pour-water/ + * Primary idea: One pointer. Go left and then right to update the drop pointer. + * Time Complexity: O(nk), Space Complexity: O(1) + * + */ + + class PourWater { + func pourWater(_ heights: [Int], _ volume: Int, _ k: Int) -> [Int] { + var result = heights, idx = k, dropIdx = k + + for _ in 0..= 0 { + if result[idx] > result[dropIdx] { + break + } + + if result[idx] < result[dropIdx] { + dropIdx = idx + } + + idx -= 1 + } + + // check right + if dropIdx == k { + idx = k + 1 + while idx < result.count { + if result[idx] > result[dropIdx] { + break + } + + if result[idx] < result[dropIdx] { + dropIdx = idx + } + + idx += 1 + } + } + + result[dropIdx] += 1 + } + + return result + } +} \ No newline at end of file diff --git a/README.md b/README.md index c83a7d8a..b0b410d5 100644 --- a/README.md +++ b/README.md @@ -340,7 +340,8 @@ [Sparse Matrix Multiplication](https://leetcode.com/problems/sparse-matrix-multiplication/)| [Swift](./Math/SparseMatrixMultiplication.swift)| Medium| O(n^3)| O(n^2)| [Rectangle Area](https://leetcode.com/problems/rectangle-area/)| [Swift](./Math/RectangleArea.swift)| Easy| O(1)| O(1)| [Minimum Moves to Equal Array Elements](https://leetcode.com/problems/minimum-moves-to-equal-array-elements/)| [Swift](./Math/MinimumMovesEqualArrayElements.swift)| Easy| O(n)| O(1)| -[Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/)| [Swift](./Math/TrappingRainWater.swift)| Hard| O(n)| O(n)| +[Pour Water](https://leetcode.com/problems/pour-water/)| [Swift](./Math/TrappingRainWater.swift)| Hard| O(n)| O(n)| +[Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/)| [Swift](./Math/TrappingRainWater.swift)| Medium| O(nk)| O(1)| [Container With Most Water](https://leetcode.com/problems/container-with-most-water/)| [Swift](./Math/ContainerMostWater.swift)| Medium| O(n)| O(1)| [Counting Bits](https://leetcode.com/problems/counting-bits/)| [Swift](./Math/CountingBits.swift)| Medium| O(n)| O(n)| [K-th Smallest in Lexicographical Order](https://leetcode.com/problems/k-th-smallest-in-lexicographical-order/)| [Swift](./Math/KthSmallestLexicographicalOrder.swift)| Hard| O(n)| O(1)| @@ -683,7 +684,7 @@ | [Swift](./Array/ProductExceptSelf.swift) | 238 | [Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/) | Medium | | | 237 | [Delete Node in a Linked List](https://leetcode.com/problems/delete-node-in-a-linked-list/) | Easy | | | 236 | [Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/) | Medium | -| | 235 | [Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/) | Easy | +| [Swift](./Tree/LowestCommonAncestorBinarySearchTree.swift) | 235 | [Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/) | Easy | | [Swift](./LinkedList/PalindromeLinkedList.swift) | 234 | [Palindrome Linked List](https://leetcode.com/problems/palindrome-linked-list/) | Easy | | | 233 | [Number of Digit One](https://leetcode.com/problems/number-of-digit-one/) | Hard | | [Swift](./Queue/ImplementQueueUsingStacks.swift) | 232 | [Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks/) | Easy | From e23019e613caca224ff00e85308af3c6e95271f8 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Sat, 20 Aug 2022 21:10:10 -0700 Subject: [PATCH 13/75] Fix typo for zigzag conversion file name --- String/{ZigZagConverstion.swift => ZigzagConversion.swift} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename String/{ZigZagConverstion.swift => ZigzagConversion.swift} (97%) diff --git a/String/ZigZagConverstion.swift b/String/ZigzagConversion.swift similarity index 97% rename from String/ZigZagConverstion.swift rename to String/ZigzagConversion.swift index 3503aba5..cbde9d45 100644 --- a/String/ZigZagConverstion.swift +++ b/String/ZigzagConversion.swift @@ -8,7 +8,7 @@ * */ -class Solution { +class ZigzagConversion { func convert(s: String, _ numRows: Int) -> String { if numRows == 1 { return s From 9c24227096cccf5a45d1f41f0f8e11a5a9baf8a0 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Sun, 21 Aug 2022 15:14:11 -0700 Subject: [PATCH 14/75] Update the solution to the Valid Sudoku --- Array/ValidSudoku.swift | 80 +++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 52 deletions(-) diff --git a/Array/ValidSudoku.swift b/Array/ValidSudoku.swift index e6d6c9d3..13c5bfd7 100644 --- a/Array/ValidSudoku.swift +++ b/Array/ValidSudoku.swift @@ -6,55 +6,35 @@ */ class ValidSudoku { - func isValidSudoku(_ board: [[Character]]) -> Bool { - return areRowsValid(board) && areColsValid(board) && areSubsquaresValid(board) - } - - private func areRowsValid(_ board: [[Character]]) -> Bool { - var existingDigits = Set() - - for i in 0.. Bool { + let len = 9 - return true - } - - private func areColsValid(_ board: [[Character]]) -> Bool { - var existingDigits = Set() + var rowSet = Array(repeating: Set(), count: len) + var colSet = Array(repeating: Set(), count: len) + var boxSet = Array(repeating: Set(), count: len) - for i in 0.. Bool { - var existingDigits = Set() - - for i in stride(from: 0, to: board.count, by: 3) { - for j in stride(from: 0, to: board[0].count, by: 3) { - existingDigits.removeAll() - for m in i..) -> Bool { - if digit == "." { - return true - } - - if set.contains(digit) { + private func isValid(_ set: inout Set, _ char: Character) -> Bool { + if set.contains(char) { return false } else { - set.insert(digit) + set.insert(char) return true } } From fddd5a57597b14a0873ede55f284dbecccfb7594 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Sun, 21 Aug 2022 15:14:48 -0700 Subject: [PATCH 15/75] Update the solution to the Sudoku Solver --- DFS/SudokuSolver.swift | 66 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/DFS/SudokuSolver.swift b/DFS/SudokuSolver.swift index 3d969fc1..c7a8cd29 100644 --- a/DFS/SudokuSolver.swift +++ b/DFS/SudokuSolver.swift @@ -3,34 +3,34 @@ * Primary idea: Iterate through the whole matrix, try to fill out empty space with all * possible cases and check the vaildity * - * Time Complexity: O(n^4), Space Complexity: O(1) + * Time Complexity: O((9!) ^ 9), Space Complexity: O(1) */ - class SudokuSolver { + +class SudokuSolver { + private let length = 9 + func solveSudoku(_ board: inout [[Character]]) { - guard board.count != 0 || board[0].count != 0 else { - return - } - helper(&board) + dfs(&board) } - private func helper(_ board: inout [[Character]]) -> Bool { - let m = board.count, n = board[0].count - - for i in 0.. Bool { + let candidates = "123456789" + + for i in 0.. Bool { - let m = board.count, n = board[0].count - - // check row - for x in 0.. Date: Mon, 22 Aug 2022 20:20:02 -0700 Subject: [PATCH 16/75] Add a solution to the Longest Common Subsequence --- DP/LongestCommonSubsequence.swift | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 DP/LongestCommonSubsequence.swift diff --git a/DP/LongestCommonSubsequence.swift b/DP/LongestCommonSubsequence.swift new file mode 100644 index 00000000..870775e5 --- /dev/null +++ b/DP/LongestCommonSubsequence.swift @@ -0,0 +1,26 @@ +/** + * Question Link: https://leetcode.com/problems/longest-common-subsequence/ + * Primary idea: Dynamic Programming, dp[i][j] = dp[i - 1][j - 1] + 1 or max(dp[i - 1][j], dp[i][j - 1]) + * + * Time Complexity: O(mn), Space Complexity: O(1) + */ + +class LongestCommonSubsequence { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + let text1Chars = Array(text1), text2Chars = Array(text2) + let m = text1.count, n = text2.count + var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1) + + for i in 1...m { + for j in 1...n { + if text1Chars[i - 1] == text2Chars[j - 1] { + dp[i][j] = dp[i - 1][j - 1] + 1 + } else { + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + } + } + } + + return dp[m][n] + } +} \ No newline at end of file From 28ec17bce8661b7ceb7748949880d39e68b215e4 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Mon, 22 Aug 2022 20:24:04 -0700 Subject: [PATCH 17/75] Add a solution to the Longest Increasing Path in a Matrix --- DFS/LongestIncreasingPathMatrix.swift | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 DFS/LongestIncreasingPathMatrix.swift diff --git a/DFS/LongestIncreasingPathMatrix.swift b/DFS/LongestIncreasingPathMatrix.swift new file mode 100644 index 00000000..e137947b --- /dev/null +++ b/DFS/LongestIncreasingPathMatrix.swift @@ -0,0 +1,44 @@ +/** + * Question Link: https://leetcode.com/problems/longest-increasing-path-in-a-matrix/ + * Primary idea: Classic Depth-first Search, iterate four directions and update current local max value. + * + * Time Complexity: O(mn), Space Complexity: O(mn) + * + */ + +class LongestIncreasingPathMatrix { + func longestIncreasingPath(_ matrix: [[Int]]) -> Int { + let m = matrix.count, n = matrix[0].count + var res = 0, cache = Array(repeating: Array(repeating: -1, count: n), count: m) + + for i in 0.. Int { + + if cache[i][j] != -1 { + return cache[i][j] + } + + var longestLength = 1 + + for dir in [(1, 0), (-1, 0), (0, 1), (0, -1)] { + let x = i + dir.0, y = j + dir.1 + + if x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j] { + let candidate = dfs(matrix, x, y, m, n, &cache) + 1 + + longestLength = max(longestLength, candidate) + } + } + + cache[i][j] = longestLength + return cache[i][j] + } +} \ No newline at end of file From 5091d0ae78f8ae55824b41b3a108dbc8bb674766 Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Wed, 24 Aug 2022 12:19:50 -0700 Subject: [PATCH 18/75] Add a solution to Minimum Number of Steps to Make Two Strings Anagram --- String/MinimumStepsMakeTwoStringsAnagram.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 String/MinimumStepsMakeTwoStringsAnagram.swift diff --git a/String/MinimumStepsMakeTwoStringsAnagram.swift b/String/MinimumStepsMakeTwoStringsAnagram.swift new file mode 100644 index 00000000..842e788c --- /dev/null +++ b/String/MinimumStepsMakeTwoStringsAnagram.swift @@ -0,0 +1,17 @@ +/** + * Question Link: https://leetcode.com/problems/minimum-number-of-steps-to-make-two-strings-anagram/ + * Primary idea: Check character frequency in the original string and replace all redundant ones. + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class MinimumStepsMakeTwoStringsAnagram { + func minSteps(_ s: String, _ t: String) -> Int { + var tFreq = Dictionary(t.map { ($0, 1) }, uniquingKeysWith: +) + + for char in s { + tFreq[char, default: 0] -= 1 + } + + return tFreq.values.filter { $0 > 0 }.reduce(0) { $0 + $1 } + } +} \ No newline at end of file From f3c744b25297e924bfb8dc2653efd8a658818f4d Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Wed, 24 Aug 2022 12:20:22 -0700 Subject: [PATCH 19/75] Refactor code style for Minimum Number of Steps to Make Two Strings Anagram --- String/MinimumStepsMakeTwoStringsAnagram.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/String/MinimumStepsMakeTwoStringsAnagram.swift b/String/MinimumStepsMakeTwoStringsAnagram.swift index 842e788c..68ff5ede 100644 --- a/String/MinimumStepsMakeTwoStringsAnagram.swift +++ b/String/MinimumStepsMakeTwoStringsAnagram.swift @@ -14,4 +14,4 @@ class MinimumStepsMakeTwoStringsAnagram { return tFreq.values.filter { $0 > 0 }.reduce(0) { $0 + $1 } } -} \ No newline at end of file +} From 35c937c7f4d0f4776c5a0fe49465313dca96602b Mon Sep 17 00:00:00 2001 From: Yi Gu Date: Wed, 24 Aug 2022 12:26:58 -0700 Subject: [PATCH 20/75] Add a new solution to the Maximum Profit in Job Scheduling --- DP/MaximumProfitJobScheduling.swift | 73 +++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 DP/MaximumProfitJobScheduling.swift diff --git a/DP/MaximumProfitJobScheduling.swift b/DP/MaximumProfitJobScheduling.swift new file mode 100644 index 00000000..847e0202 --- /dev/null +++ b/DP/MaximumProfitJobScheduling.swift @@ -0,0 +1,73 @@ +/** + * Question Link: https://leetcode.com/problems/maximum-profit-in-job-scheduling/ + * Primary idea: Dynamic Programming, dp[i] means the max profit starting at i, find valid next job using binary search. + * Time Complexity: O(nlogn), Space Complexity: O(n) + */ + +class MaximumProfitJobScheduling { + func jobScheduling(_ startTime: [Int], _ endTime: [Int], _ profit: [Int]) -> Int { + let jobs = constructJobs(startTime, endTime, profit) + var maxProfits = Array(repeating: -1, count: jobs.count), maxProfit = Int.min + + for i in 0.. Int { + if index == jobs.count { + return 0 + } + + if maxProfits[index] != -1 { + return maxProfits[index] + } + + let nextIndex = findNextIndex(jobs, index) + + let maxProfit = max(findMaxProfit(jobs, index + 1, &maxProfits), jobs[index].profit + findMaxProfit(jobs, nextIndex, &maxProfits)) + + maxProfits[index] = maxProfit + + return maxProfit + } + + private func findNextIndex(_ jobs: [Job], _ index: Int) -> Int { + var left = index, right = jobs.count - 1 + var mid = 0 + + while left <= right { + mid = (right - left) / 2 + left + + // overlap + if jobs[index].endTime <= jobs[mid].startTime { + right = mid - 1 + } else { + left = mid + 1 + } + } + + return right + 1 + } + + + private func constructJobs(_ startTime: [Int], _ endTime: [Int], _ profit: [Int]) -> [Job] { + return (0.. Date: Fri, 26 Aug 2022 21:33:51 -0700 Subject: [PATCH 21/75] Update the solution to Alien Dictionary --- Graph/AlienDictionary.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Graph/AlienDictionary.swift b/Graph/AlienDictionary.swift index 0630d717..921ad025 100644 --- a/Graph/AlienDictionary.swift +++ b/Graph/AlienDictionary.swift @@ -38,7 +38,7 @@ class AlienDictionary { } private func initGraph(_ words: [String]) -> ([Character: Int], [Character: [Character]]) { - var indegrees = [Character: Int](), charToChars = [Character: [Character]]() + var indegrees = [Character: Int](), charToChars = [Character: [Character] // init indegress and charToChars words.forEach { word in @@ -51,15 +51,19 @@ class AlienDictionary { // refactor indegress and charToChars based on words for i in 0.. Date: Fri, 26 Aug 2022 22:10:13 -0700 Subject: [PATCH 22/75] Add a solution to the Cheapest Flights Within K Stops --- Graph/CheapestFlightsKStops.swift | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Graph/CheapestFlightsKStops.swift diff --git a/Graph/CheapestFlightsKStops.swift b/Graph/CheapestFlightsKStops.swift new file mode 100644 index 00000000..a1b7e49f --- /dev/null +++ b/Graph/CheapestFlightsKStops.swift @@ -0,0 +1,52 @@ +/** + * Question Link: https://leetcode.com/problems/cheapest-flights-within-k-stops/ + * Primary idea: Topological sort + BFS. + * Build a graph with each srouce and destination as an edge. Put the source as the head node for the queue, visit every possible destinaiton and update the result with the possible smallest value. + * + * Time Complexity: O(n * (n - 1) + K), Space Complexity: O(n * (n - 1)), + * n represents number of the cities + */ + +class CheapestFlightsKStops { + func findCheapestPrice(_ n: Int, _ flights: [[Int]], _ src: Int, _ dst: Int, _ k: Int) -> Int { + var srcDestinations = buildGraph(flights) + var queue = [(src, 0)], dstPrice = [Int: Int](), stopCount = 0 + + while !queue.isEmpty && stopCount < k + 1 { + + let currentQueueLen = queue.count + + for _ in 0.. (dst, price) + private func buildGraph(_ flights: [[Int]]) -> [Int: [(Int, Int)]] { + var srcDestinations = [Int: [(Int, Int)]]() + + for flight in flights { + srcDestinations[flight[0], default:[(Int, Int)]()].append((flight[1], flight[2])) + } + + return srcDestinations + } +} \ No newline at end of file From 4dd9e34aa1ae7861e2ec7026307b6f1b381f8c3f Mon Sep 17 00:00:00 2001 From: Soap Date: Sun, 28 Aug 2022 15:58:01 -0700 Subject: [PATCH 23/75] Add a solution to IP to CIDR --- Math/IPToCIDR.swift | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Math/IPToCIDR.swift diff --git a/Math/IPToCIDR.swift b/Math/IPToCIDR.swift new file mode 100644 index 00000000..ebfcffa6 --- /dev/null +++ b/Math/IPToCIDR.swift @@ -0,0 +1,69 @@ +** + * Question Link: https://leetcode.com/problems/ip-to-cidr/ + * Primary idea: Bit manipulation. Get the most right 1 and update steps based on it. + * + * Time Complexity: O(mlogn), Space Complexity: O(m) + * m is the length of the ip string + */ + +class IPToCIDR { + private let BASE = 256 + + func ipToCIDR(_ ip: String, _ n: Int) -> [String] { + var currentIPInt = ipToInt(ip) + var res = [String](), n = n + + while n > 0 { + // get the most right one bit + var step = currentIPInt & -currentIPInt + + if step == 0 { + step = Int(pow(Double(2), Double(32))) + } + + while step > n { + step /= 2 + } + + res.append(IntToIP(currentIPInt, step)) + + currentIPInt += step + n -= step + } + + return res + } + + private func ipToInt(_ ip: String) -> Int { + var x = 0 + let strings = ip.split(separator: ".") + + for str in strings { + x = x * BASE + Int(str)! + } + + return x + } + + private func IntToIP(_ x: Int, _ step: Int) -> String { + var res = Array(""), x = x + + for i in 0..<4 { + res.insert(contentsOf: String(x % BASE), at: 0) + if i != 3 { + res.insert(".", at: 0) + } + x /= BASE + } + + var len = 33, step = step + while step > 0 { + len -= 1 + step /= 2 + } + + res += "/\(len)" + + return String(res) + } +} From abc922aec2658aaba5dba47893af77283da7b4ef Mon Sep 17 00:00:00 2001 From: Soap Date: Tue, 30 Aug 2022 13:03:09 -0700 Subject: [PATCH 24/75] Add a solution to Sliding Puzzle --- BFS/SlidingPuzzle.swift | 81 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 BFS/SlidingPuzzle.swift diff --git a/BFS/SlidingPuzzle.swift b/BFS/SlidingPuzzle.swift new file mode 100644 index 00000000..0d53aeab --- /dev/null +++ b/BFS/SlidingPuzzle.swift @@ -0,0 +1,81 @@ +/** + * Question Link: https://leetcode.com/problems/sliding-puzzle/ + * Primary idea: BFS to go over 4 directions until the board meets the requirement. + * Please make sure to keep a global set to store visited board. + * + * Time Complexity: O(mn*(mn)!), Space Complexity: O(mn*(mn)!) + * m stands for row num, n stands for col num + * + */ + +class SlidingPuzzle { + private let m = 2 + private let n = 3 + private let expectedBoard = [[1,2,3],[4,5,0]] + + func slidingPuzzle(_ board: [[Int]]) -> Int { + // find start point + let startPoint = findStart(board) + var queue = [ZeroNode(i: startPoint.0, + j: startPoint.1, + board: board, + step: 0)] + + var isVisited = Set<[[Int]]>() + + while !queue.isEmpty { + let current = queue.removeFirst() + + if current.board == expectedBoard { + return current.step + } + + isVisited.insert(current.board) + + for dir in [(0, 1), (0, -1), (1, 0), (-1, 0)] { + let (x, y) = (current.i + dir.0, current.j + dir.1) + + guard x >= 0 && x < m && y >= 0 && y < n else { + continue + } + + var board = current.board + swap(&board, (x, y), (current.i, current.j)) + + if isVisited.contains(board) { + continue + } + + queue.append(ZeroNode(i: x, + j: y, + board: board, + step: current.step + 1)) + } + } + + return -1 + } + + private func swap(_ board: inout [[Int]], _ pointA: (Int, Int), _ pointB: (Int, Int)) { + (board[pointA.0][pointA.1], board[pointB.0][pointB.1]) = (board[pointB.0][pointB.1], board[pointA.0][pointA.1]) + } + + private func findStart(_ board: [[Int]]) -> (Int, Int) { + for i in 0.. Date: Wed, 31 Aug 2022 13:50:24 -0700 Subject: [PATCH 25/75] Add a solution to Subarrays with K Different Integers --- Array/SubarraysKDifferentIntegers.swift | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Array/SubarraysKDifferentIntegers.swift diff --git a/Array/SubarraysKDifferentIntegers.swift b/Array/SubarraysKDifferentIntegers.swift new file mode 100644 index 00000000..e003a600 --- /dev/null +++ b/Array/SubarraysKDifferentIntegers.swift @@ -0,0 +1,59 @@ +/** + * Question Link: https://leetcode.com/problems/subarrays-with-k-different-integers/ + * Primary idea: Sliding window. + * Assuming i1 < i2 < i3, (i1,j) and (i3,j) are valid, (i2,j) is definitely valid. + * However, assuming i3 < i4, (i4,j) is is only invalid when the distance between i4 and j is less than k. + * Using 2 sliding windows could solve the problem. + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class SubarraysKDifferentIntegers { + func subarraysWithKDistinct(_ nums: [Int], _ k: Int) -> Int { + var left1 = 0, left2 = 0, window1 = Window(), window2 = Window(), uniqueSubarrayCount = 0 + + for right in 0.. k { + window1.remove(nums[left1]) + left1 += 1 + } + + while window2.uniqueNum >= k { + window2.remove(nums[left2]) + left2 += 1 + } + + uniqueSubarrayCount += (left2 - left1) + } + + return uniqueSubarrayCount + } + + class Window { + var uniqueNum = 0 + private var numFreq = [Int: Int]() + + func add(_ num: Int) { + numFreq[num, default: 0] += 1 + if numFreq[num] == 1 { + uniqueNum += 1 + } + } + + func remove(_ num: Int) { + if let freq = numFreq[num] { + numFreq[num] = freq - 1 + + if freq == 1 { + uniqueNum -= 1 + } + } + } + } +} + From 4813fac2b531f1d504d6737981c70fd3602a97b5 Mon Sep 17 00:00:00 2001 From: Soap Date: Wed, 31 Aug 2022 13:55:06 -0700 Subject: [PATCH 26/75] Refactor the solution to the alien dictionary --- Graph/AlienDictionary.swift | 71 +++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/Graph/AlienDictionary.swift b/Graph/AlienDictionary.swift index 921ad025..41251259 100644 --- a/Graph/AlienDictionary.swift +++ b/Graph/AlienDictionary.swift @@ -9,72 +9,65 @@ class AlienDictionary { func alienOrder(_ words: [String]) -> String { - var res = "", queueChars = [Character]() - var (indegrees, charToChars) = initGraph(words) + var (inDegrees, toChars) = buildGraph(words) - indegrees.keys.filter { indegrees[$0] == 0 }.forEach { queueChars.append($0) } + var queue = inDegrees.keys.filter { inDegrees[$0] == 0 } + var res = "" - while !queueChars.isEmpty { - let char = queueChars.removeFirst() - res.append(char) + while !queue.isEmpty { + let char = queue.removeFirst() - guard let toChars = charToChars[char] else { - fatalError("Init Graph Error") - } + res.append(char) - for toChar in toChars { - guard let indegree = indegrees[toChar] else { - fatalError("Init Graph Error") - } + for nextChar in toChars[char]! { + inDegrees[nextChar]! -= 1 - indegrees[toChar] = indegree - 1 - if indegree == 1 { - queueChars.append(toChar) + if inDegrees[nextChar] == 0 { + queue.append(nextChar) } } } - return res.count == indegrees.count ? res : "" + return res.count == inDegrees.count ? res : "" } - private func initGraph(_ words: [String]) -> ([Character: Int], [Character: [Character]]) { - var indegrees = [Character: Int](), charToChars = [Character: [Character] + private func buildGraph(_ words: [String]) -> ([Character: Int], [Character: [Character]]) { + // init inDegrees and toChars + var inDegrees = [Character: Int](), toChars = [Character: [Character]]() - // init indegress and charToChars words.forEach { word in word.forEach { char in - indegrees[char] = 0 - charToChars[char] = [Character]() - } + inDegrees[char] = 0 + toChars[char] = [Character]() + } } - // refactor indegress and charToChars based on words + // update based on orders for i in 0.. Date: Sat, 3 Sep 2022 20:23:47 -0700 Subject: [PATCH 27/75] Update solutions to Regular Expression Matching and Wildcard Matching --- DP/RegularExpressionMatching.swift | 34 ++++++++++----------- DP/WildcardMatching.swift | 48 +++++++++++++----------------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/DP/RegularExpressionMatching.swift b/DP/RegularExpressionMatching.swift index bd2bc838..9de0c1ec 100644 --- a/DP/RegularExpressionMatching.swift +++ b/DP/RegularExpressionMatching.swift @@ -6,31 +6,29 @@ class RegularExpressionMatching { func isMatch(_ s: String, _ p: String) -> Bool { - let sChars = Array(s), pChars = Array(p) - var dp = Array(repeating: Array(repeating: false, count: pChars.count + 1), count: sChars.count + 1) - dp[0][0] = true + let s = Array(s), p = Array(p), m = s.count, n = p.count + var dp = Array(repeating: Array(repeating: false, count: n + 1), count: m + 1) - for i in 0...pChars.count { - // jump over "" vs. "x*" case - dp[0][i] = i == 0 || i > 1 && dp[0][i - 2] && pChars[i - 1] == "*" + for j in 0...n { + dp[0][j] = j == 0 || (j > 1 && dp[0][j - 2] && p[j - 1] == "*") } - for i in 0...sChars.count { - for j in 0...pChars.count { - guard j > 0 else { - continue - } - - let pCurrent = pChars[j - 1] - - if pCurrent != "*" { - dp[i][j] = i > 0 && dp[i - 1][j - 1] && (pCurrent == "." || pCurrent == sChars[i - 1]) + for i in 1...m { + for j in 1...n { + if p[j - 1] != "*" { + if p[j - 1] == s[i - 1] || p[j - 1] == "." { + dp[i][j] = dp[i - 1][j - 1] + } else { + dp[i][j] = false + } } else { - dp[i][j] = dp[i][j - 2] || i > 0 && j > 1 && (sChars[i - 1] == pChars[j - 2] || pChars[j - 2] == ".") && dp[i - 1][j] + if j > 1 { + dp[i][j] = dp[i][j - 2] || ((p[j - 2] == s[i - 1] || p[j - 2] == ".") && dp[i - 1][j]) + } } } } - return dp[sChars.count][pChars.count] + return dp[m][n] } } \ No newline at end of file diff --git a/DP/WildcardMatching.swift b/DP/WildcardMatching.swift index 2b0b1b1d..b2951836 100644 --- a/DP/WildcardMatching.swift +++ b/DP/WildcardMatching.swift @@ -6,39 +6,31 @@ class WildcardMatching { func isMatch(_ s: String, _ p: String) -> Bool { - let sChars = Array(s), pChars = Array(p) - var dp = Array(repeating: Array(repeating: false, count: p.count + 1), count: s.count + 1) - dp[0][0] = true + let s = Array(s), p = Array(p), m = s.count, n = p.count + var dp = Array(repeating: Array(repeating: false, count: n + 1), count: m + 1) - // must start from 0, to make range feasible and handle empty vs. * case - for i in 0...s.count { - for j in 0...p.count { - guard j > 0 else { - continue - } - - let pCurrent = pChars[j - 1] - - if pCurrent != "*" { - dp[i][j] = i > 0 && dp[i - 1][j - 1] && (pCurrent == sChars[i - 1] || pCurrent == "?") - } else { - // Two situations: - // (1) '*' is the first character in p; - // (2) For k>=0 and k<=i, there is some dp[k][j-1] being true; - // and '*' will match the rest sequence in s after index k; - var flag = false - for k in 0...i { - if dp[k][j - 1] { - flag = true - break - } + for j in 0...n { + dp[0][j] = j == 0 || (dp[0][j - 1] && p[j - 1] == "*") + } + + if m < 1 || n < 1 { + return dp[m][n] + } + + for i in 1...m { + for j in 1...n { + if p[j - 1] != "*" { + if p[j - 1] == s[i - 1] || p[j - 1] == "?" { + dp[i][j] = dp[i - 1][j - 1] + } else { + dp[i][j] = false } - - dp[i][j] = flag || j == 1 + } else { + dp[i][j] = dp[i][j - 1] || dp[i - 1][j] } } } - return dp[s.count][p.count] + return dp[m][n] } } \ No newline at end of file From 46478f621970f12404ea19d9865a16accb3c9c37 Mon Sep 17 00:00:00 2001 From: Soap Date: Mon, 5 Sep 2022 23:26:36 -0700 Subject: [PATCH 28/75] Add a solution to Minimum Remove to Make Valid Parentheses --- Array/MinimumRemoveMakeValidParentheses.swift | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Array/MinimumRemoveMakeValidParentheses.swift diff --git a/Array/MinimumRemoveMakeValidParentheses.swift b/Array/MinimumRemoveMakeValidParentheses.swift new file mode 100644 index 00000000..a7e16efc --- /dev/null +++ b/Array/MinimumRemoveMakeValidParentheses.swift @@ -0,0 +1,47 @@ +/** + * Question Link: https://leetcode.com/problems/minimum-remove-to-make-valid-parentheses/ + * Primary idea: Count the number of left brackets and append to result accordingly. Remove unnecessary left brackets from most right. + * Time Complexity: O(n), Space Complexity: O(1) + */ + +class MinimumRemoveMakeValidParentheses { + func minRemoveToMakeValid(_ s: String) -> String { + var leftCount = 0, res = Array("") + + for char in s { + if char == "(" { + leftCount += 1 + res.append(char) + } else if char == ")" { + if leftCount == 0 { + continue + } else { + leftCount -= 1 + res.append(char) + } + } else { + res.append(char) + } + } + + // remove unnecessary left bracket + if leftCount > 0 { + var i = res.count - 1 + + while i >= 0 { + if res[i] == "(" { + res.remove(at: i) + leftCount -= 1 + + if leftCount == 0 { + break + } + } + + i -= 1 + } + } + + return String(res) + } +} \ No newline at end of file From b1c13266262df8ac5d057085cfe0516658e14b80 Mon Sep 17 00:00:00 2001 From: Soap Date: Mon, 5 Sep 2022 23:27:24 -0700 Subject: [PATCH 29/75] Refactor code style --- Array/MinimumRemoveMakeValidParentheses.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Array/MinimumRemoveMakeValidParentheses.swift b/Array/MinimumRemoveMakeValidParentheses.swift index a7e16efc..230bace3 100644 --- a/Array/MinimumRemoveMakeValidParentheses.swift +++ b/Array/MinimumRemoveMakeValidParentheses.swift @@ -44,4 +44,4 @@ class MinimumRemoveMakeValidParentheses { return String(res) } -} \ No newline at end of file +} From f5c081cde7e8c550c19e2c0f30874c424d81dbb5 Mon Sep 17 00:00:00 2001 From: Soap Date: Wed, 7 Sep 2022 16:27:40 -0700 Subject: [PATCH 30/75] Add a solution to Step-By-Step Directions From a Binary Tree Node to Another --- Tree/StepByStepDirectionsBinaryTreeNode.swift | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Tree/StepByStepDirectionsBinaryTreeNode.swift diff --git a/Tree/StepByStepDirectionsBinaryTreeNode.swift b/Tree/StepByStepDirectionsBinaryTreeNode.swift new file mode 100644 index 00000000..93342af1 --- /dev/null +++ b/Tree/StepByStepDirectionsBinaryTreeNode.swift @@ -0,0 +1,67 @@ +/** + * Question Link: https://leetcode.com/problems/step-by-step-directions-from-a-binary-tree-node-to-another/ + * Primary idea: The shortest path should pass LCA. Find paths for two nodes and remove their longgest common prefix. + * Time Complexity: O(n), Space Complexity: O(n) + * + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init(_ val: Int) { + * self.val = val + * self.left = nil + * self.right = nil + * } + * } + */ + +class StepByStepDirectionsBinaryTreeNode { + func getDirections(_ root: TreeNode?, _ startValue: Int, _ destValue: Int) -> String { + guard let root = root else { + return "" + } + + let startPath = getPath(root, startValue) + let destPath = getPath(root, destValue) + let len = longestCommonPrefixLen(startPath, destPath) + + return String(repeating: "U", count: startPath.count - len) + destPath.dropFirst(len) + } + + private func longestCommonPrefixLen(_ s: String, _ d: String) -> Int { + var i = 0 + let s = Array(s), d = Array(d) + + while i < min(s.count, d.count) { + if s[i] != d[i] { + break + } + + i += 1 + } + + return i + } + + private func getPath(_ parent: TreeNode, _ val: Int) -> String { + var queue = [(parent, "")] + + while !queue.isEmpty { + let current = queue.removeFirst() + + if current.0.val == val { + return current.1 + } + + if let left = current.0.left { + queue.append((left, current.1 + "L")) + } + if let right = current.0.right { + queue.append((right, current.1 + "R")) + } + } + + return "" + } +} From 4153136c5628d0db5cb0161bf0b2038ec6f40cda Mon Sep 17 00:00:00 2001 From: Soap Date: Fri, 9 Sep 2022 11:54:56 -0700 Subject: [PATCH 31/75] Add a solution to Maximum Split of Positive Even Integers --- Math/MaximumSplitPositiveEvenIntegers.swift | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Math/MaximumSplitPositiveEvenIntegers.swift diff --git a/Math/MaximumSplitPositiveEvenIntegers.swift b/Math/MaximumSplitPositiveEvenIntegers.swift new file mode 100644 index 00000000..fcd7f338 --- /dev/null +++ b/Math/MaximumSplitPositiveEvenIntegers.swift @@ -0,0 +1,25 @@ +/** + * Question Link: https://leetcode.com/problems/maximum-split-of-positive-even-integers/ + * Primary idea: Greedy. The most split should contain numbers as small as possible. + * Time Complexity: O(logn), Space Complexity: O(1) + * + */ + +class MaximumSplitPositiveEvenIntegers { + func maximumEvenSplit(_ finalSum: Int) -> [Int] { + guard finalSum % 2 == 0 else { + return [] + } + + var res = [Int](), candidate = 2, currentSum = 0 + + while (currentSum + candidate) <= finalSum { + res.append(candidate) + currentSum += candidate + candidate += 2 + } + res[res.count - 1] += finalSum - currentSum + + return res + } +} From 236fc3ad2bd0be97b585cb94226254c615479e33 Mon Sep 17 00:00:00 2001 From: Soap Date: Sat, 10 Sep 2022 13:08:17 -0700 Subject: [PATCH 32/75] Add solution to Maximum Number of Points with Cost --- DP/MaximumNumberPointsCost.swift | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 DP/MaximumNumberPointsCost.swift diff --git a/DP/MaximumNumberPointsCost.swift b/DP/MaximumNumberPointsCost.swift new file mode 100644 index 00000000..89895b09 --- /dev/null +++ b/DP/MaximumNumberPointsCost.swift @@ -0,0 +1,33 @@ +/** + * Question Link: https://leetcode.com/problems/maximum-number-of-points-with-cost/ + * Primary idea: DP. dp[i][j] is the maximum number of points we can have if points[i][j] is the most recent cell we picked. + * Optimization: Keeps track the current row is enough. Update current row max by traversing from left and right. + * Time Complexity: O(mn), Space Complexity: O(1) + * + */ + +class MaximumNumberPointsCost { + func maxPoints(_ points: [[Int]]) -> Int { + + let m = points.count, n = points[0].count + var rowMaxes = points[0] + + for i in 1.. Date: Sat, 10 Sep 2022 15:30:33 -0700 Subject: [PATCH 33/75] Add a solution to Find All Possible Recipes from Given Supplies --- .../FindAllPossibleRecipesGivenSupplies.swift | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Graph/FindAllPossibleRecipesGivenSupplies.swift diff --git a/Graph/FindAllPossibleRecipesGivenSupplies.swift b/Graph/FindAllPossibleRecipesGivenSupplies.swift new file mode 100644 index 00000000..9fbcfc3d --- /dev/null +++ b/Graph/FindAllPossibleRecipesGivenSupplies.swift @@ -0,0 +1,46 @@ +/** + * Question Link: https://leetcode.com/problems/find-all-possible-recipes-from-given-supplies/ + * Primary idea: Topological sort, keep each recipe with ingredients for a inDegree number and + * to recipe list, use a queue to decrease inDegree and form the result + * + * Time Complexity: O(n ^ 2), Space Complexity: O(n + m), + * n represents the length of recipes, m represents the length of supplies + */ + +class FindAllPossibleRecipesGivenSupplies { + func findAllRecipes(_ recipes: [String], _ ingredients: [[String]], _ supplies: [String]) -> [String] { + var (inDegrees, toRecipes) = buildGraph(recipes, ingredients) + var res = [String](), queue = supplies + + while !queue.isEmpty { + let food = queue.removeFirst() + + for recipe in toRecipes[food] ?? [] { + inDegrees[recipe]! -= 1 + + if inDegrees[recipe] == 0 { + res.append(recipe) + queue.append(recipe) + } + } + } + + return res + } + + private func buildGraph(_ recipes: [String], _ ingredients: [[String]]) -> ([String: Int], [String: [String]]) { + var inDegrees = [String: Int]() + var toRecipes = [String: [String]]() + + for i in 0.. Date: Sat, 10 Sep 2022 15:31:18 -0700 Subject: [PATCH 34/75] Refactor code style --- Graph/FindAllPossibleRecipesGivenSupplies.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graph/FindAllPossibleRecipesGivenSupplies.swift b/Graph/FindAllPossibleRecipesGivenSupplies.swift index 9fbcfc3d..3da672fd 100644 --- a/Graph/FindAllPossibleRecipesGivenSupplies.swift +++ b/Graph/FindAllPossibleRecipesGivenSupplies.swift @@ -43,4 +43,4 @@ class FindAllPossibleRecipesGivenSupplies { return (inDegrees, toRecipes) } -} \ No newline at end of file +} From bb20cd5a898b21016a6603bf56d23c92c8fdc132 Mon Sep 17 00:00:00 2001 From: Soap Date: Tue, 13 Sep 2022 19:57:10 -0700 Subject: [PATCH 35/75] Update solution to unique path ii --- DP/UniquePathsII.swift | 48 ++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/DP/UniquePathsII.swift b/DP/UniquePathsII.swift index 118a3e60..8724b14c 100644 --- a/DP/UniquePathsII.swift +++ b/DP/UniquePathsII.swift @@ -5,37 +5,29 @@ */ class UniquePathsII { - func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int { - let m = obstacleGrid.count - guard m > 0 else { - return 0 - } + func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int { + let m = obstacleGrid.count, n = obstacleGrid[0].count - let n = obstacleGrid[0].count - guard n > 0 else { - return 0 - } - - var dp = Array(repeating: Array(repeating: -1, count: n), count: m) + var dp = Array(repeating: Array(repeating: 0, count: n), count: m) - return help(m - 1, n - 1, &dp, obstacleGrid) - } - - fileprivate func help(_ m: Int, _ n: Int, _ dp: inout [[Int]], _ obstacleGrid: [[Int]]) -> Int { - if m < 0 || n < 0 { - return 0 - } - if obstacleGrid[m][n] == 1 { - return 0 - } - if m == 0 && n == 0 { - return 1 - } - if dp[m][n] != -1 { - return dp[m][n] + for i in 0.. Date: Tue, 13 Sep 2022 20:00:12 -0700 Subject: [PATCH 36/75] Add a solution to Shortest Path to Get Food --- BFS/ShortestPathGetFood.swift | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 BFS/ShortestPathGetFood.swift diff --git a/BFS/ShortestPathGetFood.swift b/BFS/ShortestPathGetFood.swift new file mode 100644 index 00000000..79fc85e6 --- /dev/null +++ b/BFS/ShortestPathGetFood.swift @@ -0,0 +1,61 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-path-to-get-food/ + * Primary idea: BFS to go over all possible word paths until the cell is food, avoid hitting the obstacle. + * + * Time Complexity: O(nm), Space Complexity: O(nm) + */ + +class ShortestPathGetFood { + func getFood(_ grid: [[Character]]) -> Int { + let m = grid.count, n = grid[0].count + var isVisited = Array(repeating: Array(repeating: false, count: n), count: m) + + let start = findStart(grid) + + isVisited[start.0][start.1] = true + var queue = [Point(i: start.0, j: start.1, len: 0)] + + while !queue.isEmpty { + let point = queue.removeFirst() + + if grid[point.i][point.j] == "#" { + return point.len + } + + for dir in [(0, 1), (0, -1), (1, 0), (-1, 0)] { + let (x, y) = (point.i + dir.0, point.j + dir.1) + + guard x >= 0 && x < m && y >= 0 && y < n && !isVisited[x][y] else { + continue + } + + if grid[x][y] == "X" { + continue + } + + isVisited[x][y] = true + queue.append(Point(i: x, j: y, len: point.len + 1)) + } + } + + return -1 + } + + private func findStart(_ grid: [[Character]]) -> (Int, Int) { + for i in 0.. Date: Tue, 13 Sep 2022 21:21:33 -0700 Subject: [PATCH 37/75] Add a solution to Shortest Path in a Grid with Obstacles Elimination --- ...ShortestPathGridObstaclesElimination.swift | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 BFS/ShortestPathGridObstaclesElimination.swift diff --git a/BFS/ShortestPathGridObstaclesElimination.swift b/BFS/ShortestPathGridObstaclesElimination.swift new file mode 100644 index 00000000..0d36fe14 --- /dev/null +++ b/BFS/ShortestPathGridObstaclesElimination.swift @@ -0,0 +1,63 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-path-in-a-grid-with-obstacles-elimination/ + * Primary idea: BFS to go over all possible word paths until the end. Choose the path only the remaining elimination number is greater. + * + * Time Complexity: O(nmk), Space Complexity: O(nm) + */ + +class ShortestPathGridObstaclesElimination { + func shortestPath(_ grid: [[Int]], _ k: Int) -> Int { + let m = grid.count, n = grid[0].count + var remainings = Array(repeating: Array(repeating: -1, count: n), count: m) + + remainings[0][0] = k + var queue = [Point(i: 0, j: 0, remain: k)] + var count = 0 + + if k > m + n - 2 { + return m + n - 2; + } + + while !queue.isEmpty { + + let currentPoints = queue + queue.removeAll() + + for point in currentPoints { + if point.i == m - 1 && point.j == n - 1 { + return count + } + + for dir in [(0, 1), (0, -1), (1, 0), (-1, 0)] { + let (x, y) = (point.i + dir.0, point.j + dir.1) + + guard x >= 0 && x < m && y >= 0 && y < n else { + continue + } + + if grid[x][y] == 1 && point.remain <= 0 { + continue + } + + let remain = grid[x][y] == 1 ? point.remain - 1 : point.remain + // only choose the path if remainings are greater + if remainings[x][y] >= remain { + continue + } + + remainings[x][y] = remain + queue.append(Point(i: x, j: y, remain: remain)) + } + } + count += 1 + } + + return -1 + } + + struct Point { + var i: Int + var j: Int + var remain: Int + } +} \ No newline at end of file From 607819e9d61a650e299a1ae88b9f6f41ec95cf48 Mon Sep 17 00:00:00 2001 From: Soap Date: Wed, 14 Sep 2022 11:30:09 -0700 Subject: [PATCH 38/75] Add a solution to Word Break --- DP/WordBreak.swift | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 DP/WordBreak.swift diff --git a/DP/WordBreak.swift b/DP/WordBreak.swift new file mode 100644 index 00000000..58ee5a00 --- /dev/null +++ b/DP/WordBreak.swift @@ -0,0 +1,26 @@ +/** + * Question Link: https://leetcode.com/problems/word-break/ + * Primary idea: DP. dp[i] = dp[j] && dict.contains(s[j.. Bool { + let dict = Set(wordDict), s = Array(s) + var dp = Array(repeating: false, count: s.count + 1) + dp[0] = true + + for i in 1...s.count { + for j in 0.. Date: Wed, 14 Sep 2022 11:59:58 -0700 Subject: [PATCH 39/75] Add a solution to Word Break II --- DFS/WordBreakII.swift | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 DFS/WordBreakII.swift diff --git a/DFS/WordBreakII.swift b/DFS/WordBreakII.swift new file mode 100644 index 00000000..21231241 --- /dev/null +++ b/DFS/WordBreakII.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/word-break-ii/ + * Primary idea: DFS. Termination case is index hits the end of the string + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class WordBreakII { + func wordBreak(_ s: String, _ wordDict: [String]) -> [String] { + var res = [String](), path = [String]() + + dfs(&res, &path, Array(s), Set(wordDict), 0) + + return res + } + + private func dfs(_ res: inout [String], _ path: inout [String], _ s: [Character], _ dict: Set, _ idx: Int) { + if idx >= s.count { + res.append(path.joined(separator: " ")) + return + } + + for i in idx.. Date: Thu, 15 Sep 2022 20:42:18 -0700 Subject: [PATCH 40/75] Add a solution to StudentAttendanceRecordI --- String/StudentAttendanceRecordI.swift | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 String/StudentAttendanceRecordI.swift diff --git a/String/StudentAttendanceRecordI.swift b/String/StudentAttendanceRecordI.swift new file mode 100644 index 00000000..8326f250 --- /dev/null +++ b/String/StudentAttendanceRecordI.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/student-attendance-record-i/ + * Primary idea: Iterate the string and check late & absent counts to check the eligibility. + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class StudentAttendanceRecordI { + func checkRecord(_ s: String) -> Bool { + var ACount = 0, LCount = 0 + let sChars = Array(s) + + for i in 0..= 2 { + return false + } + } else if sChars[i] == "L" { + if i > 0 && sChars[i - 1] == "L" { + LCount += 1 + + if LCount >= 3 { + return false + } + } else { + LCount = 1 + } + } + } + + return true + } +} \ No newline at end of file From a0e6b51db028e11c493e1443fa9e8be3fee79b37 Mon Sep 17 00:00:00 2001 From: Soap Date: Sat, 17 Sep 2022 15:24:24 -0700 Subject: [PATCH 41/75] Add a solution to Detect Squares --- Design/DetectSquares.swift | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Design/DetectSquares.swift diff --git a/Design/DetectSquares.swift b/Design/DetectSquares.swift new file mode 100644 index 00000000..edb4bff6 --- /dev/null +++ b/Design/DetectSquares.swift @@ -0,0 +1,60 @@ +/** + * Question Link: https://leetcode.com/problems/detect-squares/ + * Primary idea: Use hashmap to track points having the same x and y and calculate all possible results + * + * Time Complexity: add - O(1), count - O(n) + * Space Complexity: O(n) + * + */ + +class DetectSquares { + + private var xPoints = [Int: [Int: Int]]() + private var yPoints = [Int: [Int: Int]]() + + init() { } + + func add(_ point: [Int]) { + let x = point[0], y = point[1] + + xPoints[x, default: [y: 0]][y, default: 0] += 1 + yPoints[y, default: [x: 0]][x, default: 0] += 1 + } + + func count(_ point: [Int]) -> Int { + let x = point[0], y = point[1] + + guard let xEqualPoints = xPoints[x], let yEqualPoints = yPoints[y] else { + return 0 + } + + var res = 0 + + for (yEqualPointX, firstPointsCount) in yEqualPoints { + if x == yEqualPointX { + continue + } + let sideLength = abs(x - yEqualPointX) + // check bottom square + if let secondPointCount = xEqualPoints[y - sideLength] { + if let thirdPointCount = xPoints[yEqualPointX]?[y - sideLength] { + res += firstPointsCount * secondPointCount * thirdPointCount + } + } + if let secondPointCount = xEqualPoints[y + sideLength] { + if let thirdPointCount = xPoints[yEqualPointX]?[y + sideLength] { + res += firstPointsCount * secondPointCount * thirdPointCount + } + } + } + + return res + } +} + +/** + * Your DetectSquares object will be instantiated and called as such: + * let obj = DetectSquares() + * obj.add(point) + * let ret_2: Int = obj.count(point) + */ From 78e53edd94f960db50dfa00ed29f4d8440e020bd Mon Sep 17 00:00:00 2001 From: Soap Date: Sat, 17 Sep 2022 19:44:52 -0700 Subject: [PATCH 42/75] Add a solution to Guess Word --- Design/GuessWord.swift | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Design/GuessWord.swift diff --git a/Design/GuessWord.swift b/Design/GuessWord.swift new file mode 100644 index 00000000..302ea750 --- /dev/null +++ b/Design/GuessWord.swift @@ -0,0 +1,48 @@ +/** + * Question Link: https://leetcode.com/problems/guess-the-word/ + * Primary idea: Random select a word and check the match count. Filter all words having the same match count. + * + * Time Complexity: O(n), Space Complexity: O(n) + * + * // This is the Master's API interface. + * // You should not implement it, or speculate about its implementation + * class Master { + * public func guess(word: String) -> Int {} + * } + */ + +class GuessWord { + func findSecretWord(_ words: [String], _ master: Master) { + var words = words + + for i in 0..<30 { + let trial = words[words.count / 2] + let count = master.guess(trial) + + if count == 6 { + return + } + + var possibilities = [String]() + for word in words { + if matchCount(trial, word) == count { + possibilities.append(word) + } + } + words = possibilities + } + + } + + private func matchCount(_ wordA: String, _ wordB: String) -> Int { + var res = 0 + + for (charA, charB) in zip(wordA, wordB) { + if charA == charB { + res += 1 + } + } + + return res + } +} From ae09477fd96ba33853e18d0b8ed2619f12fb3532 Mon Sep 17 00:00:00 2001 From: Soap Date: Sat, 17 Sep 2022 20:50:39 -0700 Subject: [PATCH 43/75] Add a solution to Shortest Way to Form String --- String/ShortestWayFormString.swift | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 String/ShortestWayFormString.swift diff --git a/String/ShortestWayFormString.swift b/String/ShortestWayFormString.swift new file mode 100644 index 00000000..82dba6b5 --- /dev/null +++ b/String/ShortestWayFormString.swift @@ -0,0 +1,30 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-way-to-form-string/ + * Primary idea: Iterate through source and consume same characters for the target + * Time Complexity: O(n), Space Complexity: O(1) + */ + +class ShortestWayFormString { + func shortestWay(_ source: String, _ target: String) -> Int { + var res = 0, idx = 0 + let s = Array(source), t = Array(target) + + while idx < t.count { + let pre = idx + + for i in 0.. Date: Sun, 18 Sep 2022 19:51:27 -0700 Subject: [PATCH 44/75] Add a solution to The Earliest Moment When Everyone Become Friends --- Graph/EarliestMomentFriends.swift | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Graph/EarliestMomentFriends.swift diff --git a/Graph/EarliestMomentFriends.swift b/Graph/EarliestMomentFriends.swift new file mode 100644 index 00000000..036bd9c0 --- /dev/null +++ b/Graph/EarliestMomentFriends.swift @@ -0,0 +1,43 @@ +/** + * Question Link: https://leetcode.com/problems/the-earliest-moment-when-everyone-become-friends/ + * Primary idea: Classic Union Find, union every node until the count drops down to 1. + * + * Time Complexity: O(nlogn), Space Complexity: O(n) + * + */ + +class EarliestMomentFriends { + func earliestAcq(_ logs: [[Int]], _ n: Int) -> Int { + let logs = logs.sorted { $0[0] < $1[0] } + + var roots = Array(0.. Int { + var node = node + + while node != roots[node] { + node = roots[node] + } + + return node + } +} From af7f1f8b776886957f442a64f86252d6f9344ae3 Mon Sep 17 00:00:00 2001 From: Soap Date: Sun, 18 Sep 2022 21:56:33 -0700 Subject: [PATCH 45/75] Add a solution to Snapshot Array --- Design/SnapshotArray.swift | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Design/SnapshotArray.swift diff --git a/Design/SnapshotArray.swift b/Design/SnapshotArray.swift new file mode 100644 index 00000000..f75fa4e2 --- /dev/null +++ b/Design/SnapshotArray.swift @@ -0,0 +1,33 @@ +/** + * Question Link: https://leetcode.com/problems/snapshot-array/ + * Primary idea: Use the dictionary to dictionary to hold snapshot id to array data if necessary + * + * Time Complexity: O(1), Space Complexity: O(n) + * + */ + +class SnapshotArray { + + private var snapshotArrayMap: [Int: [Int: Int]] + private var count = 0 + private var array = [Int: Int]() + + init(_ length: Int) { + snapshotArrayMap = [Int: [Int: Int]]() + } + + func set(_ index: Int, _ val: Int) { + array[index] = val + } + + func snap() -> Int { + snapshotArrayMap[count] = array + count += 1 + + return count - 1 + } + + func get(_ index: Int, _ snap_id: Int) -> Int { + return snapshotArrayMap[snap_id]?[index] ?? 0 + } +} From 2b28d38dfb667b711dba466c70c75fcaa869adb7 Mon Sep 17 00:00:00 2001 From: Soap Date: Mon, 19 Sep 2022 17:02:39 -0700 Subject: [PATCH 46/75] Add a solution to Race Car --- BFS/RaceCar.swift | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 BFS/RaceCar.swift diff --git a/BFS/RaceCar.swift b/BFS/RaceCar.swift new file mode 100644 index 00000000..cc74767c --- /dev/null +++ b/BFS/RaceCar.swift @@ -0,0 +1,53 @@ +/** + * Question Link: https://leetcode.com/problems/race-car/ + * Primary idea: BFS to go over A or R at a specific step. Only add R to the queue when necessary. + * + * Time Complexity: O(nlogn), Space Complexity: O(n) + */ + +class RaceCar { + func racecar(_ target: Int) -> Int { + let startNode = Node(speed: 1, position: 0) + var queue = [startNode], len = 0, isVisited = Set() + + while !queue.isEmpty { + let size = queue.count + + for _ in 0.. 0 ? -1 : 1, position: node.position) + queue.append(RNode) + } + + } + + len += 1 + } + + return len + } + + private func shouldRevert(_ node: Node, _ target: Int) -> Bool { + return (node.position + node.speed > target && node.speed > 0) || (node.position + node.speed < target && node.speed < 0) + } + + struct Node: Hashable { + let speed: Int + let position: Int + } +} From 0b15f3df6e397add324c2a6740cc7bdf8bba3944 Mon Sep 17 00:00:00 2001 From: Soap Date: Wed, 21 Sep 2022 22:19:38 -0700 Subject: [PATCH 47/75] Add a solution to Number of Closed Islands --- DFS/NumberClosedIslands.swift | 59 +++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 DFS/NumberClosedIslands.swift diff --git a/DFS/NumberClosedIslands.swift b/DFS/NumberClosedIslands.swift new file mode 100644 index 00000000..3e4c3f95 --- /dev/null +++ b/DFS/NumberClosedIslands.swift @@ -0,0 +1,59 @@ +/** + * Question Link: https://leetcode.com/problems/number-of-closed-islands/ + * Primary idea: Classic Depth-first Search, go up, down, left, right four directions. Return false only it hits the edge. + * + * Time Complexity: O(mn), Space Complexity: O(mn) + * + */ + +class NumberClosedIslands { + func closedIsland(_ grid: [[Int]]) -> Int { + let m = grid.count, n = grid[0].count + + var isVisited = Array(repeating: Array(repeating: false, count: n), count: m), res = 0 + + for i in 0.. Bool { + guard i >= 0 && i < m && j >= 0 && j < n else { + return false + } + + if grid[i][j] == 1 { + return true + } + + isVisited[i][j] = true + + + var up = true, down = true, left = true, right = true + + if i - 1 < 0 || !isVisited[i - 1][j] { + up = dfs(grid, i - 1, j, m, n, &isVisited) + } + if i + 1 >= m || !isVisited[i + 1][j] { + down = dfs(grid, i + 1, j, m, n, &isVisited) + } + if j - 1 < 0 || !isVisited[i][j - 1] { + left = dfs(grid, i, j - 1, m, n, &isVisited) + } + if j + 1 >= n || !isVisited[i][j + 1] { + right = dfs(grid, i, j + 1, m, n, &isVisited) + } + + return up && down && left && right + } +} From 12f4ff173a8c818733c9325a91cd50121862780e Mon Sep 17 00:00:00 2001 From: Soap Date: Thu, 22 Sep 2022 20:40:34 -0700 Subject: [PATCH 48/75] Refactor the solution to word ladder --- BFS/WordLadder.swift | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/BFS/WordLadder.swift b/BFS/WordLadder.swift index 3346c53c..6220d033 100644 --- a/BFS/WordLadder.swift +++ b/BFS/WordLadder.swift @@ -10,20 +10,26 @@ class WordLadder { func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { - var wordSet = Set(wordList), wordStepQueue = [(beginWord, 1)] + var wordSet = Set(wordList), wordQueue = [beginWord], count = 1 - while !wordStepQueue.isEmpty { - let (currentWord, currentStep) = wordStepQueue.removeFirst() + while !wordQueue.isEmpty { - if currentWord == endWord { - return currentStep - } + let size = wordQueue.count - for word in neighbors(for: currentWord, in: wordSet) { + for i in 0.. Date: Thu, 22 Sep 2022 21:51:26 -0700 Subject: [PATCH 49/75] Refactor code style for BFS questions --- BFS/ShortestPathGetFood.swift | 45 ++++++++-------- ...ShortestPathGridObstaclesElimination.swift | 51 +++++++++---------- BFS/WordLadder.swift | 2 +- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/BFS/ShortestPathGetFood.swift b/BFS/ShortestPathGetFood.swift index 79fc85e6..7f76035e 100644 --- a/BFS/ShortestPathGetFood.swift +++ b/BFS/ShortestPathGetFood.swift @@ -13,29 +13,36 @@ class ShortestPathGetFood { let start = findStart(grid) isVisited[start.0][start.1] = true - var queue = [Point(i: start.0, j: start.1, len: 0)] + var queue = [(start.0, start.1)], count = 0 while !queue.isEmpty { - let point = queue.removeFirst() - if grid[point.i][point.j] == "#" { - return point.len - } + let size = queue.count - for dir in [(0, 1), (0, -1), (1, 0), (-1, 0)] { - let (x, y) = (point.i + dir.0, point.j + dir.1) - - guard x >= 0 && x < m && y >= 0 && y < n && !isVisited[x][y] else { - continue + for _ in 0..= 0 && x < m && y >= 0 && y < n && !isVisited[x][y] else { + continue + } + + if grid[x][y] == "X" { + continue + } + + isVisited[x][y] = true + queue.append((x, y)) } - - isVisited[x][y] = true - queue.append(Point(i: x, j: y, len: point.len + 1)) } + + count += 1 } return -1 @@ -52,10 +59,4 @@ class ShortestPathGetFood { return (-1, -1) } - - struct Point { - var i: Int - var j: Int - var len: Int - } } diff --git a/BFS/ShortestPathGridObstaclesElimination.swift b/BFS/ShortestPathGridObstaclesElimination.swift index 0d36fe14..9e17e296 100644 --- a/BFS/ShortestPathGridObstaclesElimination.swift +++ b/BFS/ShortestPathGridObstaclesElimination.swift @@ -8,47 +8,46 @@ class ShortestPathGridObstaclesElimination { func shortestPath(_ grid: [[Int]], _ k: Int) -> Int { let m = grid.count, n = grid[0].count - var remainings = Array(repeating: Array(repeating: -1, count: n), count: m) + var remaining = Array(repeating: Array(repeating: -1, count: n), count: m) + var queue = [Point(i: 0, j: 0, remain: k)], count = 0 - remainings[0][0] = k - var queue = [Point(i: 0, j: 0, remain: k)] - var count = 0 - - if k > m + n - 2 { - return m + n - 2; + if m + n - 2 < k { + return m + n - 2 } while !queue.isEmpty { + let size = queue.count - let currentPoints = queue - queue.removeAll() - - for point in currentPoints { - if point.i == m - 1 && point.j == n - 1 { + for _ in 0..= 0 && x < m && y >= 0 && y < n else { continue } - - if grid[x][y] == 1 && point.remain <= 0 { + + if grid[x][y] == 1 && point.remain == 0 { continue } - let remain = grid[x][y] == 1 ? point.remain - 1 : point.remain - // only choose the path if remainings are greater - if remainings[x][y] >= remain { + let nextRemaining = grid[x][y] == 1 ? point.remain - 1 : point.remain + + if remaining[x][y] >= nextRemaining { continue - } + } - remainings[x][y] = remain - queue.append(Point(i: x, j: y, remain: remain)) + remaining[x][y] = nextRemaining + queue.append(Point(i: x, j: y, remain: nextRemaining)) + } } + count += 1 } @@ -56,8 +55,8 @@ class ShortestPathGridObstaclesElimination { } struct Point { - var i: Int - var j: Int - var remain: Int + let i: Int + let j: Int + let remain: Int } } \ No newline at end of file diff --git a/BFS/WordLadder.swift b/BFS/WordLadder.swift index 6220d033..633c8c09 100644 --- a/BFS/WordLadder.swift +++ b/BFS/WordLadder.swift @@ -16,7 +16,7 @@ class WordLadder { let size = wordQueue.count - for i in 0.. Date: Sat, 24 Sep 2022 19:03:51 -0700 Subject: [PATCH 50/75] Refactor Code Style --- Graph/CourseSchedule.swift | 37 +++++++++++++++--------------------- Graph/CourseScheduleII.swift | 9 ++------- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/Graph/CourseSchedule.swift b/Graph/CourseSchedule.swift index ac2b43e3..995d691f 100644 --- a/Graph/CourseSchedule.swift +++ b/Graph/CourseSchedule.swift @@ -8,39 +8,32 @@ class CourseSchedule { func canFinish(_ numCourses: Int, _ prerequisites: [[Int]]) -> Bool { - var inDegrees = Array(repeating: 0, count: numCourses), fromTo = [Int: [Int]]() - var coursesCouldTake = [Int](), queue = [Int]() + var inDegrees = Array(repeating: 0, count: numCourses), toCourses = [Int: [Int]]() - // init graph - for prerequisite in prerequisites { - fromTo[prerequisite[1], default: []].append(prerequisite[0]) - inDegrees[prerequisite[0]] += 1 + for courses in prerequisites { + inDegrees[courses[0]] += 1 + toCourses[courses[1], default:[]].append(courses[0]) } - // BFS - for course in 0.. [Int] { var inDegrees = Array(repeating: 0, count: numCourses), fromTo = [Int: [Int]]() - var coursesCouldTake = [Int](), queue = [Int]() + var coursesCouldTake = [Int]() // init graph for prerequisite in prerequisites { @@ -17,12 +17,7 @@ inDegrees[prerequisite[0]] += 1 } - // BFS - for course in 0.. Date: Sun, 25 Sep 2022 21:39:34 -0700 Subject: [PATCH 51/75] Refactor solution to LRU Cache --- Design/LRUCache.swift | 124 ++++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/Design/LRUCache.swift b/Design/LRUCache.swift index 1158a90c..ac7fb6c6 100644 --- a/Design/LRUCache.swift +++ b/Design/LRUCache.swift @@ -5,71 +5,87 @@ * */ -class DoublyLinkedList{ - var key: Int - var value: Int - var previous: DoublyLinkedList? - var next: DoublyLinkedList? - var hashValue: Int +class LRUCache { - init(_ key: Int, _ value: Int) { - self.key = key - self.value = value - self.hashValue = key - } -} + private let capacity: Int + private let head = Node(0, 0) + private let tail = Node(0, 0) + + private var keyNodeMap = [Int: Node]() -class LRUCache{ - var maxCapacity: Int - var head: DoublyLinkedList - var tail: DoublyLinkedList - var cache: [Int: DoublyLinkedList] + init(_ capacity: Int) { + self.capacity = capacity + + head.next = tail + tail.prev = head + } - init(_ maxCapacity: Int) { - self.maxCapacity = maxCapacity - self.cache = [Int: DoublyLinkedList]() - self.head = DoublyLinkedList(Int.min, Int.min) - self.tail = DoublyLinkedList(Int.max, Int.max) - self.head.next = self.tail - self.tail.previous = self.head + func get(_ key: Int) -> Int { + guard let node = keyNodeMap[key] else { + return -1 + } + + remove(node) + moveToHead(node) + + return node.val } - func add(_ node: DoublyLinkedList){ - let next = head.next - head.next = node - node.previous = head - node.next = next - next?.previous = node + func put(_ key: Int, _ value: Int) { + let node = Node(key, value) + + if let lastNode = keyNodeMap[key] { + remove(lastNode) + } + + keyNodeMap[key] = node + moveToHead(node) + + if keyNodeMap.count > capacity { + keyNodeMap[tail.prev!.key] = nil + remove(tail.prev!) + } } - func remove(_ node: DoublyLinkedList){ - let previous = node.previous - let next = node.next - previous?.next = next - next?.previous = previous + private func remove(_ node: Node) { + let prev = node.prev + let post = node.next + + prev!.next = post + post!.prev = prev + + node.next = nil + node.prev = nil } - func get(_ key: Int) -> Int{ - if let node = cache[key]{ - remove(node) - add(node) - return node.value - } - return -1 + private func moveToHead(_ node: Node) { + let first = head.next + + head.next = node + + node.prev = head + node.next = first + + first!.prev = node } - func put(_ key: Int, _ value: Int){ - if let node = cache[key]{ - remove(node) - cache.removeValue(forKey: key) - }else if cache.keys.count >= maxCapacity{ - if let leastNode = tail.previous{ - remove(leastNode) - cache.removeValue(forKey: leastNode.key) - } + class Node { + let key: Int + var val: Int + + var prev: Node? + var next: Node? + + init(_ key: Int, _ val: Int) { + self.key = key + self.val = val } - let newNode = DoublyLinkedList(key, value) - cache[key] = newNode - add(newNode) } } + +/** + * Your LRUCache object will be instantiated and called as such: + * let obj = LRUCache(capacity) + * let ret_1: Int = obj.get(key) + * obj.put(key, value) + */ From 67ee0da99d2bc18089482de777d12aaed4281943 Mon Sep 17 00:00:00 2001 From: Soap Date: Mon, 26 Sep 2022 19:54:33 -0700 Subject: [PATCH 52/75] Add a solution to Search Suggestions System --- Design/SearchSuggestionsSystem.swift | 72 ++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Design/SearchSuggestionsSystem.swift diff --git a/Design/SearchSuggestionsSystem.swift b/Design/SearchSuggestionsSystem.swift new file mode 100644 index 00000000..c31cfc30 --- /dev/null +++ b/Design/SearchSuggestionsSystem.swift @@ -0,0 +1,72 @@ +/** + * Question Link: https://leetcode.com/problems/search-suggestions-system/ + * Primary idea: Use trie to add and search prefix (DFS). + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class SearchSuggestionsSystem { + func suggestedProducts(_ products: [String], _ searchWord: String) -> [[String]] { + let trie = Trie() + var res = [[String]]() + + products.forEach { trie.insert($0) } + + return (1...searchWord.count).map { trie.searchWords(for: String(searchWord.prefix($0))) } + } + + private class Trie { + let root = TrieNode() + + func insert(_ word: String) { + var node = root + + for char in word { + if node.charNodeMap[char] == nil { + node.charNodeMap[char] = TrieNode() + } + + node = node.charNodeMap[char]! + } + + node.isWord = true + } + + func searchWords(for term: String) -> [String] { + var res = [String](), node = root + + for char in term { + guard let next = node.charNodeMap[char] else { + return res + } + + node = next + } + + dfs(&res, term, node) + + return Array(res.sorted().prefix(3)) + } + + private func dfs(_ res: inout [String], _ path: String, _ node: TrieNode) { + if node.isWord { + res.append(path) + } + + for (char, next) in node.charNodeMap { + dfs(&res, path + String(char), next) + } + } + } + + private class TrieNode { + var isWord: Bool + var charNodeMap: [Character: TrieNode] + + init() { + isWord = false + charNodeMap = [Character: TrieNode]() + } + } +} From d8bcc6ffb11ca64293281e14b689ddccb6cbac3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Sarac=CC=A7?= Date: Thu, 29 Sep 2022 00:15:08 +0300 Subject: [PATCH 53/75] updated solution for multiplyStrings --- String/MultiplyStrings.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/String/MultiplyStrings.swift b/String/MultiplyStrings.swift index a8169b91..c1c0302d 100644 --- a/String/MultiplyStrings.swift +++ b/String/MultiplyStrings.swift @@ -18,12 +18,12 @@ class MultiplyStrings { // calculate product for every digit for (i, char1) in num1.enumerated() { - guard let digit1 = Int(String(char1)) else { + guard let digit1 = char1.wholeNumberValue else { fatalError("Invalid Input num1") } for (j, char2) in num2.enumerated() { - guard let digit2 = Int(String(char2)) else { + guard let digit2 = char2.wholeNumberValue else { fatalError("Invalid Input num2") } @@ -44,7 +44,7 @@ class MultiplyStrings { } // trim starting 0s - while !resStr.isEmpty && resStr.first! == "0" { + while resStr.first == "0" { resStr.removeFirst() } From 23bf81dc49d23082c593c91877c84ee2263f84e0 Mon Sep 17 00:00:00 2001 From: Soap Date: Sat, 1 Oct 2022 22:30:26 -0700 Subject: [PATCH 54/75] Refactor solution to Text Justification --- String/TextJustification.swift | 89 ++++++++++++++++------------------ 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/String/TextJustification.swift b/String/TextJustification.swift index d2d3b125..90efbb84 100644 --- a/String/TextJustification.swift +++ b/String/TextJustification.swift @@ -7,72 +7,67 @@ class TextJustification { func fullJustify(_ words: [String], _ maxWidth: Int) -> [String] { - var res = [String]() - var last = 0, currentLineLength = 0 - + var res = [String](), rowStart = 0, currentLen = 0 + for (i, word) in words.enumerated() { - if currentLineLength + word.count + (i - last) > maxWidth { - - res.append(buildLine(words, last, i - 1, maxWidth, currentLineLength)) - - last = i - currentLineLength = 0 + + if currentLen + word.count + (i - rowStart) > maxWidth { + res.append(buildRow(rowStart, i - 1, words, maxWidth, currentLen)) + + rowStart = i + currentLen = 0 } - - currentLineLength += word.count + + currentLen += word.count } - - res.append(buildLastLine(words, last, words.count - 1, maxWidth)) - + + res.append(buildLastRow(rowStart, words, maxWidth)) + return res } - - fileprivate func buildLine(_ words: [String], _ start: Int, _ end: Int, _ maxWidth: Int, _ currentLineLength: Int) -> String { - var line = "" - var extraSpaceNum = 0, spaceNum = 0 - + + private func buildRow(_ start: Int, _ end: Int, _ words: [String], _ maxWidth: Int, _ currentLen: Int) -> String { + var res = "", spaceNum = 0, extraSpaceNum = 0 + if end > start { - extraSpaceNum = (maxWidth - currentLineLength) % (end - start) - spaceNum = (maxWidth - currentLineLength) / (end - start) + spaceNum = (maxWidth - currentLen) / (end - start) + extraSpaceNum = (maxWidth - currentLen) % (end - start) } else { - spaceNum = maxWidth - currentLineLength + spaceNum = maxWidth - currentLen } - + for i in start...end { - line.append(words[i]) - + res += words[i] + if start != end && i == end { break - } - - for _ in 0.. 0 { - line.append(" ") + res.append(" ") extraSpaceNum -= 1 } } - - return line + + return res } - - fileprivate func buildLastLine(_ words: [String], _ start: Int, _ end: Int, _ maxWidth: Int) -> String { - var line = "" - - for i in start...end { - line.append(words[i]) - - if i < end { - line.append(" ") + + private func buildLastRow(_ start: Int, _ words: [String], _ maxWidth: Int) -> String { + var res = "" + + for i in start.. Date: Sat, 1 Oct 2022 22:37:29 -0700 Subject: [PATCH 55/75] Add a solution to Check if Word Can Be Placed In Crossword --- DFS/CheckWordCrossword.swift | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 DFS/CheckWordCrossword.swift diff --git a/DFS/CheckWordCrossword.swift b/DFS/CheckWordCrossword.swift new file mode 100644 index 00000000..94396961 --- /dev/null +++ b/DFS/CheckWordCrossword.swift @@ -0,0 +1,54 @@ +/** + * Question Link: https://leetcode.com/problems/check-if-word-can-be-placed-in-crossword/ + * Primary idea: Classic Depth-first Search, check possible position and go through four directions + * + * Time Complexity: O(mnl), Space Complexity: O(1) + * + */ + +class CheckWordCrossword { + func placeWordInCrossword(_ board: [[Character]], _ word: String) -> Bool { + let dirs = [(0, 1), (0, -1), (1, 0), (-1, 0)], m = board.count, n = board[0].count + + for i in 0.. Bool { + let m = board.count, n = board[0].count + + return i >= 0 && i < m && j >= 0 && j < n && board[i][j] != "#" + } + + private func dfs(_ idx: Int, _ word: [Character], _ board: [[Character]], _ i: Int, _ j: Int, _ dir: (Int, Int)) -> Bool { + if idx == word.count { + return !isValid(i, j, board) + } + + guard isValid(i, j, board) else { + return false + } + + guard board[i][j] == " " || board[i][j] == word[idx] else { + return false + } + + return dfs(idx + 1, word, board, i + dir.0, j + dir.1, dir) + } +} From 3503f3b1acc19917223f56df5201bbc49d32ebb4 Mon Sep 17 00:00:00 2001 From: Soap Date: Sun, 2 Oct 2022 13:00:46 -0700 Subject: [PATCH 56/75] Add a solution to Range Module --- Sort/RangeModule.swift | 100 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Sort/RangeModule.swift diff --git a/Sort/RangeModule.swift b/Sort/RangeModule.swift new file mode 100644 index 00000000..4b5ef42c --- /dev/null +++ b/Sort/RangeModule.swift @@ -0,0 +1,100 @@ +/** + * Question Link: https://leetcode.com/problems/range-module/ + * Primary idea: Sort ranges by left, query using binary search, remove to handle 4 cases separately. + * Time Complexity: add - O(nlogn), query - O(logn), remove - O(n), Space Complexity: O(n) + * + */ + +class RangeModule { + + var sortedRanges: [(Int, Int)] + + init() { + sortedRanges = [(Int, Int)]() + } + + func addRange(_ left: Int, _ right: Int) { + sortRanges(for: (left, right)) + } + + func queryRange(_ left: Int, _ right: Int) -> Bool { + // binary search + var l = 0, r = sortedRanges.count - 1 + + while l <= r { + let m = (r - l) / 2 + l + + if sortedRanges[m].0 <= left && sortedRanges[m].1 >= right { + return true + } else { + if sortedRanges[m].0 > right { + r = m - 1 + } else if sortedRanges[m].1 < left { + l = m + 1 + } else { + return false + } + } + } + + return false + } + + func removeRange(_ left: Int, _ right: Int) { + var idx = 0 + + while idx < sortedRanges.count { + let range = sortedRanges[idx] + + if isOverlap(range, (left, right)) { + if range.0 >= left && range.1 <= right { + sortedRanges.remove(at: idx) + } else if range.0 < left && range.1 > right { + sortedRanges.remove(at: idx) + sortedRanges.insert((right, range.1), at: idx) + sortedRanges.insert((range.0, left), at: idx) + idx += 2 + } else if range.1 <= right { + sortedRanges[idx].1 = left + idx += 1 + } else { + sortedRanges[idx].0 = right + idx += 1 + } + } else { + idx += 1 + } + } + } + + + private func sortRanges(for newRange: (Int, Int)) { + sortedRanges.append(newRange) + + sortedRanges.sort { + $0.0 < $1.0 + } + + var res = [(Int, Int)]() + + for range in sortedRanges { + guard let last = res.last else { + res.append(range) + continue + } + + if range.0 > last.1 { + res.append(range) + } else { + res.removeLast() + res.append((last.0, max(last.1, range.1))) + } + } + + sortedRanges = res + } + + private func isOverlap(_ l: (Int, Int), _ r: (Int, Int)) -> Bool { + return !(l.0 >= r.1 || r.0 >= l.1) + } +} From 8dc4b64a66163ebb85908402a57b24db182fde3c Mon Sep 17 00:00:00 2001 From: Soap Date: Mon, 3 Oct 2022 14:30:39 -0700 Subject: [PATCH 57/75] Refactor solution to the Longest Increasing Subsequence --- DP/LongestIncreasingSubsequence.swift | 48 +++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/DP/LongestIncreasingSubsequence.swift b/DP/LongestIncreasingSubsequence.swift index 819439d2..89592487 100644 --- a/DP/LongestIncreasingSubsequence.swift +++ b/DP/LongestIncreasingSubsequence.swift @@ -6,34 +6,34 @@ class LongestIncreasingSubsequence { func lengthOfLIS(_ nums: [Int]) -> Int { - guard let first = nums.first else { - return 0 - } - - var ends = [first] + var res = [nums[0]] for i in 1..= ends.count { - ends.append(nums[i]) + if res.last! < nums[i] { + res.append(nums[i]) } else { - ends[right] = nums[i] + res[binarySearch(res, nums[i])] = nums[i] } } - return ends.count + return res.count + } + + private func binarySearch(_ num: Int, _ res: [Int]) -> Int { + var l = 0, r = res.count - 1 + + while l < r { + let mid = (r - l) / 2 + l + + if res[mid] == num { + return mid + } else if res[mid] > num { + r = mid + } else { + l = mid + 1 + } + } + + return l } -} \ No newline at end of file +} From f6c0b05509ae69ed69c55070b1c3478836ce0a0a Mon Sep 17 00:00:00 2001 From: Soap Date: Mon, 3 Oct 2022 15:37:43 -0700 Subject: [PATCH 58/75] Refactor solution to Minimum Path Sum --- DP/MinimumPathSum.swift | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/DP/MinimumPathSum.swift b/DP/MinimumPathSum.swift index ab455b1a..39b3c028 100644 --- a/DP/MinimumPathSum.swift +++ b/DP/MinimumPathSum.swift @@ -6,27 +6,23 @@ class MinimumPathSum { func minPathSum(_ grid: [[Int]]) -> Int { - guard grid.count != 0 && grid[0].count != 0 else{ - return 0 - } - let m = grid.count, n = grid[0].count - var dp = Array(repeating: Array(repeating: 0, count: n), count: m) - + var dp = grid + for i in 0.. Date: Wed, 5 Oct 2022 20:05:42 -0700 Subject: [PATCH 59/75] Update solution to the Group Anagrams --- String/GroupAnagrams.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/String/GroupAnagrams.swift b/String/GroupAnagrams.swift index 08ab82f2..3b8b6ba3 100644 --- a/String/GroupAnagrams.swift +++ b/String/GroupAnagrams.swift @@ -8,14 +8,6 @@ class GroupAnagrams { func groupAnagrams(_ strs: [String]) -> [[String]] { - var sortedStrToStrs = [String: [String]]() - - for str in strs { - let sortedStr = String(str.sorted()) - - sortedStrToStrs[sortedStr, default: []].append(str) - } - - return Array(sortedStrToStrs.values) + return Array(Dictionary(strs.map { (String($0.sorted()), [$0]) }, uniquingKeysWith: +).values) } } From c712ca0f496327f42390e74bdf28a328f1fdf94d Mon Sep 17 00:00:00 2001 From: Soap Date: Wed, 5 Oct 2022 20:48:31 -0700 Subject: [PATCH 60/75] Build a sliding window solutions set and add a solution to the Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit --- .../LongestContinuousSubarrayLimit.swift | 41 +++++++++++++++++++ ...gestSubstringMostKDistinctCharacters.swift | 0 ...stSubstringMostTwoDistinctCharacters.swift | 0 ...tSubstringWithoutRepeatingCharacters.swift | 0 .../MinimumWindowSubstring.swift | 0 .../SlidingWindowMaximum.swift | 0 .../SubarraysKDifferentIntegers.swift | 0 7 files changed, 41 insertions(+) create mode 100644 SlidingWindow/LongestContinuousSubarrayLimit.swift rename {String => SlidingWindow}/LongestSubstringMostKDistinctCharacters.swift (100%) rename {String => SlidingWindow}/LongestSubstringMostTwoDistinctCharacters.swift (100%) rename {String => SlidingWindow}/LongestSubstringWithoutRepeatingCharacters.swift (100%) rename {String => SlidingWindow}/MinimumWindowSubstring.swift (100%) rename {Array => SlidingWindow}/SlidingWindowMaximum.swift (100%) rename {Array => SlidingWindow}/SubarraysKDifferentIntegers.swift (100%) diff --git a/SlidingWindow/LongestContinuousSubarrayLimit.swift b/SlidingWindow/LongestContinuousSubarrayLimit.swift new file mode 100644 index 00000000..a0bd7b10 --- /dev/null +++ b/SlidingWindow/LongestContinuousSubarrayLimit.swift @@ -0,0 +1,41 @@ +/** + * Question Link: https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/description/ + * Primary idea: Slding window, use max and min queues to track largest difference along the way. Move left pointer to get the potential result. + * + * Note: k may be invalid, mention that with interviewer + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class LongestContinuousSubarrayLimit { + func longestSubarray(_ nums: [Int], _ limit: Int) -> Int { + var maxQueue = [Int](), minQueue = [Int](), left = 0, size = 0 + + for (i, num) in nums.enumerated() { + while !maxQueue.isEmpty && maxQueue.last! < num { + maxQueue.removeLast() + } + while !minQueue.isEmpty && minQueue.last! > num { + minQueue.removeLast() + } + + maxQueue.append(num) + minQueue.append(num) + + if maxQueue.first! - minQueue.first! > limit { + if nums[left] == maxQueue.first! { + maxQueue.removeFirst() + } + if nums[left] == minQueue.first! { + minQueue.removeFirst() + } + + left += 1 + } + + size = max(size, i - left + 1) + } + + return size + } +} \ No newline at end of file diff --git a/String/LongestSubstringMostKDistinctCharacters.swift b/SlidingWindow/LongestSubstringMostKDistinctCharacters.swift similarity index 100% rename from String/LongestSubstringMostKDistinctCharacters.swift rename to SlidingWindow/LongestSubstringMostKDistinctCharacters.swift diff --git a/String/LongestSubstringMostTwoDistinctCharacters.swift b/SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift similarity index 100% rename from String/LongestSubstringMostTwoDistinctCharacters.swift rename to SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift diff --git a/String/LongestSubstringWithoutRepeatingCharacters.swift b/SlidingWindow/LongestSubstringWithoutRepeatingCharacters.swift similarity index 100% rename from String/LongestSubstringWithoutRepeatingCharacters.swift rename to SlidingWindow/LongestSubstringWithoutRepeatingCharacters.swift diff --git a/String/MinimumWindowSubstring.swift b/SlidingWindow/MinimumWindowSubstring.swift similarity index 100% rename from String/MinimumWindowSubstring.swift rename to SlidingWindow/MinimumWindowSubstring.swift diff --git a/Array/SlidingWindowMaximum.swift b/SlidingWindow/SlidingWindowMaximum.swift similarity index 100% rename from Array/SlidingWindowMaximum.swift rename to SlidingWindow/SlidingWindowMaximum.swift diff --git a/Array/SubarraysKDifferentIntegers.swift b/SlidingWindow/SubarraysKDifferentIntegers.swift similarity index 100% rename from Array/SubarraysKDifferentIntegers.swift rename to SlidingWindow/SubarraysKDifferentIntegers.swift From 264d2ab639f47b839eee98eb135ba368e9a19320 Mon Sep 17 00:00:00 2001 From: Soap Date: Thu, 6 Oct 2022 10:15:46 -0700 Subject: [PATCH 61/75] Add a solution to the Frequency of the Most Frequent Element --- .../FrequencyMostFrequentElement.swift | 27 +++++++++++++++++++ .../LongestContinuousSubarrayLimit.swift | 10 +++---- 2 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 SlidingWindow/FrequencyMostFrequentElement.swift diff --git a/SlidingWindow/FrequencyMostFrequentElement.swift b/SlidingWindow/FrequencyMostFrequentElement.swift new file mode 100644 index 00000000..62d3ab09 --- /dev/null +++ b/SlidingWindow/FrequencyMostFrequentElement.swift @@ -0,0 +1,27 @@ +/** + * Question Link: https://leetcode.com/problems/frequency-of-the-most-frequent-element/ + * Primary idea: Slding window, sort the nums and move left if k cannot make current window even. + * + * Time Complexity: O(nlogn), Space Complexity: O(1) + * + */ + +class FrequencyMostFrequentElement { + func maxFrequency(_ nums: [Int], _ k: Int) -> Int { + let nums = nums.sorted() + var left = 0, res = 1, sum = 0 + + for (i, num) in nums.enumerated() { + sum += num + + while (i - left + 1) * num - sum > k { + sum -= nums[left] + left += 1 + } + + res = max(res, i - left + 1) + } + + return res + } +} diff --git a/SlidingWindow/LongestContinuousSubarrayLimit.swift b/SlidingWindow/LongestContinuousSubarrayLimit.swift index a0bd7b10..362c72fc 100644 --- a/SlidingWindow/LongestContinuousSubarrayLimit.swift +++ b/SlidingWindow/LongestContinuousSubarrayLimit.swift @@ -1,5 +1,5 @@ /** - * Question Link: https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/description/ + * Question Link: https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/ * Primary idea: Slding window, use max and min queues to track largest difference along the way. Move left pointer to get the potential result. * * Note: k may be invalid, mention that with interviewer @@ -9,7 +9,7 @@ class LongestContinuousSubarrayLimit { func longestSubarray(_ nums: [Int], _ limit: Int) -> Int { - var maxQueue = [Int](), minQueue = [Int](), left = 0, size = 0 + var maxQueue = [Int](), minQueue = [Int](), left = 0, res = 0 for (i, num) in nums.enumerated() { while !maxQueue.isEmpty && maxQueue.last! < num { @@ -33,9 +33,9 @@ class LongestContinuousSubarrayLimit { left += 1 } - size = max(size, i - left + 1) + res = max(res, i - left + 1) } - return size + return res } -} \ No newline at end of file +} From 618b3ce8bb5fbb9c1cdb222f53eb925f645b6961 Mon Sep 17 00:00:00 2001 From: Soap Date: Thu, 6 Oct 2022 15:31:35 -0700 Subject: [PATCH 62/75] Add a solution to Minesweeper --- DFS/Minesweeper.swift | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 DFS/Minesweeper.swift diff --git a/DFS/Minesweeper.swift b/DFS/Minesweeper.swift new file mode 100644 index 00000000..29652b24 --- /dev/null +++ b/DFS/Minesweeper.swift @@ -0,0 +1,64 @@ +/** + * Question Link: https://leetcode.com/problems/minesweeper/ + * Primary idea: Classic Depth-first Search. Check current node and dfs all directions if mine count is 0, update the board accordingly. + * + * Time Complexity: O(mn), Space Complexity: O(mn) + * + */ + +class Minesweeper { + private let dirs = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (-1, 1), (1, -1), (-1, -1)] + + func updateBoard(_ board: [[Character]], _ click: [Int]) -> [[Character]] { + let i = click[0], j = click[1] + var board = board + + if board[i][j] == "M" { + board[i][j] = "X".first! + return board + } + + let m = board.count, n = board[0].count + var isVisited = Array(repeating: Array(repeating: false, count: n), count: m) + + dfs(&board, i, j, &isVisited) + + return board + } + + private func dfs(_ board: inout [[Character]], _ i: Int, _ j: Int, _ isVisited: inout [[Bool]]) { + guard isValid(i, j, board) && !isVisited[i][j] else { + return + } + + isVisited[i][j] = true + + if board[i][j] == "E" { + var count = 0 + for dir in dirs { + let x = i + dir.0, y = j + dir.1 + + if isValid(x, y, board) { + if board[x][y] == "X" || board[x][y] == "M" { + count += 1 + } + } + } + + if count == 0 { + board[i][j] = "B".first! + + for dir in dirs { + let x = i + dir.0, y = j + dir.1 + dfs(&board, x, y, &isVisited) + } + } else { + board[i][j] = String(count).first! + } + } + } + + private func isValid(_ i: Int, _ j: Int, _ board: [[Character]]) -> Bool { + return i >= 0 && i < board.count && j >= 0 && j < board[0].count + } +} From be5b3fbe4e5945c9d297272d17527dce6977d70e Mon Sep 17 00:00:00 2001 From: Soap Date: Thu, 6 Oct 2022 16:19:06 -0700 Subject: [PATCH 63/75] Add a solution to the Employee Free Time --- Sort/EmployeeFreeTime.swift | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Sort/EmployeeFreeTime.swift diff --git a/Sort/EmployeeFreeTime.swift b/Sort/EmployeeFreeTime.swift new file mode 100644 index 00000000..753b4aa6 --- /dev/null +++ b/Sort/EmployeeFreeTime.swift @@ -0,0 +1,77 @@ +/** + * Question Link: https://leetcode.com/problems/employee-free-time/ + * Primary idea: Combine and merge sorted arrays. Then iterate through it to get offset between every two elements. + * Time Complexity: O(n), Space Complexity: O(n) + * + * Definition for an Interval. + * public class Interval { + * public var start: Int + * public var end: Int + * public init(_ start: Int, _ end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class EmployeeFreeTime { + func employeeFreeTime(_ schedule: [[Interval]]) -> [Interval] { + let intervals = mergeIntervals(combineIntervals(schedule)) + var res = [Interval]() + + for i in 1.. [Interval] { + var res = [Interval]() + + for interval in intervals { + if let last = res.last, last.end >= interval.start { + res.removeLast() + res.append(Interval(last.start, max(last.end, interval.end))) + } else { + res.append(interval) + } + } + + return res + } + + private func combineIntervals(_ schedule: [[Interval]]) -> [Interval] { + var res = schedule[0] + + for i in 1.. [Interval] { + var res = [Interval](), i = 0, j = 0 + + while i < l.count || j < r.count { + if i == l.count { + res.append(r[j]) + j += 1 + } else if j == r.count { + res.append(l[i]) + i += 1 + } else { + if l[i].start <= r[j].start { + res.append(l[i]) + i += 1 + } else { + res.append(r[j]) + j += 1 + } + } + } + + return res + } +} From 7de926af335bb1fa87c8ef172b09ef8381c1a00f Mon Sep 17 00:00:00 2001 From: Soap Date: Sat, 8 Oct 2022 15:32:49 -0700 Subject: [PATCH 64/75] Add a solution to Evaluate Division --- Graph/EvaluateDivision.swift | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Graph/EvaluateDivision.swift diff --git a/Graph/EvaluateDivision.swift b/Graph/EvaluateDivision.swift new file mode 100644 index 00000000..6ea0c775 --- /dev/null +++ b/Graph/EvaluateDivision.swift @@ -0,0 +1,62 @@ +/** + * Question Link: https://leetcode.com/problems/evaluate-division/ + * Primary idea: Classic Union Find. Update roots and values while building the graph. + * + * Time Complexity: O((M + N) * logN), Space Complexity: O(N) + * + */ + +class EvaluateDivision { + func calcEquation(_ equations: [[String]], _ values: [Double], _ queries: [[String]]) -> [Double] { + var res = [Double]() + var graph = union(equations, values) + + for query in queries { + if graph[query[0]] == nil || graph[query[1]] == nil { + res.append(-1.0) + continue + } + + let left = find(&graph, query[0]) + let right = find(&graph, query[1]) + + if left.0 != right.0 { + res.append(-1.0) + } else { + res.append(left.1 / right.1) + } + } + + return res + } + + private func find(_ roots: inout [String: (String, Double)], _ node: String) -> (String, Double) { + if roots[node] == nil { + roots[node] = (node, 1) + } + + var n = roots[node]!.0 + + while n != roots[n]!.0 { + roots[node] = (roots[n]!.0, roots[n]!.1 * roots[node]!.1) + n = roots[n]!.0 + } + + return roots[node]! + } + + private func union(_ equations: [[String]], _ values: [Double]) -> [String: (String, Double)] { + var res = [String: (String, Double)]() + + for i in 0.. Date: Sat, 8 Oct 2022 19:57:03 -0700 Subject: [PATCH 65/75] Add a solution to Bus Routes --- BFS/BusRoutes.swift | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 BFS/BusRoutes.swift diff --git a/BFS/BusRoutes.swift b/BFS/BusRoutes.swift new file mode 100644 index 00000000..80fc5c25 --- /dev/null +++ b/BFS/BusRoutes.swift @@ -0,0 +1,58 @@ +/** + * Question Link: https://leetcode.com/problems/bus-routes/ + * Primary idea: BFS. Build a map for stop and related buses' indexes map. Use a queue to track until the current stop is equal to the target. + * + * Time Complexity: O(nm), Space Complexity: O(nm) + * + */ + +class BusRoutes { + func numBusesToDestination(_ routes: [[Int]], _ source: Int, _ target: Int) -> Int { + + if source == target { + return 0 + } + + let stopBusesMap = buildMap(routes) + + // bfs + var queue = [source], res = 0, isVisited = Set() + + while !queue.isEmpty { + let size = queue.count + + for _ in 0.. [Int: Set] { + var stopBusesMap = [Int: Set]() + + for (i, bus) in routes.enumerated() { + bus.forEach { + stopBusesMap[$0, default: Set()].insert(i) + } + } + + return stopBusesMap + } +} From 37bcbeeec9378c755fa06aeef456c5a48e124f97 Mon Sep 17 00:00:00 2001 From: Soap Date: Sun, 9 Oct 2022 14:12:02 -0700 Subject: [PATCH 66/75] Refactor solution to Next Permutation --- Array/NextPermutation.swift | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Array/NextPermutation.swift b/Array/NextPermutation.swift index 332f721a..4a76991e 100644 --- a/Array/NextPermutation.swift +++ b/Array/NextPermutation.swift @@ -9,36 +9,36 @@ class NextPermutation { func nextPermutation(_ nums: inout [Int]) { - guard let violateIndex = findViolate(nums) else { + guard let violateIdx = findViolate(nums) else { nums.reverse() return } - - swap(&nums, violateIndex, findLeastGreater(nums, violateIndex)) - nums = nums[0...violateIndex] + nums[(violateIndex + 1)...].reversed() + + swap(&nums, findFirstGreater(nums, violateIdx), violateIdx) + nums[(violateIdx + 1)...].reverse() + } + + private func findFirstGreater(_ nums: [Int], _ violateIdx: Int) -> Int { + for i in ((violateIdx + 1).. nums[violateIdx] { + return i + } + } + + return -1 } - - fileprivate func findViolate(_ nums: [Int]) -> Int? { + + private func findViolate(_ nums: [Int]) -> Int? { for i in (1.. nums[i - 1] { return i - 1 } } - + return nil } - - fileprivate func findLeastGreater(_ nums: [Int], _ violateIndex: Int) -> Int { - for i in (violateIndex + 1.. nums[violateIndex] { - return i - } - } - - fatalError() - } - - fileprivate func swap(_ nums: inout [T], _ indexL: Int, _ indexR: Int) { - (nums[indexL], nums[indexR]) = (nums[indexR], nums[indexL]) + + private func swap(_ nums: inout [Int], _ l: Int, _ r: Int) { + (nums[l], nums[r]) = (nums[r], nums[l]) } -} \ No newline at end of file +} From 220c9e08d4a01bddf189fc4f72cdc0caa44dd2be Mon Sep 17 00:00:00 2001 From: Soap Date: Sun, 9 Oct 2022 15:17:00 -0700 Subject: [PATCH 67/75] Add a solution to Next Palindrome Using Same Digits --- Array/NextPalindromeUsingSameDigits.swift | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Array/NextPalindromeUsingSameDigits.swift diff --git a/Array/NextPalindromeUsingSameDigits.swift b/Array/NextPalindromeUsingSameDigits.swift new file mode 100644 index 00000000..c3380b59 --- /dev/null +++ b/Array/NextPalindromeUsingSameDigits.swift @@ -0,0 +1,56 @@ +/** + * Question Link: https://leetcode.com/problems/next-palindrome-using-same-digits/ + * Primary idea: Figure out the first half's next permutation, then double it to the result. + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class NextPalindromeUsingSameDigits { + func nextPalindrome(_ num: String) -> String { + let num = Array(num) + var firstHalf = num[0.. Bool { + var violateIdx = -1 + + for i in (1.. nums[i - 1] { + violateIdx = i - 1 + break + } + } + + if violateIdx == -1 { + nums.reverse() + return false + } + + for i in ((violateIdx + 1).. nums[violateIdx] { + swap(&nums, i, violateIdx) + break + } + } + + nums[(violateIdx + 1)...].reverse() + return true + } + + private func swap(_ nums: inout [Int], _ l: Int, _ r: Int) { + (nums[l], nums[r]) = (nums[r], nums[l]) + } +} From fef1217e33b181491055c20c5a0ab8941a4ab2a6 Mon Sep 17 00:00:00 2001 From: Soap Date: Mon, 10 Oct 2022 11:53:36 -0700 Subject: [PATCH 68/75] Refactor code style to Longest Substring with At Most K Distinct Characters and Longest Substring with At Most Two Distinct Characters --- ...gestSubstringMostKDistinctCharacters.swift | 38 +++++++------- ...stSubstringMostTwoDistinctCharacters.swift | 50 +++++++++---------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/SlidingWindow/LongestSubstringMostKDistinctCharacters.swift b/SlidingWindow/LongestSubstringMostKDistinctCharacters.swift index 8c6e613b..addab4a0 100644 --- a/SlidingWindow/LongestSubstringMostKDistinctCharacters.swift +++ b/SlidingWindow/LongestSubstringMostKDistinctCharacters.swift @@ -13,29 +13,33 @@ class LongestSubstringMostKDistinctCharacters { guard k > 0 else { return 0 } - + + var charFreqMap = [Character: Int](), left = 0, res = 0 let s = Array(s) - var start = 0, longest = 0, charsFreq = [Character: Int]() - + for (i, char) in s.enumerated() { - if let freq = charsFreq[char] { - charsFreq[char] = freq + 1 + if let freq = charFreqMap[char] { + charFreqMap[char] = freq + 1 } else { - while charsFreq.count == k { - longest = max(i - start, longest) - - guard let freq = charsFreq[s[start]] else { + + // update res + res = max(i - left, res) + + // move left and window + while charFreqMap.count == k { + if let leftFreq = charFreqMap[s[left]] { + charFreqMap[s[left]] = leftFreq == 1 ? nil : leftFreq - 1 + left += 1 + } else { fatalError() } - charsFreq[s[start]] = freq == 1 ? nil : freq - 1 - - start += 1 } - - charsFreq[char] = 1 + + // update window for current char + charFreqMap[char] = 1 } } - - return max(longest, s.count - start) + + return max(res, s.count - left) } -} \ No newline at end of file +} diff --git a/SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift b/SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift index ec4537c0..6bf3fe2c 100644 --- a/SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift +++ b/SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift @@ -1,6 +1,6 @@ /** * Question Link: https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/ - * Primary idea: Slding window, use dictionary to check substring is valid or not, and + * Primary idea: Slding window. Use char freq map to check substring is valid or not, and note to handle the end of string edge case * * Time Complexity: O(n), Space Complexity: O(n) @@ -9,32 +9,32 @@ class LongestSubstringMostTwoDistinctCharacters { func lengthOfLongestSubstringTwoDistinct(_ s: String) -> Int { - var start = 0, longest = 0, charFreq = [Character: Int]() - let sChars = Array(s) - - for (i, char) in sChars.enumerated() { - if let freq = charFreq[char] { - charFreq[char] = freq + 1 + var charFreqMap = [Character: Int](), left = 0, res = 0 + let s = Array(s) + + for (i, char) in s.enumerated() { + if let freq = charFreqMap[char] { + charFreqMap[char] = freq + 1 } else { - if charFreq.count == 2 { - longest = max(longest, i - start) - - while charFreq.count == 2 { - let charStart = sChars[start] - charFreq[charStart]! -= 1 - - if charFreq[charStart] == 0 { - charFreq[charStart] = nil - } - - start += 1 - } + + // update res + res = max(i - left, res) + + // move left and window + while charFreqMap.count == 2 { + if let leftFreq = charFreqMap[s[left]] { + charFreqMap[s[left]] = leftFreq == 1 ? nil : leftFreq - 1 + left += 1 + } else { + fatalError() + } } - - charFreq[char] = 1 + + // update window for current char + charFreqMap[char] = 1 } } - - return max(longest, sChars.count - start) + + return max(res, s.count - left) } -} \ No newline at end of file +} From cc7ea56ca4862f66657538c98a13c15165919757 Mon Sep 17 00:00:00 2001 From: Soap Date: Tue, 11 Oct 2022 20:01:11 -0700 Subject: [PATCH 69/75] Add a solution to Squares of a Sorted Array --- Array/SquaresSortedArray.swift | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Array/SquaresSortedArray.swift diff --git a/Array/SquaresSortedArray.swift b/Array/SquaresSortedArray.swift new file mode 100644 index 00000000..e1f4b31b --- /dev/null +++ b/Array/SquaresSortedArray.swift @@ -0,0 +1,29 @@ +/** + * Question Link: https://leetcode.com/problems/squares-of-a-sorted-array/ + * Primary idea: Two pointers. Compare absolute value and assign the bigger one to the right most index of the result. + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class SquaresSortedArray { + func sortedSquares(_ nums: [Int]) -> [Int] { + var left = 0, right = nums.count - 1, res = Array(repeating: 0, count: nums.count) + var square = 0, idx = nums.count - 1 + + while left <= right { + + if abs(nums[left]) < abs(nums[right]) { + square = nums[right] + right -= 1 + } else { + square = nums[left] + left += 1 + } + + res[idx] = square * square + idx -= 1 + } + + return res + } +} From e7b7adce945435fe86fd8e4d942562e304f149e0 Mon Sep 17 00:00:00 2001 From: Soap Date: Mon, 17 Oct 2022 20:36:20 -0700 Subject: [PATCH 70/75] Add a solution to Bomb Enemy --- Array/BombEnemy.swift | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Array/BombEnemy.swift diff --git a/Array/BombEnemy.swift b/Array/BombEnemy.swift new file mode 100644 index 00000000..98234e7d --- /dev/null +++ b/Array/BombEnemy.swift @@ -0,0 +1,50 @@ +/** + * Question Link: https://leetcode.com/problems/bomb-enemy/ + * Primary idea: Greedy. Update the result only when there is wall or at the beginning. + * Time Complexity: O(mn), Space Complexity: O(n) + * + */ + +class BombEnemy { + func maxKilledEnemies(_ grid: [[Character]]) -> Int { + let m = grid.count, n = grid[0].count + var res = 0, rowHit = 0, colHit = Array(repeating: 0, count: n) + + for i in 0.. Date: Mon, 17 Oct 2022 21:06:51 -0700 Subject: [PATCH 71/75] Update README --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 041058e6..5a1a1102 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![Leetcode](./logo.png?style=centerme) ## Progress -[Problem Status](#problem-status) shows the latest progress to all 1000+ questions. Currently we have 323 completed solutions. Note: questions with ♥ mark means that you have to **Subscript to premium membership** of LeetCode to unlock them. +[Problem Status](#problem-status) shows the latest progress to all 1000+ questions. Currently we have 400+ completed solutions. Note: questions with ♥ mark means that you have to **Subscript to premium membership** of LeetCode to unlock them. ## Contributors @@ -157,6 +157,7 @@ [Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/)| [Swift](./LinkedList/MergeTwoSortedLists.swift)| Easy| O(n)| O(1)| [Merge k Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/)| [Swift](./LinkedList/MergeKSortedLists.swift)| Hard| O(mlogn)| O(1)| [Partition List](https://leetcode.com/problems/partition-list/)| [Swift](./LinkedList/PartitionList.swift)| Medium| O(n)| O(1)| +[Reverse Nodes in k-Group](https://leetcode.com/problems/reverse-nodes-in-k-group/)| [Swift](./LinkedList/ReverseNodesInKGroup.swift)| Hard| O(n)| O(1)| [LRU Cache](https://leetcode.com/problems/lru-cache/) | [Swift](./LinkedList/LRUCache.swift) | Hard| O(1)| O(1)| [LFU Cache](https://leetcode.com/problems/lfu-cache/) | [Swift](./LinkedList/LFUCache.swift) | Hard| O(1)| O(1)| @@ -245,6 +246,7 @@ [Paint House](https://leetcode.com/problems/paint-house/)| [Swift](./DP/PaintHouse.swift)| Easy| O(n)| O(n)| [Paint House II](https://leetcode.com/problems/paint-house-ii/)| [Swift](./DP/PaintHouseII.swift)| Hard| O(n)| O(1)| [Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/)| [Swift](./DP/LongestIncreasingSubsequence.swift)| Medium| O(nlogn)| O(n)| +[Longest Common Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/)| [Swift](./DP/LongestCommonSubsequence.swift)| Medium| O(mn)| O(1)| [Palindromic Substrings](https://leetcode.com/problems/palindromic-substrings/)| [Swift](./DP/PalindromicSubstrings.swift)| Medium| O(n^2)| O(n^2)| [Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/)| [Swift](./DP/LongestPalindromicSubstring.swift)| Medium| O(n^2)| O(n^2)| [Perfect Squares](https://leetcode.com/problems/perfect-squares/)| [Swift](./DP/PerfectSquares.swift)| Medium| O(n^2)| O(n)| @@ -253,6 +255,7 @@ [Paint Fence](https://leetcode.com/problems/paint-fence/)| [Swift](./DP/PaintFence.swift)| Easy| O(n)| O(n)| [Maximum Subarray](https://leetcode.com/problems/maximum-subarray/)| [Swift](./DP/MaximumSubarray.swift)| Medium| O(n)| O(1)| [Maximum Product Subarray](https://leetcode.com/problems/maximum-product-subarray/)| [Swift](./DP/MaximumProductSubarray.swift)| Medium| O(n)| O(1)| +[Maximum Number of Points with Cost](https://leetcode.com/problems/maximum-number-of-points-with-cost/)| [Swift](./DP/MaximumNumberPointsCost.swift)| Medium| O(mn)| O(1)| [Maximal Square](https://leetcode.com/problems/maximal-square/)| [Swift](./DP/MaximalSquare.swift)| Medium| O(mn)| O(mn)| [Edit Distance](https://leetcode.com/problems/edit-distance/)| [Swift](./DP/EditDistance.swift)| Hard| O(mn)| O(mn)| [Combination Sum IV](https://leetcode.com/problems/combination-sum-iv/)| [Swift](./DP/CombinationSumIV.swift)| Medium| O(2^n)| O(n)| @@ -415,6 +418,7 @@ ## Google | Title | Solution | Difficulty | Frequency | | ----- | -------- | ---------- | --------- | +[Race Car](https://leetcode.com/problems/race-car/)| [Swift](./BFS/RaceCar.swift)| Hard| ★★★★★★| [Plus One](https://leetcode.com/problems/plus-one/)| [Swift](./Math/PlusOne.swift)| Easy| ★★★★★★| [Number of Islands](https://leetcode.com/problems/number-of-islands/)| [Swift](./DFS/NumberofIslands.swift)| Medium| ★★★★| [Summary Ranges](https://leetcode.com/problems/summary-ranges/)| [Swift](./Array/SummaryRanges.swift)| Medium| ★★★★| @@ -878,7 +882,7 @@ | [Swift](./String/StrStr.swift) | 28 | [Implement strStr()](https://oj.leetcode.com/problems/implement-strstr/) | Easy | | [Swfit](./Array/RemoveElement.swift) | 27 | [Remove Element](https://oj.leetcode.com/problems/remove-element/) | Easy | | [Swift](./Array/RemoveDuplicatesFromSortedArray.swift) | 26 | [Remove Duplicates from Sorted Array](https://oj.leetcode.com/problems/remove-duplicates-from-sorted-array/) | Easy | -| | 25 | [Reverse Nodes in k-Group](https://oj.leetcode.com/problems/reverse-nodes-in-k-group/) | Hard | +| [Swift](./LinkedList/ReverseNodesInKGroup.swift) | 25 | [Reverse Nodes in k-Group](https://oj.leetcode.com/problems/reverse-nodes-in-k-group/) | Hard | | [Swift](./LinkedList/SwapNodesInPairs.swift) | 24 | [Swap Nodes in Pairs](https://oj.leetcode.com/problems/swap-nodes-in-pairs/) | Easy | | [Swift](./LinkedList/MergeKSortedLists.swift) | 23 | [Merge k Sorted Lists](https://oj.leetcode.com/problems/merge-k-sorted-lists/) | Hard | | [Swift](./Math/GenerateParentheses.swift) | 22 | [Generate Parentheses](https://oj.leetcode.com/problems/generate-parentheses/) | Medium | From e9253b85635988053c76b2801154d4bb033d9a23 Mon Sep 17 00:00:00 2001 From: Soap Date: Thu, 12 Jan 2023 20:58:28 -0800 Subject: [PATCH 72/75] Graph (#373) * Refactor code style for course schedule I & II * Delete redundant solutions * Refactor code style --- DFS/CourseSchedule.swift | 66 ------------------------------------ DFS/CourseScheduleII.swift | 64 ---------------------------------- Graph/CourseSchedule.swift | 14 +++----- Graph/CourseScheduleII.swift | 46 +++++++++++-------------- 4 files changed, 25 insertions(+), 165 deletions(-) delete mode 100644 DFS/CourseSchedule.swift delete mode 100644 DFS/CourseScheduleII.swift diff --git a/DFS/CourseSchedule.swift b/DFS/CourseSchedule.swift deleted file mode 100644 index 64aa7396..00000000 --- a/DFS/CourseSchedule.swift +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Question Link: https://leetcode.com/problems/course-schedule/ - * Primary idea: Kahn's Algorithms - * 1) Create the graph - * 2) Decorate each vertex with its in-degree - * 3) Create a set of all sources - * 4) While the set isn’t empty, - * i. Remove a vertex from the set and add it to the sorted list - * ii. For every edge from that vertex: - * - Decrement the in-degree of the destination node - * - Check all of its destination vertices and add them to the set if they have no incoming edges - * Time Complexity: O(|E| + |V|), Space Complexity: O(n^2) - * Recommand Reading: http://cs.brown.edu/courses/csci0160/lectures/14.pdf - */ - -class CourseSchedule { - func canFinish(_ numCourses: Int, _ prerequisites: [[Int]]) -> Bool { - // ATTENTION: if graph use [[Int]], will get 'memory limited exceed' - var graph = [[UInt8]](repeatElement([UInt8](repeatElement(0, count: numCourses)), count: numCourses)) - var indegree = [Int](repeatElement(0, count: numCourses)) - - // 1. Create the graph - for i in 0.. [Int] { - // ATTENTION: if graph use [[Int]], will get 'memory limited exceed' - var graph = [[UInt8]](repeatElement([UInt8](repeatElement(0, count: numCourses)), count: numCourses)) - var indegree = [Int](repeatElement(0, count: numCourses)) - - // 1. Create the graph - for i in 0.. [Int] { - var inDegrees = Array(repeating: 0, count: numCourses), fromTo = [Int: [Int]]() - var coursesCouldTake = [Int]() - - // init graph - for prerequisite in prerequisites { - fromTo[prerequisite[1], default: []].append(prerequisite[0]) - inDegrees[prerequisite[0]] += 1 + var indegrees = Array(repeating: 0, count: numCourses), toCourses = [Int: [Int]]() + + for pres in prerequisites { + indegrees[pres[0]] += 1 + toCourses[pres[1], default: []].append(pres[0]) } - - var queue = (0.. Date: Sat, 4 Nov 2023 11:22:00 -0700 Subject: [PATCH 73/75] Refactor solution to the Valid Palindrome II --- String/ValidPalindromeII.swift | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/String/ValidPalindromeII.swift b/String/ValidPalindromeII.swift index 39752e9f..8a18495e 100644 --- a/String/ValidPalindromeII.swift +++ b/String/ValidPalindromeII.swift @@ -1,38 +1,40 @@ /** * Question Link: https://leetcode.com/problems/valid-palindrome-ii/ - * Primary idea: Take advantage of validPalindrome, and jump left and right separately to get correct character should be deleted + * Primary idea: Two pointers. Move left and right when they are equal or cannot separate by moving either side, otherwise move one direction and update the flag. * - * Time Complexity: O(n), Space Complexity: O(n) + * Time Complexity: O(n), Space Complexity: O(1) * */ class ValidPalindromeII { func validPalindrome(_ s: String) -> Bool { + var i = 0, j = s.count - 1, isDeleted = false let s = Array(s) - return isValid(true, s) || isValid(false, s) - } - - private func isValid(_ skipLeft: Bool, _ s: [Character]) -> Bool { - var i = 0, j = s.count - 1, alreadySkipped = false - + while i < j { - if s[i] == s[j] { - i += 1 - j -= 1 - } else { - if alreadySkipped { + if s[i] != s[j] { + if isDeleted { return false } else { - alreadySkipped = true - if skipLeft { - i += 1 - } else { + if s[i + 1] == s [j] && s[j - 1] == s[i] { + i += 1 j -= 1 + } else if s[i + 1] == s[j] { + i += 1 + isDeleted = true + } else if s[j - 1] == s[i] { + j -= 1 + isDeleted = true + } else { + return false } } + } else { + i += 1 + j -= 1 } } - + return true } } From 54513f61aa9e268aabb9a2fab8e9055debb10f39 Mon Sep 17 00:00:00 2001 From: Soap Date: Fri, 9 Feb 2024 12:08:00 +0800 Subject: [PATCH 74/75] Refactor solutions for Valid Parentheses and Insert Interval --- Sort/InsertInterval.swift | 42 +++++++++++++++++++----------------- Stack/ValidParentheses.swift | 21 ++++++++++-------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/Sort/InsertInterval.swift b/Sort/InsertInterval.swift index 97a19a83..8b3e85e7 100644 --- a/Sort/InsertInterval.swift +++ b/Sort/InsertInterval.swift @@ -21,27 +21,29 @@ class InsertInterval { func insert(_ intervals: [Interval], _ newInterval: Interval) -> [Interval] { - var index = 0 - var result: [Interval] = [] - var tempInterval = Interval(newInterval.start, newInterval.end) - - while index < intervals.count && newInterval.start > intervals[index].end { - result.append(intervals[index]) - index += 1 - } - - while index < intervals.count && newInterval.end >= intervals[index].start { - let minStart = min(tempInterval.start, intervals[index].start) - let maxEnd = max(tempInterval.end, intervals[index].end) - tempInterval = Interval(minStart, maxEnd) - index += 1 + var res = [Interval](), insertIdx = 0 + + for (i, interval) in intervals.enumerated() { + if interval.isOverlap(with: newInterval) { + newInterval.start = min(newInterval.start, interval.start) + newInterval.end = max(newInterval.end, interval.end) + } else { + if interval.end < newInterval.start { + insertIdx += 1 + } + + res.append(interval) + } } - result.append(tempInterval) - - for i in index ..< intervals.count { - result.append(intervals[i]) + + res.insert(newInterval, at: insertIdx) + + return res + } + + extension Interval { + func isOverlap(with interval: Interval) -> Bool { + return start <= interval.end && end >= interval.start } - - return result } } diff --git a/Stack/ValidParentheses.swift b/Stack/ValidParentheses.swift index 302ee9f2..5ac0bf14 100644 --- a/Stack/ValidParentheses.swift +++ b/Stack/ValidParentheses.swift @@ -7,25 +7,28 @@ class ValidParentheses { func isValid(_ s: String) -> Bool { var stack = [Character]() - + for char in s { - if char == "(" || char == "[" || char == "{" { + switch char { + case "(", "[", "{": stack.append(char) - } else if char == ")" { - guard stack.count != 0 && stack.removeLast() == "(" else { + case ")": + if stack.popLast() != "(" { return false } - } else if char == "]" { - guard stack.count != 0 && stack.removeLast() == "[" else { + case "]": + if stack.popLast() != "[" { return false } - } else if char == "}" { - guard stack.count != 0 && stack.removeLast() == "{" else { + case "}": + if stack.popLast() != "{" { return false } + default: + continue } } - + return stack.isEmpty } } \ No newline at end of file From d4aec0f4fc03ca4013a32f8229d049b7a0234532 Mon Sep 17 00:00:00 2001 From: Soap Date: Fri, 9 Feb 2024 14:09:06 +0800 Subject: [PATCH 75/75] [Sort] Update solution for the merge intervals --- Sort/MergeIntervals.swift | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/Sort/MergeIntervals.swift b/Sort/MergeIntervals.swift index b83f339e..dc558a34 100644 --- a/Sort/MergeIntervals.swift +++ b/Sort/MergeIntervals.swift @@ -15,30 +15,21 @@ */ class MergeIntervals { - func merge(intervals: [Interval]) -> [Interval] { - var result = [Interval]() - - let intervals = intervals.sorted { - if $0.start != $1.start { - return $0.start < $1.start - } else { - return $0.end < $1.end - } - } - - for interval in intervals { - guard let last = result.last else { - result.append(interval) - continue - } + func merge(_ intervals: [[Int]]) -> [[Int]] { + let intervals = intervals.sorted { return $0[0] < $1[0] } + var res = [intervals[0]] + + for interval in intervals[1..