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.. Bool { + var maxFlowersCouldPlant = 0, flowerbed = flowerbed + + for i in 0..= n { + return true + } + } + + return maxFlowersCouldPlant >= n + } +} \ No newline at end of file diff --git a/Array/ContainsDuplicateII.swift b/Array/ContainsDuplicateII.swift index 1bca20f6..12b126d2 100644 --- a/Array/ContainsDuplicateII.swift +++ b/Array/ContainsDuplicateII.swift @@ -7,24 +7,21 @@ */ class ContainsDuplicateII { - func containsNearbyDuplicate(nums: [Int], _ k: Int) -> Bool { - // edge case - if nums.count <= 1 { + func containsNearbyDuplicate(_ nums: [Int], _ k: Int) -> Bool { + guard nums.count > 1 else { return false } - // key: nums[index], value: index - var dict = [Int: Int]() + var numToLastIndex = [Int: Int]() - for i in 0.. [Int] { + var nums = nums + var result = [Int]() + + for i in 0.. 0 { + nums[index] = -nums[index] + } + } + + for i in 0.. 0 { + result.append(i+1) + } + } + + return result + } +} diff --git a/Array/FirstMissingPositive.swift b/Array/FirstMissingPositive.swift index 03c5e60a..c5a04e35 100644 --- a/Array/FirstMissingPositive.swift +++ b/Array/FirstMissingPositive.swift @@ -7,9 +7,7 @@ class FirstMissingPositive { func firstMissingPositive(_ nums: [Int]) -> Int { - var set = Set() - - nums.forEach { set.insert($0) } + let set = Set(nums) for i in 0.. Bool { + var smallest = Int.max, smaller = Int.max + + for num in nums { + if smallest >= num { + smallest = num + } else if smaller >= num { + smaller = num + } else { + return true + } + } + + return false + } +} \ No newline at end of file diff --git a/Array/IntersectionTwoArrays.swift b/Array/IntersectionTwoArrays.swift index 65a6bea6..f1caacfd 100644 --- a/Array/IntersectionTwoArrays.swift +++ b/Array/IntersectionTwoArrays.swift @@ -1,13 +1,23 @@ /** * Question Link: https://leetcode.com/problems/intersection-of-two-arrays/ - * Primary idea: Use set interact function to help + * Primary idea: Use set to hold numbers for one array and iterate the other one to output result, + * remove the number from set to avoid duplicates. + * + * Note: Do not use built-in intersection function for Set in Swift, that is not this question is asking for. * * Time Complexity: O(n), Space Complexity: O(n) * */ class IntersectionTwoArrays { - func intersection(nums1: [Int], _ nums2: [Int]) -> [Int] { - return [Int](Set(nums1).intersect(nums2)) + func intersection(_ nums1: [Int], _ nums2: [Int]) -> [Int] { + var set = Set(nums1), res = [Int]() + + for num in nums2 where set.contains(num) { + res.append(num) + set.remove(num) + } + + return res } -} \ No newline at end of file +} diff --git a/Array/IntersectionTwoArraysII.swift b/Array/IntersectionTwoArraysII.swift index 739caeda..a5ec9159 100644 --- a/Array/IntersectionTwoArraysII.swift +++ b/Array/IntersectionTwoArraysII.swift @@ -10,20 +10,16 @@ class IntersectionTwoArraysII { func intersect(_ nums1: [Int], _ nums2: [Int]) -> [Int] { - var frequencies = Dictionary(nums1.map { ($0, 1) } , uniquingKeysWith: +) + var numsFreq = Dictionary(nums1.map { ($0, 1) }, uniquingKeysWith: +) var res = [Int]() for num in nums2 { - guard let frequent = frequencies[num] else { - continue - } - - if frequent > 0 { - frequencies[num]! = frequent - 1 + if let freq = numsFreq[num], freq > 0 { res.append(num) + numsFreq[num] = freq - 1 } } return res } -} \ No newline at end of file +} diff --git a/Array/LongestConsecutiveSequence.swift b/Array/LongestConsecutiveSequence.swift index 9c52c0e1..3c79709f 100644 --- a/Array/LongestConsecutiveSequence.swift +++ b/Array/LongestConsecutiveSequence.swift @@ -8,28 +8,26 @@ class LongestConsecutiveSequence { func longestConsecutive(_ nums: [Int]) -> Int { - var set = Set(nums), longest = 0 + var set = Set(nums), longest = 0 for num in nums { - if set.contains(num) { - set.remove(num) - let distance = 1 + findConsecutive(&set, num, 1) + findConsecutive(&set, num, -1) - longest = max(longest, distance) - } - } + var currentLength = 1 + dfs(num, &set, &longest, ¤tLength) + } return longest } - fileprivate func findConsecutive(_ set: inout Set, _ num: Int, _ step: Int) -> Int { - var len = 0, num = num + step - - while set.contains(num) { - set.remove(num) - len += 1 - num += step + private func dfs(_ num: Int, _ set: inout Set, _ longest: inout Int, _ length: inout Int) { + if !set.contains(num) { + return } - return len + longest = max(longest, length) + set.remove(num) + length += 1 + + dfs(num + 1, &set, &longest, &length) + dfs(num - 1, &set, &longest, &length) } } \ No newline at end of file diff --git a/Array/MajorityElement.swift b/Array/MajorityElement.swift index 8185b372..39b42e9d 100644 --- a/Array/MajorityElement.swift +++ b/Array/MajorityElement.swift @@ -8,22 +8,16 @@ class MajorityElement { func majorityElement(_ nums: [Int]) -> Int { - var major = nums.first! - var sum = 0 + var count = 0, candidate = 0 for num in nums { - if num == major { - sum += 1 - } else { - sum -= 1 - } + if count == 0 { + candidate = num + } - if sum == 0 { - major = num - sum = 1 - } + count += (candidate == num) ? 1 : -1 } - return major + return candidate } -} \ No newline at end of file +} diff --git a/Array/MinimumRemoveMakeValidParentheses.swift b/Array/MinimumRemoveMakeValidParentheses.swift new file mode 100644 index 00000000..230bace3 --- /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) + } +} diff --git a/Array/MinimumSizeSubarraySum.swift b/Array/MinimumSizeSubarraySum.swift index a9051835..538c3230 100644 --- a/Array/MinimumSizeSubarraySum.swift +++ b/Array/MinimumSizeSubarraySum.swift @@ -1,7 +1,7 @@ /** * Question Link: https://leetcode.com/problems/minimum-size-subarray-sum/ * Primary idea: Two Pointers, anchor the former and move forward the latter one to ensure the sum of subarray just covers the target - * Note: There could be no invalid subarray which sum >= target + * Note: There could be no valid subarray which sum >= target * Time Complexity: O(n), Space Complexity: O(1) * */ @@ -13,7 +13,7 @@ for (i, num) in nums.enumerated() { currentSum += num - while currentSum >= s && start <= i { + while currentSum >= s, start <= i { miniSize = min(miniSize, i - start + 1) currentSum -= nums[start] @@ -23,4 +23,4 @@ return miniSize == Int.max ? 0 : miniSize } -} \ No newline at end of file +} diff --git a/Array/MissingRanges.swift b/Array/MissingRanges.swift new file mode 100644 index 00000000..ff3f2783 --- /dev/null +++ b/Array/MissingRanges.swift @@ -0,0 +1,38 @@ +/** + * Question Link: https://leetcode.com/problems/missing-ranges/ + * Primary idea: Scan the array and compare each element with previous one and generate corresponding ranges + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class MissingRanges { + func findMissingRanges(_ nums: [Int], _ lower: Int, _ upper: Int) -> [String] { + var res = [String]() + + guard !nums.isEmpty else { + addRange(&res, lower, upper) + return res + } + + addRange(&res, lower, nums[0] - 1) + + for i in 1.. end { + return + } else if start == end { + res.append("\(end)") + } else { + res.append("\(start)->\(end)") + } + } +} \ No newline at end of file diff --git a/Array/MoveZeroes.swift b/Array/MoveZeroes.swift index 2c66eee6..2354e18d 100644 --- a/Array/MoveZeroes.swift +++ b/Array/MoveZeroes.swift @@ -8,18 +8,16 @@ class MoveZeroes { func moveZeroes(_ nums: inout [Int]) { - var idx = 0 + var nonZeroIdx = 0 - for (i, num) in nums.enumerated() { - if num != 0 { - nums[idx] = num - idx += 1 - } + for num in nums where num != 0 { + nums[nonZeroIdx] = num + nonZeroIdx += 1 } - while idx < nums.count { - nums[idx] = 0 - idx += 1 + while nonZeroIdx < nums.count { + nums[nonZeroIdx] = 0 + nonZeroIdx += 1 } } } \ No newline at end of file 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]) + } +} 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 +} 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.. [Int] { - var res = Array(repeating: 1, count: nums.count) - var right = 1 - - guard nums.count > 0 else { - return res - } - - for i in 1.. [Int] { + var zeroCount = 0 + let total = nums.reduce(1) { + if $1 == 0 { + zeroCount += 1 + } + return $0 * ($1 == 0 ? 1 : $1) + } + if zeroCount > 1 {return nums.map({_ in return 0})} + return nums.map({ + if zeroCount == 1 { + return ($0 == 0 ? total : 0) + } else { + return ($0 == 0 ? total : total / $0) + } + }) + } +} + + + + +/** +* Question Link: https://leetcode.com/problems/product-of-array-except-self/ +* Primary idea: Dynamic Programming, track Left and Right product lists at the same time and just use one additional array.The problem statement mentions that using theanswer array doesn't add to the space complexity. +* Time Complexity: O(n), Space Complexity: O(1) +*/ + + +class ProductExceptSelfNotUseDivide{ + + func productExceptSelf(_ nums: [Int]) -> [Int] { + + var arr = Array.init(repeating: 1, count: nums.count) + + for i in (0..<(nums.count - 1)).reversed() { + + arr[i] = arr[i + 1] * nums[i + 1] + + } + + var left = 1 + for i in 0.. 0 && k > 0 else { + return + } + let k = k % nums.count + guard k != 0 else { + return + } _reverse(&nums, 0, nums.count - 1) _reverse(&nums, 0, k - 1) _reverse(&nums, k, nums.count - 1) @@ -36,4 +41,4 @@ class RotateArray { private func _swap(_ nums: inout Array, _ p: Int, _ q: Int) { (nums[p], nums[q]) = (nums[q], nums[p]) } -} \ No newline at end of file +} diff --git a/Array/ShortestWordDistance.swift b/Array/ShortestWordDistance.swift index 5249a8b5..3a384a06 100644 --- a/Array/ShortestWordDistance.swift +++ b/Array/ShortestWordDistance.swift @@ -6,21 +6,24 @@ class ShortestWordDistance { func shortestDistance(_ words: [String], _ word1: String, _ word2: String) -> Int { - var distance = Int.max - var firstIndex = -1, secondIndex = -1 + var minDistance = Int.max, word1Idx = -1, word2Idx = -1 for (i, word) in words.enumerated() { if word == word1 { - firstIndex = i - } - if word == word2 { - secondIndex = i - } - if firstIndex != -1 && secondIndex != -1 { - distance = min(distance, abs(firstIndex - secondIndex)) + word1Idx = i + + if word2Idx != -1 { + minDistance = min(i - word2Idx, minDistance) + } + } else if word == word2 { + word2Idx = i + + if word1Idx != -1 { + minDistance = min(i - word1Idx, minDistance) + } } } - return distance + return minDistance } } \ No newline at end of file diff --git a/Array/ShortestWordDistanceII.swift b/Array/ShortestWordDistanceII.swift new file mode 100644 index 00000000..b2bfe6ee --- /dev/null +++ b/Array/ShortestWordDistanceII.swift @@ -0,0 +1,38 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-word-distance/ + * Primary idea: Save indexes of all words and use two pointers to get the shortest distance. + * Time Complexity: O(n), Space Complexity: O(n) + */ + + class WordDistance { + + var wordToIdx: [String: [Int]] + + init(_ words: [String]) { + wordToIdx = [String: [Int]]() + + for (idx, word) in words.enumerated() { + wordToIdx[word, default:[]].append(idx) + } + } + + func shortest(_ word1: String, _ word2: String) -> Int { + guard let idxes1 = wordToIdx[word1], let idxes2 = wordToIdx[word2] else { + fatalError("Invalid Input") + } + + var i = 0, j = 0, minDistance = Int.max + + while i < idxes1.count && j < idxes2.count { + minDistance = min(abs(idxes1[i] - idxes2[j]), minDistance) + + if idxes1[i] < idxes2[j] { + i += 1 + } else { + j += 1 + } + } + + return minDistance + } +} \ No newline at end of file diff --git a/Array/SortArrayByParity.swift b/Array/SortArrayByParity.swift new file mode 100644 index 00000000..060a1b01 --- /dev/null +++ b/Array/SortArrayByParity.swift @@ -0,0 +1,16 @@ +/** + * Question Link: https://leetcode.com/problems/sort-array-by-parity/ + * Primary idea: traverse the array and insert Even into the 0 th index and odd into the last index + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class SortArrayByParity { + func sortArrayByParity(_ A: [Int]) -> [Int] { + return A.enumerated().reduce(into: [Int]()) { (acc, arg) in + let (_, value) = arg + acc.insert(value, at: value.isMultiple(of: 2) ? 0 : acc.count) + } + } +} diff --git a/Array/SpiralMatrix.swift b/Array/SpiralMatrix.swift index b1522a80..a68eb939 100644 --- a/Array/SpiralMatrix.swift +++ b/Array/SpiralMatrix.swift @@ -6,7 +6,7 @@ */ class SpiralMatrix { - func spiralOrder(matrix: [[Int]]) -> [Int] { + func spiralOrder(_ matrix: [[Int]]) -> [Int] { var res = [Int]() guard matrix.count != 0 else { @@ -38,7 +38,7 @@ class SpiralMatrix { } // bottom - for i in endY.stride(through: startY, by: -1) { + for i in stride(from: endY, through: startY, by: -1) { res.append(matrix[endX][i]) } endX -= 1 @@ -47,7 +47,7 @@ class SpiralMatrix { } // left - for i in endX.stride(through: startX, by: -1) { + for i in stride(from: endX, through: startX, by: -1) { res.append(matrix[i][startY]) } startY += 1 @@ -58,4 +58,4 @@ class SpiralMatrix { return res } -} \ No newline at end of file +} 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 + } +} diff --git a/Array/StrobogrammaticNumber.swift b/Array/StrobogrammaticNumber.swift index 0505df77..c655a78c 100644 --- a/Array/StrobogrammaticNumber.swift +++ b/Array/StrobogrammaticNumber.swift @@ -24,14 +24,14 @@ } fileprivate func isValid(_ charA: Character, _ charB: Character) -> Bool { - if charA == charB { - return ["0", "1", "8"].contains(charA) + guard let digitA = Int(String(charA)), let digitB = Int(String(charB)) else { + fatalError("Invalid input") + } + + if let mirrorA = mirrorDigits[digitA], mirrorA == digitB { + return true } else { - if (charA == "6" && charB == "9") || (charA == "9" && charB == "6") { - return true - } else { - return false - } + return false } } } diff --git a/Array/TaskScheduler.swift b/Array/TaskScheduler.swift index fd110843..1ab3a7fd 100644 --- a/Array/TaskScheduler.swift +++ b/Array/TaskScheduler.swift @@ -1,6 +1,6 @@ /** * Question Link: https://leetcode.com/problems/task-scheduler/ - * Primary idea: Most frequent character should be put at head of each chunk and join the chunks with less frequent one. + * Primary idea: Most frequent character should be put at head of each cycle and join the chunks with less frequent one. * * Time Complexity: O(nlogn), Space Complexity: O(n) * @@ -16,6 +16,7 @@ let sortedTasks = taskFreqs.keys.sorted { return taskFreqs[$0]! > taskFreqs[$1]! } var mostFreqCount = 0 + // get interval number for last cycle for sortedTask in sortedTasks { if taskFreqs[sortedTask] != taskFreqs[sortedTasks[0]] { break @@ -24,6 +25,8 @@ mostFreqCount += 1 } + // when number of different tasks is greater than n + 1, and the most freqent task won't cause idle run, + // then we should return tasks.count return max(tasks.count, (taskFreqs[sortedTasks[0]]! - 1) * (n + 1) + mostFreqCount) } -} \ No newline at end of file +} diff --git a/Array/ThreeSum.swift b/Array/ThreeSum.swift index 8c05de26..d9ef51d0 100644 --- a/Array/ThreeSum.swift +++ b/Array/ThreeSum.swift @@ -6,42 +6,38 @@ */ class ThreeSum { - func threeSum(nums: [Int]) -> [[Int]] { - var nums = nums.sorted(by: <) + func threeSum(_ nums: [Int]) -> [[Int]] { var res = [[Int]]() - if nums.count <= 2 { + guard nums.count >= 3 else { return res } - for i in 0...nums.count - 3 { - if i == 0 || nums[i] != nums[i - 1] { - var remain = -nums[i] - var left = i + 1 - var right = nums.count - 1 - while left < right { - if nums[left] + nums[right] == remain { - var temp = [Int]() - temp.append(nums[i]) - temp.append(nums[left]) - temp.append(nums[right]) - - res.append(temp) - repeat { - left += 1 - } while (left < right && nums[left] == nums[left - 1]) - repeat { - right -= 1 - } while (left < right && nums[right] == nums[right + 1]) - } else if nums[left] + nums[right] < remain { - repeat { - left += 1 - } while (left < right && nums[left] == nums[left - 1]) - } else { - repeat { - right -= 1 - } while (left < right && nums[right] == nums[right + 1]) - } + let nums = nums.sorted() + + for i in 0.. 0 && nums[i] == nums[i - 1] { + continue + } + + let firstNum = nums[i], remainingSum = -firstNum + var m = i + 1, n = nums.count - 1 + + while m < n { + if nums[m] + nums[n] == remainingSum { + res.append([firstNum, nums[m], nums[n]]) + + repeat { + m += 1 + } while nums[m] == nums[m - 1] && m < n + + repeat { + n -= 1 + } while nums[n] == nums[n + 1] && m < n + } else if nums[m] + nums[n] < remainingSum { + m += 1 + } else { + n -= 1 } } } diff --git a/Array/TwoSumII.swift b/Array/TwoSumII.swift new file mode 100644 index 00000000..9d9feed1 --- /dev/null +++ b/Array/TwoSumII.swift @@ -0,0 +1,28 @@ +/** + * Question Link: https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/ + * Primary idea: Two pointers -- left moves forward and right moves backward to meet the right two sum. + * + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class TwoSumII { + func twoSum(_ numbers: [Int], _ target: Int) -> [Int] { + if numbers.count <= 1 { + return [Int]() + } + + var left = 0, right = numbers.count - 1 + + while left < right { + if numbers[left] + numbers[right] < target { + left += 1 + } else if numbers[left] + numbers[right] > target { + right -= 1 + } else { + return [left + 1, right + 1] + } + } + + return [Int]() + } +} \ No newline at end of file diff --git a/Array/TwoSumIII.swift b/Array/TwoSumIII.swift new file mode 100644 index 00000000..d89715c6 --- /dev/null +++ b/Array/TwoSumIII.swift @@ -0,0 +1,33 @@ +/** + * Question Link: https://leetcode.com/problems/two-sum-iii-data-structure-design/ + * + * Note: This answer offers a different solution instead of the one requsted by leetcode. + * Assuming the use case is find API is called many more times than add API. + * For the answer accepted by leetcode, you could reference Two Sum and Two Sum II. + * + * Primary idea: Use a set for all two sums value, and array to keep all numbers added. + * Time Complexity: add - O(n), find - O(1), Space Complexity: O(n) + */ + +class TwoSumIII { + + var nums: [Int] + var twoSums: Set() + + /** Initialize your data structure here. */ + init() { + nums = [Int]() + twoSums = Set() + } + + /** Add the number to an internal data structure.. */ + func add(_ number: Int) { + nums.forEach { twoSums.insert($0 + number) } + nums.append(number) + } + + /** Find if there exists any pair of numbers which sum is equal to the value. */ + func find(_ value: Int) -> Bool { + return twoSums.contains(value) + } +} \ No newline at end of file diff --git a/Array/TwoSumLessThanK.swift b/Array/TwoSumLessThanK.swift new file mode 100644 index 00000000..319f2b9c --- /dev/null +++ b/Array/TwoSumLessThanK.swift @@ -0,0 +1,28 @@ +/** + * Question Link: https://leetcode.com/problems/two-sum-less-than-k/ + * Primary idea: Sort the arry and use two pointers to get the closest maximum value. + * + * Note: Directly using two points and update values correspondly to try to solve the + * problem with O(n) time complexity does not work -- it has too many edge cases. + * + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class TwoSumLessThanK { + func twoSumLessThanK(_ A: [Int], _ K: Int) -> Int { + let sortedA = A.sorted() + var left = 0, right = sortedA.count - 1 + var closest = -1 + + while left < right { + if sortedA[left] + sortedA[right] < K { + closest = max(sortedA[left] + sortedA[right], closest) + left += 1 + } else { + right -= 1 + } + } + + return closest + } +} \ No newline at end of file diff --git a/Array/ValidSudoku.swift b/Array/ValidSudoku.swift index fc738628..13c5bfd7 100644 --- a/Array/ValidSudoku.swift +++ b/Array/ValidSudoku.swift @@ -6,54 +6,35 @@ */ class ValidSudoku { - let size = 9 - - func isValidSudoku(board: [[Character]]) -> Bool { - return _isRowValid(board) && _isColValid(board) && _isSquareValid(board) - } - - private func _isRowValid(board: [[Character]]) -> Bool { - var visited = Array(count: size, repeatedValue: false) + func isValidSudoku(_ board: [[Character]]) -> Bool { + let len = 9 - for i in 0..(), count: len) + var colSet = Array(repeating: Set(), count: len) + var boxSet = Array(repeating: Set(), count: len) - return true - } - - private func _isColValid(board: [[Character]]) -> Bool { - var visited = Array(count: size, repeatedValue: false) - - for i in 0.. Bool { - var visited = Array(count: size, repeatedValue: false) - - for i in 0.stride(to: size, by: 3) { - for j in 0.stride(to: size, by: 3) { - visited = Array(count: size, repeatedValue: false) - for m in i.. Bool { - let current = String(char) - - if current != "." { - if let num = Int(current){ - if num < 1 || num > 9 || visited[num - 1] { - return false - } else { - visited[num - 1] = true - } - } else { - return false - } - } - - return true + private func isValid(_ set: inout Set, _ char: Character) -> Bool { + if set.contains(char) { + return false + } else { + set.insert(char) + return true + } } } \ No newline at end of file diff --git a/Array/ValidateIPAddress.swift b/Array/ValidateIPAddress.swift new file mode 100644 index 00000000..2f44f3a4 --- /dev/null +++ b/Array/ValidateIPAddress.swift @@ -0,0 +1,94 @@ +/** + * Question Link: https://leetcode.com/problems/validate-ip-address/ + * Primary idea: Determine whether they are iPv4 or iPv6, then handle them separately + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class ValidateIPAddress { + + let IPv4 = "IPv4" + let IPv4Separator = Character(".") + let IPv6 = "IPv6" + let IPv6Separator = Character(":") + let InvalidIP = "Neither" + + func validIPAddress(_ IP: String) -> String { + // detect IPv4 or IPv6 + guard let isIPv4 = isPotentialIPv4(IP) else { + return InvalidIP + } + + return isIPv4 ? isValidIPv4(IP) : isValidIPv6(IP) + } + + private func isPotentialIPv4(_ IP: String) -> Bool? { + let isIPv4 = IP.contains(IPv4Separator), isIPv6 = IP.contains(IPv6Separator) + + if isIPv4 || isIPv6 { + return isIPv4 + } else { + return nil + } + } + + private func isValidIPv4(_ IP: String) -> String { + if IP.contains("\(IPv4Separator)\(IPv4Separator)") || IP.first == IPv4Separator || IP.last == IPv4Separator { + return InvalidIP + } + + let numbers = IP.split(separator: IPv4Separator) + + guard numbers.count == 4 else { + return InvalidIP + } + + for number in numbers { + guard let num = Int(number) else { + return InvalidIP + } + + guard let first = number.first, first.isNumber else { + return InvalidIP + } + + if first == "0", number.count > 1 { + return InvalidIP + } + if num < 0 || num > 255 { + return InvalidIP + } + } + + return IPv4 + } + + private func isValidIPv6(_ IP: String) -> String { + if IP.contains("\(IPv6Separator)\(IPv6Separator)") || IP.first == IPv6Separator || IP.last == IPv6Separator { + return InvalidIP + } + + let numbers = IP.split(separator: IPv6Separator) + + guard numbers.count == 8 else { + return InvalidIP + } + + for number in numbers { + let number = number.lowercased() + + if number.count > 4 { + return InvalidIP + } + + for char in number { + if !char.isHexDigit { + return InvalidIP + } + } + } + + return IPv6 + } +} \ No newline at end of file diff --git a/Array/VerifyingAlienDictionary.swift b/Array/VerifyingAlienDictionary.swift new file mode 100644 index 00000000..f571654f --- /dev/null +++ b/Array/VerifyingAlienDictionary.swift @@ -0,0 +1,40 @@ +/** + * Question Link: https://leetcode.com/problems/verifying-an-alien-dictionary/ + * Primary idea: Compare words one by one and make sure they are following the order + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + + class VerifyingAlienDictionary { + func isAlienSorted(_ words: [String], _ order: String) -> Bool { + let charToOrder = Dictionary(uniqueKeysWithValues: order.enumerated().map { ($0.1, $0.0) }) + + for i in 0.. charToOrder[nextChar]! { + return false + } else { + break + } + } + + // edge case: "apple" vs. "app"; "kuvp" vs. "q" + if j == nextWord.count && currentWord.count > nextWord.count { + return false + } + } + + return true + } +} \ No newline at end of file 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 + } +} diff --git a/BFS/EvaluateDivision.swift b/BFS/EvaluateDivision.swift index bb920fc9..d24d1c1a 100644 --- a/BFS/EvaluateDivision.swift +++ b/BFS/EvaluateDivision.swift @@ -75,9 +75,7 @@ class EvaluateDivision { qStrs.append(str) qVals.append(currentVal * val) } - - - rest.append(-1.0) } + rest.append(-1.0) } } \ No newline at end of file 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 + } +} diff --git a/BFS/ShortestPathGetFood.swift b/BFS/ShortestPathGetFood.swift new file mode 100644 index 00000000..7f76035e --- /dev/null +++ b/BFS/ShortestPathGetFood.swift @@ -0,0 +1,62 @@ +/** + * 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 = [(start.0, start.1)], count = 0 + + while !queue.isEmpty { + + let size = queue.count + + 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)) + } + } + + count += 1 + } + + return -1 + } + + private func findStart(_ grid: [[Character]]) -> (Int, Int) { + for i in 0.. Int { + let m = grid.count, n = grid[0].count + var remaining = Array(repeating: Array(repeating: -1, count: n), count: m) + var queue = [Point(i: 0, j: 0, remain: k)], count = 0 + + if m + n - 2 < k { + return m + n - 2 + } + + while !queue.isEmpty { + let size = queue.count + + for _ in 0..= 0 && x < m && y >= 0 && y < n else { + continue + } + + if grid[x][y] == 1 && point.remain == 0 { + continue + } + + let nextRemaining = grid[x][y] == 1 ? point.remain - 1 : point.remain + + if remaining[x][y] >= nextRemaining { + continue + } + + remaining[x][y] = nextRemaining + queue.append(Point(i: x, j: y, remain: nextRemaining)) + + } + } + + count += 1 + } + + return -1 + } + + struct Point { + let i: Int + let j: Int + let remain: Int + } +} \ No newline at end of file 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.. Int { + var wordSet = Set(wordList), wordQueue = [beginWord], count = 1 + + while !wordQueue.isEmpty { + + let size = wordQueue.count + + for _ in 0..) -> [String] { + var res = [String]() + + // change character at every offset of the word + for i in 0.. 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) + } +} diff --git a/DFS/CombinationSum.swift b/DFS/CombinationSum.swift index 3ad3aa92..ac076d1b 100644 --- a/DFS/CombinationSum.swift +++ b/DFS/CombinationSum.swift @@ -2,23 +2,22 @@ * 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) * */ class CombinationSum { - func combinationSum(candidates: [Int], _ target: Int) -> [[Int]] { - var res = [[Int]]() - var path = [Int]() + func combinationSum(_ candidates: [Int], _ target: Int) -> [[Int]] { + var combination = [Int](), combinations = [[Int]]() - _dfs(candidates.sorted(by: <), target, &res, &path, 0) + dfs(candidates.sorted(), target, 0, &combinations, &combination) - return res + return combinations } - private func _dfs(candidates: [Int], _ target: Int, inout _ res: [[Int]], inout _ path: [Int], _ index: Int) { + private func dfs(_ candidates: [Int], _ target: Int, _ index: Int, _ combinations: inout [[Int]], _ combination: inout [Int]) { if target == 0 { - res.append(Array(path)) + combinations.append(combination) return } @@ -27,9 +26,9 @@ class CombinationSum { break } - path.append(candidates[i]) - _dfs(candidates, target - candidates[i], &res, &path, i) - path.removeLast() + combination.append(candidates[i]) + dfs(candidates, target - candidates[i], i, &combinations, &combination) + combination.removeLast() } } } \ No newline at end of file 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 a8b17aee..4e01de52 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(2^n), Space Complexity: O(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.. 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.. [String] { var res = [String]() - - guard num.count > 0 else { - return res - } - - dfs(&res, "", num, target, 0, 0, 0) - + + dfs(Array(num), 0, target, 0, 0, &res, "") + return res } - private func dfs(_ res: inout [String], _ temp: String, _ num: String, _ target: Int, _ pos: Int, _ eval: Int, _ mul: Int) { - if pos == num.count { + private func dfs(_ nums: [Character], _ index: Int, _ target: Int, _ eval: Int, _ mul: Int, _ res: inout [String], _ candidate: String) { + if index == nums.count { if eval == target { - res.append(temp) + res.append(candidate) } + return } - - for i in pos.. [] + if i != index && nums[index] == "0" { break } - let curt = Int(num[pos.. String { - get { - assert(index < self.count) - return String(Array(self.characters)[index]) - } - } - - subscript(range: CountableRange) -> String { - get { - var result = "" - for i in range { - assert(i < self.count) - result.append(self[i]) - } - return result - } - } -} diff --git a/DFS/FactorCombinations.swift b/DFS/FactorCombinations.swift index 19f7d05c..e4941901 100644 --- a/DFS/FactorCombinations.swift +++ b/DFS/FactorCombinations.swift @@ -8,30 +8,27 @@ class FactorCombinations { func getFactors(_ n: Int) -> [[Int]] { - var res = [[Int]]() - var path = [Int]() + var paths = [[Int]](), path = [Int]() - dfs(&res, &path, n, 2) + dfs(&paths, path, 2, n) - return res + return paths } - private func dfs(_ res: inout [[Int]], _ path: inout [Int], _ n: Int, _ start: Int) { - if n == 1 { + private func dfs(_ paths: inout [[Int]], _ path: [Int], _ start: Int, _ target: Int) { + if target == 1 { if path.count > 1 { - res.append(Array(path)) + paths.append(path) } return } - if start > n { + guard start <= target else { return } - for i in start...n where n % i == 0 { - path.append(i) - dfs(&res, &path, n / i, i) - path.removeLast() + for factor in start...target where target % factor == 0 { + dfs(&paths, path + [factor], factor, target / factor) } } } \ No newline at end of file diff --git a/DFS/LetterCombinationsPhoneNumber.swift b/DFS/LetterCombinationsPhoneNumber.swift index 9ada40a4..df88df14 100644 --- a/DFS/LetterCombinationsPhoneNumber.swift +++ b/DFS/LetterCombinationsPhoneNumber.swift @@ -2,52 +2,51 @@ * Question Link: https://leetcode.com/problems/letter-combinations-of-a-phone-number/ * Primary idea: Classic Depth-first Search, create phone board at first * - * Time Complexity: O(nm), m stands for the average size of a string represented by a number - * Space Complexity: O(n) + * Time Complexity: O(4^n), n stands for length of digits + * Space Complexity: O(n), n stands for length of digits * */ class LetterCombinationsPhoneNumber { func letterCombinations(_ digits: String) -> [String] { + guard digits.count > 0 else { + return [String]() + } + var combinations = [String](), combination = "" + let numberToStr = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] - dfs(createBoard(), &combinations, &combination, Array(digits), 0) + dfs(&combinations, &combination, numberToStr, digits, 0) return combinations } - fileprivate func createBoard() -> [String] { - var res = [String]() - - res.append("") - res.append("") - res.append("abc") - res.append("def") - res.append("ghi") - res.append("jkl") - res.append("mno") - res.append("pqrs") - res.append("tuv") - res.append("wxyz") - - return res - } - - fileprivate func dfs(_ board: [String], _ combinations: inout [String], _ combination: inout String, _ digits: [Character], _ index: Int) { - if digits.count == index { - if combination != "" { - combinations.append(String(combination)) - } - + private func dfs(_ combinations: inout [String], _ combination: inout String, _ numberToStr: [String], _ digits: String, _ index: Int) { + if combination.count == digits.count { + combinations.append(combination) return } - let digitStr = board[Int(String(digits[index]))!] + let currentStr = fetchCurrentStr(from: digits, at: index, numberToStr) - for digitChar in digitStr { - combination.append(digitChar) - dfs(board, &combinations, &combination, digits, index + 1) + for char in currentStr { + combination.append(char) + dfs(&combinations, &combination, numberToStr, digits, index + 1) combination.removeLast() } } -} \ No newline at end of file + + private func fetchCurrentStr(from digits: String, at index: Int, _ numberToStr: [String]) -> String { + guard index >= 0 && index < digits.count else { + fatalError("Invalid index") + } + + let currentDigitChar = digits[digits.index(digits.startIndex, offsetBy: index)] + + guard let currentDigit = Int(String(currentDigitChar)), currentDigit >= 0, currentDigit < numberToStr.count else { + fatalError("Invalid digits") + } + + return numberToStr[currentDigit] + } +} 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 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 + } +} 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 + } +} diff --git a/DFS/PartitionKEqualSumSubsets.swift b/DFS/PartitionKEqualSumSubsets.swift new file mode 100644 index 00000000..9dd2ca13 --- /dev/null +++ b/DFS/PartitionKEqualSumSubsets.swift @@ -0,0 +1,53 @@ +/** + * Question Link: https://leetcode.com/problems/partition-to-k-equal-sum-subsets/ + * Primary idea: Divide the whole array into buckets and use + * DFS to check whether there is a valid solution + * + * Time Complexity: O(k^n), Space Complexity: O(n) + * + */ + +class PartitionKEqualSumSubsets { + func canPartitionKSubsets(_ nums: [Int], _ k: Int) -> Bool { + let sum = nums.reduce(0) { $0 + $1 } + + guard sum % k == 0 else { + return false + } + + let target = sum / k, nums = nums.sorted() + var index = nums.count - 1, groupsEqualToK = Array(repeating: 0, count: k) + + guard nums[index] <= target else { + return false + } + + return dfs(&groupsEqualToK, target, nums, index) + } + + private func dfs(_ groupsEqualToK: inout [Int], _ target: Int, _ nums: [Int], _ index: Int) -> Bool { + if index < 0 { + return true + } + + let currentNum = nums[index] + + for i in 0.. [[Int]] { - var res = [[Int]]() - var path = [Int]() - var visited = [Bool](count: nums.count, repeatedValue: false) + 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(Array(path)) + 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() } } -} \ No newline at end of file +} diff --git a/DFS/StrobogrammaticNumberII.swift b/DFS/StrobogrammaticNumberII.swift index 2b069668..76b16f1c 100644 --- a/DFS/StrobogrammaticNumberII.swift +++ b/DFS/StrobogrammaticNumberII.swift @@ -8,42 +8,34 @@ */ class StrobogrammaticNumberII { + let mirrorDigits = [0: 0, 1: 1, 6: 9, 8: 8, 9: 6] + func findStrobogrammatic(_ n: Int) -> [String] { - var res = [String]() - - guard n >= 1 else { - return res - } + var path = Array(repeating: Character(" "), count: n), paths = [String]() - let left = Array("01689"), right = Array("01986") - var path = Array(repeating: Character("-"), count: n) + dfs(0, n - 1, &path, &paths) - dfs(&res, left, right, 0, n - 1, &path) - - return res + return paths } - fileprivate func dfs(_ res: inout [String], _ left: [Character], _ right: [Character], _ leftIdx: Int, _ path: inout [Character]) { - let rightIdx = path.count - leftIdx - 1 - - if leftIdx > rightIdx { - res.append(String(path)) + private func dfs(_ left: Int, _ right: Int, _ path: inout [Character], _ paths: inout [String]) { + if left > right { + paths.append(String(path)) return } - for i in 0.. [[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 - - res.append(Array(path)) + private func dfs(_ res: inout [[Int]], _ path: inout [Int], _ idx: Int, _ nums: [Int]) { + res.append(path) - for i in index.. [[Int]] { - var res = [[Int]]() - var path = [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) { - res.append(Array(path)) + 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() } } -} \ No newline at end of file +} 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.. [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.. [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/DFS/WordSquares.swift b/DFS/WordSquares.swift index 34c8adaf..df5ff951 100644 --- a/DFS/WordSquares.swift +++ b/DFS/WordSquares.swift @@ -2,61 +2,64 @@ * Question Link: https://leetcode.com/problems/word-squares/ * Primary idea: Classic Depth-first Search, fill out row by row, choose correct word with fixed prefix, only need to care which column is used * - * Time Complexity: O(n^n), Space Complexity: O(n) + * Time Complexity: O(n^2), Space Complexity: O(n^2) * */ class WordSquares { func wordSquares(_ words: [String]) -> [[String]] { - var paths = [[String]]() - - guard words.count > 0 else { - return paths + guard let first = words.first else { + return [[String]]() } - var prefixWords = initPrefix(words), path = [String]() + let prefixesToWords = buildMaps(words), rowNum = first.count + var paths = [[String]](), path = [String]() - dfs(&paths, &path, prefixWords, 0, words[0].count) + dfs(&paths, path, prefixesToWords, rowNum, 0) return paths } - fileprivate func dfs(_ paths: inout [[String]], _ path: inout [String], _ prefixWords: [String:[String]], _ row: Int, _ len: Int) { - if row == len { - paths.append(Array(path)) + private func dfs(_ paths: inout [[String]], _ path: [String], _ prefixesToWords: [String: [String]], _ rowNum: Int, _ currentRow: Int) { + if currentRow == rowNum { + paths.append(path) return } - var pre = "" - for i in 0.. [String: [String]]{ - var prefix = [String: [String]]() + private func buildMaps(_ words: [String]) -> [String: [String]] { + var res = [String: [String]]() for word in words { - prefix[""] = prefix["", default:[]] + [word] - - var pre = "" - for c in word { - pre = "\(pre)\(c)" - - prefix[pre] = prefix[pre, default:[]] + [word] + for prefix in prefixes(word) { + res[prefix, default: []].append(word) } } - return prefix + return res + } + + private func prefixes(_ word: String) -> [String] { + var res = [""], prefix = "" + + for char in word { + prefix.append(char) + res.append(prefix) + } + + return res } } \ No newline at end of file diff --git a/DP/BestTimeBuySellStock.swift b/DP/BestTimeBuySellStock.swift index 10147663..eb35cb53 100644 --- a/DP/BestTimeBuySellStock.swift +++ b/DP/BestTimeBuySellStock.swift @@ -8,16 +8,16 @@ class BestTimeBuySellStock { func maxProfit(prices: [Int]) -> Int { - guard prices.count >= 2 else { - return 0 - } - + guard prices.count > 0 else {return 0} var maxProfit = 0 - var lowest = prices[0] + var buyDay = 0 - for price in prices { - maxProfit = max(maxProfit, price - lowest) - lowest = min(lowest, price) + for i in 1 ..< prices.count { + let profit = prices[i] - prices[buyDay] + if profit < 0 { + buyDay = i + } + maxProfit = max(profit, maxProfit) } return maxProfit diff --git a/DP/BestTimeBuySellStockIV.swift b/DP/BestTimeBuySellStockIV.swift index 1746b23d..3d204762 100644 --- a/DP/BestTimeBuySellStockIV.swift +++ b/DP/BestTimeBuySellStockIV.swift @@ -21,7 +21,11 @@ return makeMaxProfit(prices) } - var local = Array(repeating: 0, count: k + 1), global = Array(repeating: 0, count: k + 1) + // local[i] means the maxProfit when sell happens at ith day + var local = Array(repeating: 0, count: k + 1) + + // global[i] means the maxProfit at ith day + var global = Array(repeating: 0, count: k + 1) for i in 0.. Int { - var steps = [Int](count: n + 1, repeatedValue: 0) - return _helper(n, &steps) - } - - private func _helper(n: Int, inout _ steps: [Int]) -> Int { - // termination case + func climbStairs(_ n: Int) -> Int { if n < 0 { return 0 } - if n == 0 { + if n == 0 || n == 1 { return 1 } - if steps[n] != 0 { - return steps[n] - } + var prev = 0, post = 1, total = 0 - steps[n] = _helper(n - 1, &steps) + _helper(n - 2, &steps) - return steps[n] + for i in 1...n { + total = prev + post + + prev = post + post = total + } + + return total } } \ No newline at end of file diff --git a/DP/CoinChange.swift b/DP/CoinChange.swift index 24c03fbb..e9d93700 100644 --- a/DP/CoinChange.swift +++ b/DP/CoinChange.swift @@ -5,30 +5,29 @@ */ class CoinChange { - func coinChange(coins: [Int], _ amount: Int) -> Int { - // edge case - guard amount != 0 else { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + guard amount > 0 else { return 0 } - - var minNums = [Int](count: amount + 1, repeatedValue: -1) - for i in 0...amount { + let coins = coins.sorted() + var minAmounts = Array(repeating: -1, count: amount + 1) + minAmounts[0] = 0 + + for i in 1...amount { for coin in coins { - if coin == i { - minNums[i] = 1 + if coin > i { break } - if coin < i && minNums[i - coin] != -1 { - if minNums[i] == -1 { - minNums[i] = minNums[i - coin] + 1 - } else { - minNums[i] = min(minNums[i], minNums[i - coin] + 1) - } + + if minAmounts[i - coin] == -1 { + continue } + + minAmounts[i] = minAmounts[i] == -1 ? minAmounts[i - coin] + 1 : min(minAmounts[i - coin] + 1, minAmounts[i]) } } - return minNums[amount] + return minAmounts[amount] } -} \ No newline at end of file +} diff --git a/DP/DecodeWays.swift b/DP/DecodeWays.swift index b1778343..343b187d 100644 --- a/DP/DecodeWays.swift +++ b/DP/DecodeWays.swift @@ -8,37 +8,28 @@ class DecodeWays { func numDecodings(_ s: String) -> Int { - let sChars = Array(s) + let s = Array(s) var dp = Array(repeating: 0, count: s.count + 1) dp[0] = 1 - guard s.count >= 1 else { - return 0 - } - for i in 1...s.count { - if String(sChars[i - 1..= 2 && String(sChars[i - 2.. 1 && isValid(s, i - 2, i - 1) { dp[i] += dp[i - 2] - } + } } return dp[s.count] } -} - -extension String { - var isValid: Bool { - if let first = first, first == "0" { - return false - } - - guard let num = Int(self) else { - return false + + private func isValid(_ s: [Character], _ start: Int, _ end: Int) -> Bool { + guard let num = Int(String(s[start...end])) else { + fatalError() } - return 0 < num && 26 >= num + return num >= 10 && num <= 26 } } \ No newline at end of file diff --git a/DP/EditDistance.swift b/DP/EditDistance.swift index 394860ac..395c4033 100644 --- a/DP/EditDistance.swift +++ b/DP/EditDistance.swift @@ -7,27 +7,24 @@ class EditDistance { func minDistance(word1: String, _ word2: String) -> 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 diff --git a/DP/GenerateParentheses.swift b/DP/GenerateParentheses.swift index 90b3ae8c..8e0b65c8 100644 --- a/DP/GenerateParentheses.swift +++ b/DP/GenerateParentheses.swift @@ -7,30 +7,28 @@ class GenerateParentheses { func generateParenthesis(_ n: Int) -> [String] { - var paths = [String](), path = [Character](repeating: " ", count: 2 * n) + guard n > 0 else { + return [String]() + } + + var paths = [String](), path = "" - helper(&paths, &path, n, n, 0) + dfs(&paths, path, n, n) return paths } - func helper(_ paths: inout [String], _ path: inout [Character], _ leftCount: Int, _ rightCount: Int, _ index: Int) { - if leftCount < 0 || leftCount > rightCount { - return - } - - if leftCount == 0 && rightCount == 0 { - paths.append(String(path)) + private func dfs(_ paths: inout [String], _ path: String, _ leftRemaining: Int, _ rightRemaining: Int) { + if rightRemaining == 0 { + paths.append(path) return } - if leftCount > 0 { - path[index] = "(" - helper(&paths, &path, leftCount - 1, rightCount, index + 1) + if leftRemaining > 0 { + dfs(&paths, path + "(", leftRemaining - 1, rightRemaining) } - if rightCount > leftCount { - path[index] = ")" - helper(&paths, &path, leftCount, rightCount - 1, index + 1) + if rightRemaining > leftRemaining { + dfs(&paths, path + ")", leftRemaining, rightRemaining - 1) } } -} \ No newline at end of file +} diff --git a/DP/JumpGame.swift b/DP/JumpGame.swift index 5201cdb6..5bdd19d5 100644 --- a/DP/JumpGame.swift +++ b/DP/JumpGame.swift @@ -7,16 +7,19 @@ class JumpGame { func canJump(_ nums: [Int]) -> Bool { - var max = 0 + var maximumIndex = nums[0] - for i in 0 ..< nums.count { - let farestStep = i + nums[i] - if i > max { + for (currentIndex, value) in nums.enumerated(){ + + if currentIndex > maximumIndex{ return false } - max = max > farestStep ? max : farestStep + + maximumIndex = max(maximumIndex, currentIndex + value) } return true } } + + 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 diff --git a/DP/LongestIncreasingSubsequence.swift b/DP/LongestIncreasingSubsequence.swift index bb33e11f..89592487 100644 --- a/DP/LongestIncreasingSubsequence.swift +++ b/DP/LongestIncreasingSubsequence.swift @@ -1,23 +1,39 @@ /** * Question Link: https://leetcode.com/problems/longest-increasing-subsequence/ - * Primary idea: Dynamic Programming, transition function is len[i] = max(len[i], len[j] + 1) - * Time Complexity: O(n^2), Space Complexity: O(n) + * Primary idea: Dynamic Programming, update the array which ends at current index using binary search + * Time Complexity: O(nlogn), Space Complexity: O(n) */ class LongestIncreasingSubsequence { - func lengthOfLIS(nums: [Int]) -> Int { - var length_global = 0 - var length_current = [Int](count: nums.count, repeatedValue: 1) + func lengthOfLIS(_ nums: [Int]) -> Int { + var res = [nums[0]] - for i in 0.. nums[j] { - length_current[i] = max(length_current[i], length_current[j] + 1) - } + for i in 1.. 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 + } +} diff --git a/DP/LongestPalindromicSubstring.swift b/DP/LongestPalindromicSubstring.swift index fa31fd9f..69d0fde2 100644 --- a/DP/LongestPalindromicSubstring.swift +++ b/DP/LongestPalindromicSubstring.swift @@ -1,49 +1,38 @@ /** * Question Link: https://leetcode.com/problems/longest-palindromic-substring/ - * Primary idea: 2D Dynamic Programming, update boolean array based on - * current two characters' equity and the previous boolean subarray - * Time Complexity: O(n^2), Space Complexity: O(n^2) + * Primary idea: Find the longest palindrome string from every index at the center. + * Time Complexity: O(n^2), Space Complexity: O(1) * */ class LongestPalindromicSubstring { func longestPalindrome(_ s: String) -> String { - guard s.characters.count > 1 else { + guard s.count > 1 else { return s } - var sChars = [Character](s.characters) - let len = sChars.count - var maxLen = 1 - var maxStart = 0 - var isPalin = Array(repeating: Array(repeating: false, count : len), count : len) + let sChars = Array(s) + var maxLen = 0, start = 0 - // set palindrome whose len is 1 - for i in 0...len - 1 { - isPalin[i][i] = true + for i in 0..= 3 { - for length in 3...len { - for i in 0...len - length { - if sChars[i] == sChars[i + length - 1] && isPalin[i + 1][i + length - 2] { - isPalin[i][i + length - 1] = true - maxLen = length - maxStart = i - } - } - } + while l >= 0 && r < chars.count && chars[l] == chars[r] { + l -= 1 + r += 1 } - return String(sChars[maxStart...maxStart + maxLen - 1]) + if maxLen < r - l - 1 { + start = l + 1 + maxLen = r - l - 1 + } } } 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.. 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.. 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.. Int * + * // Set this NestedInteger to hold a single integer. + * public func setInteger(value: Int) + * + * // Set this NestedInteger to hold a nested list and adds a nested integer to it. + * public func add(elem: NestedInteger) + * * // Return the nested list that this NestedInteger holds, if it holds a nested list * // The result is undefined if this NestedInteger holds a single integer * public func getList() -> [NestedInteger] diff --git a/DP/NestedListWeightSumII.swift b/DP/NestedListWeightSumII.swift new file mode 100644 index 00000000..79271966 --- /dev/null +++ b/DP/NestedListWeightSumII.swift @@ -0,0 +1,49 @@ +/** + * Question Link: https://leetcode.com/problems/nested-list-weight-sum-ii/ + * Primary idea: Track depth for every number and max depth overall + * Time Complexity: O(n), Space Complexity: O(n) + * + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * class NestedInteger { + * // Return true if this NestedInteger holds a single integer, rather than a nested list. + * public func isInteger() -> Bool + * + * // Return the single integer that this NestedInteger holds, if it holds a single integer + * // The result is undefined if this NestedInteger holds a nested list + * public func getInteger() -> Int + * + * // Set this NestedInteger to hold a single integer. + * public func setInteger(value: Int) + * + * // Set this NestedInteger to hold a nested list and adds a nested integer to it. + * public func add(elem: NestedInteger) + * + * // Return the nested list that this NestedInteger holds, if it holds a nested list + * // The result is undefined if this NestedInteger holds a single integer + * public func getList() -> [NestedInteger] + * } + */ + + class NestedListWeightSumII { + func depthSumInverse(_ nestedList: [NestedInteger]) -> Int { + var numDepth = [(Int, Int)](), maxDepth = 0 + + helper(nestedList, 1, &maxDepth, &numDepth) + + return numDepth.reduce(0) { (total, numDepth) in total + numDepth.0 * (maxDepth - numDepth.1 + 1) } + } + + private func helper(_ nestedList: [NestedInteger], _ depth: Int, _ maxDepth: inout Int, _ numDepth: inout [(Int, Int)]) { + maxDepth = max(depth, maxDepth) + + for nestedInt in nestedList { + if nestedInt.isInteger() { + numDepth.append((nestedInt.getInteger(), depth)) + } else { + helper(nestedInt.getList(), depth + 1, &maxDepth, &numDepth) + } + } + } +} + diff --git a/DP/PaintHouse.swift b/DP/PaintHouse.swift new file mode 100644 index 00000000..dd538a61 --- /dev/null +++ b/DP/PaintHouse.swift @@ -0,0 +1,24 @@ +/** + * Question Link: https://leetcode.com/problems/paint-house/ + * Primary idea: Dynamic Programming, dp[i][j] += min(dp[i - 1][(j + 1) % 3], dp[i - 1][(j + 2) % 3]) + * + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class PaintHouse { + func minCost(_ costs: [[Int]]) -> Int { + guard let colors = costs.first, !colors.isEmpty else { + return 0 + } + + var dp = costs + + for i in 1.. Int { + guard let colors = costs.first, !colors.isEmpty else { + return 0 + } + + var preColor = -1, preMinCost = 0, preSecondMinCost = 0 + + for cost in costs { + + var currentColor = -1, currentMinCost = Int.max, currentSecondMinCost = Int.max + + for i in 0.. 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/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.. 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 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.. Bool { + return search(word, head) + } + + private func search(_ word: String, _ startNode: TrieNode) -> Bool { + var node = startNode + + guard let char = word.first else { + return node.isEnd + } + + if char != "." { + if node.children[char] == nil { + return false + } else { + return search(String(word.dropFirst()), node.children[char]!) + } + } else { + if node.children.isEmpty { + return false + } else { + for childNode in node.children.values { + if search(String(word.dropFirst()), childNode) { + return true + } + } + return false + } + } + } +} + +class TrieNode { + var isEnd: Bool + var children: [Character: TrieNode] + + init() { + isEnd = false + children = [Character: TrieNode]() + } +} + +/** + * Your WordDictionary object will be instantiated and called as such: + * let obj = WordDictionary() + * obj.addWord(word) + * let ret_2: Bool = obj.search(word) + */ \ No newline at end of file diff --git a/Design/AllOne.swift b/Design/AllOne.swift new file mode 100644 index 00000000..07e03e1f --- /dev/null +++ b/Design/AllOne.swift @@ -0,0 +1,152 @@ +/** + * Question Link: https://leetcode.com/problems/all-oone-data-structure/ + * Primary idea: Use a doubly linked list while the node is val and all keys, and + * a hash table where value is the node containing the key + * Time Complexity: O(1), Space Complexity: O(n) + * + */ + + class AllOne { + + private var dict: [String: Node] + + private var head: Node + private var tail: Node + + /** Initialize your data structure here. */ + init() { + dict = [String: Node]() + + head = Node(-1, "") + tail = Node(-1, "") + head.post = tail + tail.prev = head + } + + /** Inserts a new key with value 1. Or increments an existing key by 1. */ + func inc(_ key: String) { + if let node = dict[key] { + update(node, key, 1) + } else { + if head.post!.val != 1 { + let node = Node(1, key) + insert(node, after: head) + dict[key] = node + } else { + head.post!.list.insert(key) + dict[key] = head.post! + } + } + } + + /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */ + func dec(_ key: String) { + guard let node = dict[key] else { + return + } + + update(node, key, -1) + } + + /** Returns one of the keys with maximal value. */ + func getMaxKey() -> String { + if dict.count > 0 { + return tail.prev!.list.first! + } else { + return "" + } + } + + /** Returns one of the keys with Minimal value. */ + func getMinKey() -> String { + if dict.count > 0 { + return head.post!.list.first! + } else { + return "" + } + } + + private func update(_ node: Node, _ key: String, _ addVal: Int) { + node.list.remove(key) + + let next = addVal == 1 ? node.post : node.prev + + // insert new node + if next!.val != node.val + addVal { + guard node.val + addVal != 0 else { + dict[key] = nil + + removeIfNecessary(node) + + return + } + + let newNode = Node(node.val + addVal, key) + + if addVal == 1 { + insert(newNode, after: node) + } else { + insert(newNode, before: node) + } + + dict[key] = newNode + + // insert key to next node + } else { + next!.list.insert(key) + dict[key] = next + } + + removeIfNecessary(node) + } + + private func insert(_ newNode: Node, after node: Node) { + node.post?.prev = newNode + newNode.post = node.post + + newNode.prev = node + node.post = newNode + } + + private func insert(_ newNode: Node, before node: Node) { + node.prev?.post = newNode + newNode.prev = node.prev + + newNode.post = node + node.prev = newNode + } + + private func removeIfNecessary(_ node: Node) { + guard node.list.isEmpty else { + return + } + + node.post!.prev = node.prev + node.prev!.post = node.post + + node.prev = nil + node.post = nil + + } +} + +fileprivate class Node { + var val: Int + var list: Set + var prev: Node? + var post: Node? + + init(_ val: Int, _ key: String) { + self.val = val + list = Set([key]) + } +} + +/** + * Your AllOne object will be instantiated and called as such: + * let obj = AllOne() + * obj.inc(key) + * obj.dec(key) + * let ret_3: String = obj.getMaxKey() + * let ret_4: String = obj.getMinKey() + */ \ No newline at end of file diff --git a/Design/DesignHashMap.swift b/Design/DesignHashMap.swift new file mode 100644 index 00000000..a3cdd640 --- /dev/null +++ b/Design/DesignHashMap.swift @@ -0,0 +1,62 @@ +/** + * Question Link: https://leetcode.com/problems/design-hashmap/ + * Primary idea: Use modulo and array of list / linked list to handle handle function and collision. + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class MyHashMap { + + let keySpace = 2069 + var buckets: [[(Int, Int)]] + + /** Initialize your data structure here. */ + init() { + buckets = Array(repeating: [(Int, Int)](), count: keySpace) + } + + /** value will always be non-negative. */ + func put(_ key: Int, _ value: Int) { + var bucket = buckets[key % keySpace] + + if let index = find(key, bucket) { + bucket[index].1 = value + } else { + bucket.append((key, value)) + } + + buckets[key % keySpace] = bucket + } + + /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */ + func get(_ key: Int) -> Int { + let bucket = buckets[key % keySpace] + + if let index = find(key, bucket) { + return bucket[index].1 + } else { + return -1 + } + } + + /** Removes the mapping of the specified value key if this map contains a mapping for the key */ + func remove(_ key: Int) { + var bucket = buckets[key % keySpace] + + guard let index = find(key, bucket) else { + return + } + + bucket.swapAt(index, bucket.count - 1) + bucket.removeLast() + + buckets[key % keySpace] = bucket + } + + private func find(_ key: Int, _ bucket: [(Int, Int)]) -> Int? { + for (i, pair) in bucket.enumerated() where pair.0 == key { + return i + } + + return nil + } +} diff --git a/Design/DesignTicTacToe.swift b/Design/DesignTicTacToe.swift new file mode 100644 index 00000000..cd1ead83 --- /dev/null +++ b/Design/DesignTicTacToe.swift @@ -0,0 +1,56 @@ +/** + * Question Link: https://leetcode.com/problems/design-tic-tac-toe/ + * Primary idea: Use two arrays and variables for accumulated scroes for + * rows, cols, and diagonal rows. + * Use their absoluate value to determine the winner. + * Time Complexity: O(1), Space Complexity: O(n) + * + */ + +class TicTacToe { + + var rows: [Int] + var cols: [Int] + var diagonal: Int + var antiDiagonal: Int + + /** Initialize your data structure here. */ + init(_ n: Int) { + rows = Array(repeating: 0, count: n) + cols = Array(repeating: 0, count: n) + diagonal = 0 + antiDiagonal = 0 + } + + /** Player {player} makes a move at ({row}, {col}). + @param row The row of the board. + @param col The column of the board. + @param player The player, can be either 1 or 2. + @return The current winning condition, can be either: + 0: No one wins. + 1: Player 1 wins. + 2: Player 2 wins. */ + func move(_ row: Int, _ col: Int, _ player: Int) -> Int { + let num = player == 1 ? 1 : -1, n = rows.count + + // row + rows[row] += num + + // col + cols[col] += num + + // diagonal + if row == col { + diagonal += num + } + if row + col == n - 1 { + antiDiagonal += num + } + + if abs(rows[row]) == n || abs(cols[col]) == n || abs(diagonal) == n || abs(antiDiagonal) == n { + return player + } + + return 0 + } +} \ No newline at end of file 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) + */ diff --git a/Design/FlattenNestedListIterator.swift b/Design/FlattenNestedListIterator.swift new file mode 100644 index 00000000..47dffb8f --- /dev/null +++ b/Design/FlattenNestedListIterator.swift @@ -0,0 +1,95 @@ +/** + * Question Link: https://leetcode.com/problems/flatten-nested-list-iterator/ + * Primary idea I: Use a stack to store nested list reversely. + * Every time pop a element and push back until it is an integer. + * Time Complexity I: next - O(1), hasNext - O(n), Space Complexity I: O(n) + * + * Primary idea II: Flatten the nested list at very first. + * Time Complexity II: next - O(1), hasNext - O(1), Space Complexity II: O(n) + * + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * class NestedInteger { + * // Return true if this NestedInteger holds a single integer, rather than a nested list. + * public func isInteger() -> Bool + * + * // Return the single integer that this NestedInteger holds, if it holds a single integer + * // The result is undefined if this NestedInteger holds a nested list + * public func getInteger() -> Int + * + * // Set this NestedInteger to hold a single integer. + * public func setInteger(value: Int) + * + * // Set this NestedInteger to hold a nested list and adds a nested integer to it. + * public func add(elem: NestedInteger) + * + * // Return the nested list that this NestedInteger holds, if it holds a nested list + * // The result is undefined if this NestedInteger holds a single integer + * public func getList() -> [NestedInteger] + * } + */ + +// Option I +class NestedIteratorI { + + private var stack: [NestedInteger] + + init(_ nestedList: [NestedInteger]) { + stack = nestedList.reversed() + } + + func next() -> Int { + return stack.removeLast().getInteger() + } + + func hasNext() -> Bool { + while !stack.isEmpty { + var nestedInteger = stack.removeLast() + + if nestedInteger.isInteger() { + stack.append(nestedInteger) + return true + } else { + for nestedInt in nestedInteger.getList().reversed() { + stack.append(nestedInt) + } + } + } + + return false + } +} + +// Option II +class NestedIteratorII { + + private var list: [Int] + private var index: Int + + init(_ nestedList: [NestedInteger]) { + list = [Int]() + index = 0 + + flatten(nestedList) + } + + func next() -> Int { + let current = list[index] + index += 1 + return current + } + + func hasNext() -> Bool { + return index < list.count + } + + private func flatten(_ nestedList: [NestedInteger]) { + for nestedInt in nestedList { + if nestedInt.isInteger() { + list.append(nestedInt.getInteger()) + } else { + flatten(nestedInt.getList()) + } + } + } +} 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 + } +} diff --git a/Design/ImplementTrie.swift b/Design/ImplementTrie.swift new file mode 100644 index 00000000..b00e07e8 --- /dev/null +++ b/Design/ImplementTrie.swift @@ -0,0 +1,57 @@ +/** + * Question Link: https://leetcode.com/problems/implement-trie-prefix-tree/ + * Primary idea: implement a tree data structure where each node has a dictionary storing + * all descendants having a common prefix. + * + * Time Complexity: O(m) where m is the length of a target string + * Space Complexity: insert - O(m), search - O(1), startsWith - O(1) + * + */ + +class Trie { + private var nodes: [Character: Trie] + + /** Initialize your data structure here. */ + init() { + nodes = [:] + } + + /** Inserts a word into the trie. */ + func insert(_ word: String) { + var trie = self + word.forEach { + if let subTrie = trie.nodes[$0] { + trie = subTrie + } else { + let subTrie = Trie() + trie.nodes[$0] = subTrie + trie = subTrie + } + } + trie.nodes["#"] = Trie() + } + + /** Returns if the word is in the trie. */ + func search(_ word: String) -> Bool { + var trie = self + for char in word { + guard let subTrie = trie.nodes[char] else { + return false + } + trie = subTrie + } + return trie.nodes["#"] != nil; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + func startsWith(_ prefix: String) -> Bool { + var trie = self + for char in prefix { + guard let subTrie = trie.nodes[char] else { + return false + } + trie = subTrie + } + return true + } +} diff --git a/Design/InsertDeleteGetRandom.swift b/Design/InsertDeleteGetRandom.swift new file mode 100644 index 00000000..af5cca00 --- /dev/null +++ b/Design/InsertDeleteGetRandom.swift @@ -0,0 +1,54 @@ +/** + * Question Link: https://leetcode.com/problems/insert-delete-getrandom-o1/ + * Primary idea: Using an array and dictionary since adding or removing at the end of array is constant. + * Time Complexity: O(1), Space Complexity: O(n) + * + */ + +class RandomizedSet { + + var numToIndex: [Int: Int] + var nums: [Int] + + /** Initialize your data structure here. */ + init() { + numToIndex = [Int: Int]() + nums = [Int]() + } + + /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ + func insert(_ val: Int) -> Bool { + if let _ = numToIndex[val] { + return false + } else { + numToIndex[val] = nums.count + nums.append(val) + + return true + } + } + + /** Removes a value from the set. Returns true if the set contained the specified element. */ + func remove(_ val: Int) -> Bool { + if let index = numToIndex[val], let last = nums.last { + numToIndex[last] = index + numToIndex[val] = nil + + nums.swapAt(index, nums.count - 1) + nums.removeLast() + + return true + } else { + return false + } + } + + /** Get a random element from the set. */ + func getRandom() -> Int { + guard let randomElement = nums.randomElement() else { + fatalError("Empty set!") + } + + return randomElement + } +} \ No newline at end of file diff --git a/Design/LRUCache.swift b/Design/LRUCache.swift new file mode 100644 index 00000000..ac7fb6c6 --- /dev/null +++ b/Design/LRUCache.swift @@ -0,0 +1,91 @@ +/** + * Question Link: https://leetcode.com/problems/lru-cache/ + * Primary idea: Use Doubly linked list and hash table to build the LRU cache. + * Time Complexity: O(1), Space Complexity: O(n) + * + */ + +class LRUCache { + + private let capacity: Int + private let head = Node(0, 0) + private let tail = Node(0, 0) + + private var keyNodeMap = [Int: Node]() + + init(_ capacity: Int) { + self.capacity = capacity + + head.next = tail + tail.prev = head + } + + func get(_ key: Int) -> Int { + guard let node = keyNodeMap[key] else { + return -1 + } + + remove(node) + moveToHead(node) + + return node.val + } + + 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!) + } + } + + 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 + } + + private func moveToHead(_ node: Node) { + let first = head.next + + head.next = node + + node.prev = head + node.next = first + + first!.prev = node + } + + 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 + } + } +} + +/** + * 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) + */ 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]() + } + } +} diff --git a/Design/ShuffleAnArray.swift b/Design/ShuffleAnArray.swift new file mode 100644 index 00000000..95aa0772 --- /dev/null +++ b/Design/ShuffleAnArray.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/shuffle-an-array/ + * Primary idea: Iterate through the array and swap the value at current index + * with that at a random selected index + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class ShuffleAnArray { + var originalNums: [Int] + + init(_ nums: [Int]) { + originalNums = nums + } + + /** Resets the array to its original configuration and return it. */ + func reset() -> [Int] { + return originalNums + } + + /** Returns a random shuffling of the array. */ + func shuffle() -> [Int] { + // note: Apple have an API called shuffled() for an array, complexity is O(n) + + let count = originalNums.count + var nums = originalNums + + for i in 0.. Int { + snapshotArrayMap[count] = array + count += 1 + + return count - 1 + } + + func get(_ index: Int, _ snap_id: Int) -> Int { + return snapshotArrayMap[snap_id]?[index] ?? 0 + } +} diff --git a/Design/Vector2D.swift b/Design/Vector2D.swift new file mode 100644 index 00000000..edf47511 --- /dev/null +++ b/Design/Vector2D.swift @@ -0,0 +1,49 @@ +/** + * Question Link: https://leetcode.com/problems/flatten-2d-vector/ + * Primary idea: Two pointers, one to iterate arrays, + * one to interate elements in a specifc array + * + * Time Complexity: O(n) for next, O(n) for hasNext() + * Space Complexity: O(n) + * + */ + + class Vector2D { + + var arrayIdx: Int + var elementIdx: Int + var arrays: [[Int]] + + init(_ v: [[Int]]) { + arrays = v + arrayIdx = 0 + elementIdx = 0 + } + + func next() -> Int { + if hasNext() { + let element = arrays[arrayIdx][elementIdx] + elementIdx += 1 + return element + } + + fatalError("Invalid next call") + } + + func hasNext() -> Bool { + findNext() + + return arrayIdx < arrays.count + } + + private func findNext() { + while arrayIdx < arrays.count { + if elementIdx < arrays[arrayIdx].count { + return + } else { + arrayIdx += 1 + elementIdx = 0 + } + } + } +} diff --git a/Graph/AlienDictionary.swift b/Graph/AlienDictionary.swift new file mode 100644 index 00000000..41251259 --- /dev/null +++ b/Graph/AlienDictionary.swift @@ -0,0 +1,73 @@ +/** + * Question Link: https://leetcode.com/problems/alien-dictionary/ + * Primary idea: Topological sort, keep each character for a inDegree number and + * to characters list, use a queue to decrease inDegree and form the result + * + * Time Complexity: O(nm), Space Complexity: O(m), + * n represents words number, m represents length of a word + */ + +class AlienDictionary { + func alienOrder(_ words: [String]) -> String { + var (inDegrees, toChars) = buildGraph(words) + + var queue = inDegrees.keys.filter { inDegrees[$0] == 0 } + var res = "" + + while !queue.isEmpty { + let char = queue.removeFirst() + + res.append(char) + + for nextChar in toChars[char]! { + inDegrees[nextChar]! -= 1 + + if inDegrees[nextChar] == 0 { + queue.append(nextChar) + } + } + } + + return res.count == inDegrees.count ? res : "" + } + + private func buildGraph(_ words: [String]) -> ([Character: Int], [Character: [Character]]) { + // init inDegrees and toChars + var inDegrees = [Character: Int](), toChars = [Character: [Character]]() + + words.forEach { word in + word.forEach { char in + inDegrees[char] = 0 + toChars[char] = [Character]() + } + } + + // update based on orders + for i in 0.. 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 diff --git a/Graph/CourseSchedule.swift b/Graph/CourseSchedule.swift new file mode 100644 index 00000000..f5e217af --- /dev/null +++ b/Graph/CourseSchedule.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/course-schedule/ + * Primary idea: Topological sort, use indegree of a graph and BFS to solve the problem + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + + class CourseSchedule { + func canFinish(_ numCourses: Int, _ prerequisites: [[Int]]) -> Bool { + var inDegrees = Array(repeating: 0, count: numCourses), toCourses = [Int: [Int]]() + + for courses in prerequisites { + inDegrees[courses[0]] += 1 + toCourses[courses[1], default:[]].append(courses[0]) + } + + var queue = (0.. [Int] { + 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.. 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 + } +} 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.. [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.. Int { + guard let node = nodeMap[key], let list = listMap[node.count] else { + return -1 + } + updateExsit(node: node) + return node.val + } + + func put(_ key: Int, _ value: Int) { + if capacity == 0 { + return + } + + if let node = nodeMap[key], let list = listMap[node.count] { + node.val = value + updateExsit(node: node) + } else { + removeCacheIfNeeded() + + let node = CacheNode(key, value) + nodeMap[key] = node + listMap(add: node) + + size += 1 + leastFrequency = 1 + } + } + + private func updateExsit(node: CacheNode) { + guard let list = listMap[node.count] else { + return + } + list.remove(node) + + if list.isEmpty { + listMap[node.count] = nil + + if leastFrequency == node.count { + leastFrequency += 1 + } + } + + node.count += 1 + listMap(add: node) + } + + private func removeCacheIfNeeded() { + guard size >= capacity, let list = listMap[leastFrequency], let key = list.removeLast()?.key else { + return + } + size -= 1 + nodeMap[key] = nil + if list.isEmpty { + listMap[leastFrequency] = nil + } + } + + private func listMap(add node: CacheNode) { + let list = listMap[node.count, default: CacheList()] + list.add(node) + listMap[node.count] = list + } + +} + +class CacheList { + + private let head = CacheNode(0, 0) + private let tail = CacheNode(0, 0) + private var count = 0 + + var isEmpty: Bool { + return count <= 0 + } + + init() { + head.next = tail + tail.pre = head + } + + func add(_ node: CacheNode) { + head.next?.pre = node + node.next = head.next + node.pre = head + head.next = node + count += 1 + } + + + func remove(_ node: CacheNode) { + node.pre?.next = node.next + node.next?.pre = node.pre + node.next = nil + node.pre = nil + count -= 1 + } + + func removeLast() -> CacheNode? { + guard !isEmpty, let node = tail.pre else { + return nil + } + remove(node) + return node + } + +} + +class CacheNode { + let key: Int + var val: Int + var pre: CacheNode? + var next: CacheNode? + var count = 1 + + init(_ key: Int, _ val: Int) { + self.key = key + self.val = val + } +} diff --git a/LinkedList/LRUCache.swift b/LinkedList/LRUCache.swift new file mode 100644 index 00000000..832410c9 --- /dev/null +++ b/LinkedList/LRUCache.swift @@ -0,0 +1,90 @@ +/** + * Question Link: https://leetcode.com/problems/lru-cache/ + * Primary idea: Use linked list and hash map to build the cache. + * Time Complexity Per Action: O(1), Space Complexity: O(K) + * + */ + +class LRUCache { + + private let capacity: Int + private var count = 0 + + private let head = LRUCacheNode(0, 0) + private let tail = LRUCacheNode(0, 0) + + private var dict = [Int: LRUCacheNode]() + + init(_ capacity: Int) { + self.capacity = capacity + + head.next = tail + tail.pre = head + } + + func get(_ key: Int) -> Int { + if let node = dict[key] { + remove(key) + insert(node) + return node.val + } + return -1 + } + + func put(_ key: Int, _ value: Int) { + if let node = dict[key] { + node.val = value + remove(key) + insert(node) + return + } + + let node = LRUCacheNode(key, value) + dict[key] = node + if count == capacity, let tailKey = tail.pre?.key { + remove(tailKey) + } + insert(node) + } + + private func insert(_ node: LRUCacheNode) { + dict[node.key] = node + + node.next = head.next + head.next?.pre = node + node.pre = head + head.next = node + + count += 1 + } + + private func remove(_ key: Int) { + guard count > 0, let node = dict[key] else { + return + } + dict[key] = nil + + node.pre?.next = node.next + node.next?.pre = node.pre + node.pre = nil + node.next = nil + + count -= 1 + } + +} + +fileprivate class LRUCacheNode { + + let key: Int + var val: Int + + var pre: LRUCacheNode? + var next: LRUCacheNode? + + init(_ key: Int, _ val: Int) { + self.key = key + self.val = val + } + +} diff --git a/LinkedList/OddEvenLinkedList.swift b/LinkedList/OddEvenLinkedList.swift index 7e799b10..8009e41c 100644 --- a/LinkedList/OddEvenLinkedList.swift +++ b/LinkedList/OddEvenLinkedList.swift @@ -16,32 +16,30 @@ class OddEvenLinkedList { func oddEvenList(_ head: ListNode?) -> ListNode? { - guard head != nil && head!.next != nil else { + guard head != nil, head!.next != nil else { return head } - let evenHead = head!.next - var p = head - var q = evenHead - var isEndEven = true + var prev = head, post = head!.next, isEndEven = true + let evenStart = post - while q!.next != nil { - let node = q!.next + while post!.next != nil { + prev!.next = post!.next - p!.next = node + prev = post + post = post!.next - p = q - q = node isEndEven = !isEndEven } if isEndEven { - p!.next = evenHead + prev!.next = evenStart } else { - p!.next = nil - q!.next = evenHead + prev!.next = nil + post!.next = evenStart } + return head } -} \ No newline at end of file +} diff --git a/LinkedList/PalindromeLinkedList.swift b/LinkedList/PalindromeLinkedList.swift index 47b8de08..2e33e078 100644 --- a/LinkedList/PalindromeLinkedList.swift +++ b/LinkedList/PalindromeLinkedList.swift @@ -15,38 +15,39 @@ */ class PalindromeLinkedList { - func isPalindrome(head: ListNode?) -> Bool { - guard head != nil else { - return true - } - - var slow = head - var fast = head - var prev: ListNode? - var post = slow!.next + func isPalindrome(_ head: ListNode?) -> Bool { + var slow = head, fast = head, dummy: ListNode? = nil + // reverse first half while fast != nil && fast!.next != nil { fast = fast!.next!.next - slow!.next = prev - prev = slow - slow = post - post = post!.next + let nextNode = slow!.next + + slow!.next = dummy + + dummy = slow + slow = nextNode } + // go to the starting point when length of list is odd if fast != nil { - slow = post + if slow == nil { + return true + } + slow = slow!.next } - while prev != nil { - if prev!.val != slow!.val { + // compare reversed first and second half + while slow != nil { + if slow!.val != dummy!.val { return false + } else { + slow = slow!.next + dummy = dummy!.next } - - prev = prev!.next - slow = slow!.next } return true } -} \ No newline at end of file +} diff --git a/Math/AddBinary.swift b/Math/AddBinary.swift index 64bb2aba..5be893be 100644 --- a/Math/AddBinary.swift +++ b/Math/AddBinary.swift @@ -1,6 +1,6 @@ /** * Question Link: https://leetcode.com/problems/add-binary/ - * Primary idea: use Carry and iterate from last to start + * Primary idea: Two pointers: use carry and iterate from last to start * * Note: Swift does not have a way to access a character in a string with O(1), * thus we have to first transfer the string to a character array @@ -10,25 +10,25 @@ class AddBinary { func addBinary(_ a: String, _ b: String) -> String { - var sum = 0, carry = 0, res = "" - let aChars = Array(a.characters), bChars = Array(b.characters) - var i = aChars.count - 1, j = bChars.count - 1 - + let a = Array(a), b = Array(b) + var res = "", carry = 0, i = a.count - 1, j = b.count - 1 + while i >= 0 || j >= 0 || carry > 0 { - sum = carry + var sum = carry + if i >= 0 { - sum += Int(String(aChars[i]))! + sum += Int(String(a[i]))! i -= 1 } if j >= 0 { - sum += Int(String(bChars[j]))! + sum += Int(String(b[j]))! j -= 1 } + + res = "\(sum % 2)" + res carry = sum / 2 - sum = sum % 2 - res = String(sum) + res } - + return res } } \ No newline at end of file diff --git a/Math/AddTwoNumbers.swift b/Math/AddTwoNumbers.swift index df900c6b..3930ce6c 100644 --- a/Math/AddTwoNumbers.swift +++ b/Math/AddTwoNumbers.swift @@ -16,26 +16,18 @@ class AddTwoNumbers { func addTwoNumbers(l1: ListNode?, _ l2: ListNode?) -> ListNode? { - var carry = 0, l1 = l1, l2 = l2 - let dummy = ListNode(0) - var node = dummy + guard let l1 = l1 else {return l2} + guard let l2 = l2 else {return l1} - while l1 != nil || l2 != nil || carry != 0 { - if l1 != nil { - carry += l1!.val - l1 = l1!.next - } - if l2 != nil { - carry += l2!.val - l2 = l2!.next - } - - node.next = ListNode(carry % 10) - node = node.next! - - carry = carry / 10 + let outputNode = ListNode((l1.val + l2.val)%10) + if l1.val + l2.val > 9 { + outputNode.next = addTwoNumbers(addTwoNumbers(l1.next, l2.next), + ListNode(1)) + } else { + outputNode.next = addTwoNumbers(l1.next, l2.next) } - return dummy.next + + return outputNode } } \ No newline at end of file diff --git a/Math/Atoi.swift b/Math/Atoi.swift index 45059401..1b3c0242 100644 --- a/Math/Atoi.swift +++ b/Math/Atoi.swift @@ -6,55 +6,55 @@ */ class Atoi { - func myAtoi(str: String) -> Int { - var res = 0 - var flag = 1 - var index = 0 - let int_max = 2147483647 - let int_min = -2147483648 + func myAtoi(_ str: String) -> Int { + var res = 0, flag = 1, index = 0 + let intMax = 2147483647, intMin = -2147483648, strChars = Array(str) // trim - let content = [Character](str.characters) - while index < content.count { - guard content[index] == " " else { + while index < strChars.count { + let currentChar = strChars[index] + + // trim + guard currentChar.isWhitespace else { break } + index += 1 } - guard index < content.count else { + + guard index < strChars.count else { return res } // handle flag - if content[index] == "-" { + if strChars[index] == "-" { flag = -1 index += 1 - } else if content[index] == "+" { + } else if strChars[index] == "+" { index += 1 } - while index < content.count { - guard _isDigit(content[index]) else { + // cast to number + while index < strChars.count { + let currentChar = strChars[index] + + guard currentChar.isNumber else { break } - res = res * 10 + Int(String(content[index]))! + res = res * 10 + currentChar.wholeNumberValue! - if res >= int_max { + if res >= intMax { if flag == 1 { - return int_max - } else if res > int_max && flag == -1 { - return int_min + return intMax + } else if flag == -1 && res > intMax { + return intMin } } - + index += 1 } return flag * res } - - private func _isDigit(char: Character) -> Bool { - return char >= "0" && char <= "9" - } -} \ No newline at end of file +} diff --git a/Math/CountPrimes.swift b/Math/CountPrimes.swift index 8951ca46..72bda921 100644 --- a/Math/CountPrimes.swift +++ b/Math/CountPrimes.swift @@ -1,7 +1,7 @@ /** * Question Link: https://leetcode.com/problems/count-primes/ * Primary idea: Create a boolean array to determine prime or not, - * filter numbers are times of previous ones + * filter numbers are times of previous number starting from its square * * Time Complexity: O(n), Space Complexity: O(n) */ @@ -11,22 +11,28 @@ class CountPrimes { guard n > 2 else { return 0 } - - var isPrime = [Bool](repeating: true, count: n) + + // init isPrime bool array + var isPrime = Array(repeating: true, count: n) + isPrime[0] = false + isPrime[1] = false - for i in 2.. Int { - if divisor == 0 { - return Int.max - } - - let isNegative = (dividend < 0) != (divisor < 0) + let isPositive = (dividend < 0) == (divisor < 0) var dividend = abs(dividend), divisor = abs(divisor), count = 0 while dividend >= divisor { @@ -22,9 +18,25 @@ } dividend -= divisor << (shift - 1) - count += 1 << (shift - 1) + + count += (1 << (shift - 1)) + } + + return refactorCount(count, isPositive) + } + + private func refactorCount(_ count: Int, _ isPositive: Bool) -> Int { + let INTMAX = 2147483647 + var count = count + + if isPositive { + if count > INTMAX { + count = INTMAX + } + } else { + count *= -1 } - return isNegative ? -count : count + return count } } diff --git a/Math/ExcelSheetColumnNumber.swift b/Math/ExcelSheetColumnNumber.swift index 2de02383..93451e39 100644 --- a/Math/ExcelSheetColumnNumber.swift +++ b/Math/ExcelSheetColumnNumber.swift @@ -7,15 +7,13 @@ class ExcelSheetColumnNumber { func titleToNumber(s: String) -> Int { - var res = 0 - let scalarsOfA = "A".unicodeScalars + var result = 0 - for char in s.characters { - let scalars = String(char).unicodeScalars - let current = Int(scalars[scalars.startIndex].value - scalarsOfA[scalarsOfA.startIndex].value) + 1 - res = res * 26 + current + for c in s.unicodeScalars { + let value = Int(c.value) - 64 + result = (result * 26) + value } - return res + return result } -} \ No newline at end of file +} diff --git a/Math/FactorialTrailingZeroes.swift b/Math/FactorialTrailingZeroes.swift new file mode 100644 index 00000000..2065374d --- /dev/null +++ b/Math/FactorialTrailingZeroes.swift @@ -0,0 +1,13 @@ +/** + * Question Link: https://leetcode.com/problems/factorial-trailing-zeroes/ + * Primary idea: Calculate how many 5s the number has since it should have more 2 + * + * Time Complexity: O(logn), Space Complexity: O(1) + * + */ + +class FactorialTrailingZeroes { + func trailingZeroes(_ n: Int) -> Int { + return n > 0 ? n / 5 + trailingZeroes(n / 5) : 0 + } +} \ No newline at end of file diff --git a/Math/FractionToRecurringDecimal.swift b/Math/FractionToRecurringDecimal.swift new file mode 100644 index 00000000..cc401ffa --- /dev/null +++ b/Math/FractionToRecurringDecimal.swift @@ -0,0 +1,46 @@ +/** + * Question Link: https://leetcode.com/problems/fraction-to-recurring-decimal/ + * Primary idea: Get quotient and remainder, if remainder exists, then it is repeating. + * Time Complexity: O(logn), Space Complexity: O(n) + * + */ + +class FractionToRecurringDecimal { + func fractionToDecimal(_ numerator: Int, _ denominator: Int) -> String { + guard numerator != 0 else { + return "0" + } + guard denominator != 0 else { + fatalError("Invalid Denominator") + } + + var isPositive = (numerator < 0) == (denominator < 0), numerator = abs(numerator), res = "", numToIndex = [Int: Int](), hasDot = false + let denominator = abs(denominator) + + while numerator != 0 { + + let (q, r) = numerator.quotientAndRemainder(dividingBy: denominator) + + if let numIndex = numToIndex[numerator]{ + res.insert("(", at: res.index(res.startIndex, offsetBy: numIndex)) + res.append(")") + break + } else { + res += "\(q)" + + if !hasDot && r != 0 { + res.append(".") + hasDot = true + } else { + // update numToIndex + numToIndex[numerator] = res.count - 1 + } + + // update reminder + numerator = r * 10 + } + } + + return isPositive ? res : "-" + res + } +} \ No newline at end of file diff --git a/Math/GaryCode.swift b/Math/GaryCode.swift new file mode 100644 index 00000000..0397d0f4 --- /dev/null +++ b/Math/GaryCode.swift @@ -0,0 +1,18 @@ +/** + * Question Link: https://leetcode.com/problems/gray-code/ + * Primary idea: the result of n can be derived from (n - 1) by reversing + the order and prepending 1 to each number's binary representation + * + * Time Complexity: O(n), Space Complexity: O(2^n) + * + */ + +class GaryCode { + func grayCode(_ n: Int) -> [Int] { + var codes = [0] + for i in 0.. 0 { - count = z & 1 == 1 ? count + 1 : count + count += z & 1 z = z >> 1 } return count } -} \ No newline at end of file +} 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) + } +} diff --git a/Math/LineReflection.swift b/Math/LineReflection.swift new file mode 100644 index 00000000..eef51d3b --- /dev/null +++ b/Math/LineReflection.swift @@ -0,0 +1,33 @@ +/** + * Question Link: https://leetcode.com/problems/line-reflection/ + * Primary idea: Find a Line that should be y = (minX + maxX) / 2, then iterate through points and make sure that it has a reflected point in the opposite side. + * + * Time Complexity: O(n), Space Complexity: O(n) + */ + + class LineReflection { + func isReflected(_ points: [[Int]]) -> Bool { + var minX = Int.max, maxX = Int.min + var pointSet = Set<[Int]>() + + for point in points { + pointSet.insert(point) + minX = min(point[0], minX) + maxX = max(point[0], maxX) + } + + let sum = minX + maxX + + for item in pointSet { + if item[0] == sum { + continue + } + + if !pointSet.contains([sum - item[0], item[1]]) { + return false + } + } + + return true + } +} \ No newline at end of file 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 + } +} diff --git a/Math/MissingNumber.swift b/Math/MissingNumber.swift new file mode 100644 index 00000000..84f44c0a --- /dev/null +++ b/Math/MissingNumber.swift @@ -0,0 +1,13 @@ +/** + * Question Link: https://leetcode.com/problems/missing-number/ + * Primary idea: The distinct number is the sum from 0 to expected end number, which is + * exactly the array length, minus the sum of all the values in array + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class MissingNumber { + func missingNumber(_ nums: [Int]) -> Int { + return nums.count * (nums.count + 1) / 2 - nums.reduce(0, +) + } +} \ No newline at end of file diff --git a/Math/PermutationSequence.swift b/Math/PermutationSequence.swift new file mode 100644 index 00000000..8a00c053 --- /dev/null +++ b/Math/PermutationSequence.swift @@ -0,0 +1,39 @@ +/** + * Question Link: https://leetcode.com/problems/permutation-sequence/ + * Primary idea: Iterate and change the array from last to the first + * + * Time Complexity: O(n^2), Space Complexity: O(1) + */ + +class PermutationSequence { + func getPermutation(_ n: Int, _ k: Int) -> String { + var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] + + var factorial = 1 + for i in 1 ..< n { + factorial *= i + } + + var result = "" + var k = k + var divisor = n - 1 + + for i in 0 ..< n { + for (index, number) in numbers.enumerated() { + if k > factorial { + k -= factorial + } else { + result += "\(number)" + numbers.remove(at: index) + break + } + } + if divisor > 1 { + factorial /= divisor + divisor -= 1 + } + } + + return result + } +} \ No newline at end of file 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/Math/Pow.swift b/Math/Pow.swift index 1d08df23..a4d93fb2 100644 --- a/Math/Pow.swift +++ b/Math/Pow.swift @@ -7,37 +7,23 @@ class Pow { func myPow(x: Double, _ n: Int) -> Double { - guard n != 0 else { - return 1 - } - guard x != 0 else { - return 0 - } - - var res = _helper(abs(x), abs(n)) - + var x = x, n = n + if n < 0 { - res = 1 / res + x = 1.0 / x + n = -n } - if n % 2 != 0 && x < 0 { - res = -res + + var res = 1.0 + + while n > 0 { + if n % 2 != 0 { + res *= x + } + x *= x + n /= 2 } - + return res } - - private func _helper(x: Double, _ n: Int) -> Double { - guard n != 0 else { - return 1 - } - guard n != 1 else { - return x - } - - if n % 2 == 0 { - return _helper(x * x, n / 2) - } else { - return _helper(x, n - 1) * x - } - } } \ No newline at end of file diff --git a/Math/PowerTwo.swift b/Math/PowerTwo.swift index 2026ad6e..4405a7e7 100644 --- a/Math/PowerTwo.swift +++ b/Math/PowerTwo.swift @@ -1,6 +1,6 @@ /** * Question Link: https://leetcode.com/problems/power-of-two/ - * Primary idea: Use and to solve the problem + * Primary idea: Use and operation to solve the problem * Time Complexity: O(1), Space Complexity: O(1) * */ @@ -13,4 +13,4 @@ class PowerTwo { return n & (n - 1) == 0 } -} \ No newline at end of file +} diff --git a/Math/SparseMatrixMultiplication.swift b/Math/SparseMatrixMultiplication.swift new file mode 100644 index 00000000..89193a26 --- /dev/null +++ b/Math/SparseMatrixMultiplication.swift @@ -0,0 +1,37 @@ +/** + * Question Link: https://leetcode.com/problems/sparse-matrix-multiplication/ + * Primary idea: Use a matrix to mark indices of an matrix where is not zero to optimize the multiplication + * + * Time Complexity: O(n^3), Space Complexity: O(n^2) + */ + +class SparseMatrixMultiplication { + func multiply(_ A: [[Int]], _ B: [[Int]]) -> [[Int]] { + let l = A.count, m = B.count, n = B[0].count + + var res = Array(repeating: Array(repeating: 0, count: n), count: l) + + var nonZeroB = Array(repeating: [Int](), count: m) + for i in 0.. Bool { + var hasPoint = false, hasExponent = false, hasNumber = false + let s = Array(s.trimmingCharacters(in: .whitespaces)) + + for (i, char) in s.enumerated() { + if char.isNumber { + hasNumber = true + } else if char == "e" { + if hasExponent || i == s.count - 1 || !hasNumber { + return false + } else { + hasExponent = true + } + } else if char == "." { + if hasPoint || hasExponent { + return false + } else { + hasPoint = true + } + } else if char == "+" || char == "-" { + if i != 0 && (s[i - 1] != "e" || i == s.count - 1) { + return false + } + } else { + return false + } + } + + // s could be made up of all white spaces or all symbols + return hasNumber + } +} \ No newline at end of file diff --git a/Queue/ImplementQueueUsingStacks.swift b/Queue/ImplementQueueUsingStacks.swift new file mode 100644 index 00000000..df91f362 --- /dev/null +++ b/Queue/ImplementQueueUsingStacks.swift @@ -0,0 +1,46 @@ +/** + * Question Link: https://leetcode.com/problems/implement-queue-using-stacks/ + * Primary idea: queue + * Time Complexity: O(n), Space Complexity: O(n) + * + * Copyright © 2019 Ilyar Mnazhdin. All rights reserved. + + * Your MyQueue object will be instantiated and called as such: + * let obj = MyQueue() + * obj.push(x) + * let ret_2: Int = obj.pop() + * let ret_3: Int = obj.peek() + * let ret_4: Bool = obj.empty() + */ + +import Foundation + +class MyQueue { + var storage = [Int]() + + /** Initialize your data structure here. */ + init() { + + } + + /** Push element x to the back of queue. */ + func push(_ x: Int) { + storage.append(x) + } + + /** Removes the element from in front of queue and returns that element. */ + func pop() -> Int { + return storage.removeFirst() + } + + /** Get the front element. */ + func peek() -> Int { + guard let first = storage.first else { return 0} + return first + } + + /** Returns whether the queue is empty. */ + func empty() -> Bool { + return storage.isEmpty + } +} diff --git a/README.md b/README.md index 59d18168..5a1a1102 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,21 @@ ![Leetcode](./logo.png?style=centerme) +## Progress +[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 + +

+ +

+ ## Data Structures * [Array](#array) * [String](#string) * [Linked List](#linked-list) * [Stack](#stack) +* [Queue](#queue) * [Tree](#tree) * [Dynamic programming](#dynamic-programming) * [Depth-first search](#depth-first-search) @@ -15,7 +25,8 @@ * [Math](#math) * [Search](#search) * [Sort](#sort) -* [Union Find](#union-find) +* [Graph](#graph) +* [Design](#design) ## Companies * [Google](#google) @@ -27,13 +38,12 @@ * [Amazon](#amazon) * [Microsoft](#microsoft) -## Progress -[Problem Status](#problem-status) shows the latest progress to all 800+ questions. Currently we have 271 completed solutions. Note: questions with ♥ mark means that you have to **Subscript to premium membership** of LeetCode to unlock them. Thank you for great contributions from [CharleneJiang](https://github.com/CharleneJiang), [ReadmeCritic](https://github.com/ReadmeCritic), [demonkoo](https://github.com/demonkoo), [DaiYue](https://github.com/DaiYue), [Quaggie](https://github.com/Quaggie) and [jindulys](https://github.com/jindulys). - ## Array | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | +[Verify an Alien Dictionary](https://leetcode.com/problems/verifying-an-alien-dictionary/)|[Swift](Array/VerifyingAlienDictionary.swift)| Easy| O(n)| O(n)| +[Sort Array By Parity](https://leetcode.com/problems/sort-array-by-parity/)|[Swift](Array/SortArrayByParity.swift)| Easy| O(n)| O(n)| [Max Consecutive Ones](https://leetcode.com/problems/max-consecutive-ones/)| [Swift](./Array/MaxConsecutiveOnes.swift)| Easy| O(n)| O(1)| [Heaters](https://leetcode.com/problems/heaters/)| [Swift](./Array/Heaters.swift)| Easy| O(nlogn)| O(1)| [Number of Boomerangs](https://leetcode.com/problems/number-of-boomerangs/)| [Swift](./Array/NumberBoomerangs.swift)| Easy| O(n ^ 2)| O(n)| @@ -50,16 +60,23 @@ [Move Zeroes](https://leetcode.com/problems/move-zeroes/)| [Swift](./Array/MoveZeroes.swift)| Easy| O(n)| O(1)| [Remove Element](https://leetcode.com/problems/remove-element/)| [Swift](./Array/RemoveElement.swift)| Easy| O(n)| O(1)| [Strobogrammatic Number](https://leetcode.com/problems/strobogrammatic-number/)| [Swift](./Array/StrobogrammaticNumber.swift)| Easy| O(n)| O(1)| +[Can Place Flowers](https://leetcode.com/problems/can-place-flowers/)| [Swift](./Array/CanPlaceFlowers.swift)| Easy| O(n)| O(1)| [Two Sum](https://leetcode.com/problems/two-sum/)| [Swift](./Array/TwoSum.swift)| Easy| O(n)| O(n)| +[Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/)| [Swift](./Array/TwoSumII.swift)| Easy| O(n)| O(1)| +[Two Sum III - Data structure design](https://leetcode.com/problems/two-sum-iii-data-structure-design/)| [Swift](./Array/TwoSumIII.swift)| Easy| O(n)| O(1)| +[Two Sum Less Than K](https://leetcode.com/problems/two-sum-less-than-k/)| [Swift](./Array/TwoSumLessThanK.swift)| Easy| O(nlogn)| O(n)| [3Sum](https://leetcode.com/problems/3sum/)| [Swift](./Array/ThreeSum.swift)| Medium| O(n^2)| O(nC3)| -[3Sum Closest](https://leetcode.com/problems/3sum-closest/)| [Swift](./Array/ThreeSum.swift)| Medium| O(n^2)| O(nC3)| +[3Sum Closest](https://leetcode.com/problems/3sum-closest/)| [Swift](./Array/ThreeSumClosest.swift)| Medium| O(n^2)| O(nC3)| [4Sum](https://leetcode.com/problems/4sum/)| [Swift](./Array/FourSum.swift)| Medium| O(n^3)| O(nC4)| +[Increasing Triplet Subsequence](https://leetcode.com/problems/increasing-triplet-subsequence/)| [Swift](./Array/IncreasingTripletSubsequence.swift)| Medium| O(n)| O(1)| [Summary Ranges](https://leetcode.com/problems/summary-ranges/)| [Swift](./Array/SummaryRanges.swift)| Medium| O(n)| O(n)| [Range Sum Query 2D - Immutable](https://leetcode.com/problems/range-sum-query-2d-immutable/)| [Swift](./Array/NumMatrix.swift)| Medium| O(mn)| O(mn)| +[Missing Ranges](https://leetcode.com/problems/missing-ranges/)| [Swift](./Array/MissingRanges.swift)| Medium| O(n)| O(1)| [Asteroid Collision](https://leetcode.com/problems/asteroid-collision/)| [Swift](./Array/AsteroidCollision.swift)| Medium| O(n)| O(n)| [Maximize Distance to Closest Person](https://leetcode.com/problems/maximize-distance-to-closest-person/)| [Swift](./Array/MaximizeDistanceToClosestPerson.swift)| Easy| O(n)| O(1)| [Exam Room](https://leetcode.com/problems/exam-room/)| [Swift](./Array/ExamRoom.swift)| Medium| O(n)| O(n)| [Shortest Word Distance](https://leetcode.com/problems/shortest-word-distance/)| [Swift](./Array/ShortestWordDistance.swift)| Easy| O(n)| O(1)| +[Shortest Word Distance II](https://leetcode.com/problems/shortest-word-distance-ii/)| [Swift](./Array/ShortestWordDistanceII.swift)| Medium| O(n)| O(n)| [Shortest Word Distance III](https://leetcode.com/problems/shortest-word-distance-iii/)| [Swift](./Array/ShortestWordDistanceIII.swift)| Medium| O(n)| O(1)| [Minimum Size Subarray Sum](https://leetcode.com/problems/minimum-size-subarray-sum/)| [Swift](./Array/MinimumSizeSubarraySum.swift)| Medium| O(n)| O(1)| [Maximum Size Subarray Sum Equals k](https://leetcode.com/problems/maximum-size-subarray-sum-equals-k/)| [Swift](./Array/MaximumSizeSubarraySumEqualsK.swift)| Medium| O(n)| O(n)| @@ -68,18 +85,19 @@ [Rotate Array](https://leetcode.com/problems/rotate-array/)| [Swift](./Array/RotateArray.swift)| Easy| O(n)| O(1)| [Rotate Image](https://leetcode.com/problems/rotate-image/)| [Swift](./Array/RotateImage.swift)| Medium| O(n^2)| O(1)| [Spiral Matrix](https://leetcode.com/problems/spiral-matrix/)| [Swift](./Array/SpiralMatrix.swift)| Medium| O(n^2)| O(1)| -[Spiral Matrix II](https://leetcode.com/problems/spiral-matrix/)| [Swift](./Array/SpiralMatrixII.swift)| Medium| O(n^2)| O(1)| +[Spiral Matrix II](https://leetcode.com/problems/spiral-matrix-ii/)| [Swift](./Array/SpiralMatrixII.swift)| Medium| O(n^2)| O(1)| [Diagonal Traverse](https://leetcode.com/problems/diagonal-traverse/description/)| [Swift](./Array/DiagonalTraverse.swift)| Medium| O(mn)| O(1)| [Valid Sudoku](https://leetcode.com/problems/valid-sudoku/)| [Swift](./Array/ValidSudoku.swift)| Easy| O(n^2)| O(n)| -[Set Matrix Zero](https://leetcode.com/problems/set-matrix-zeroes/)| [Swift](./Array/SetMatrixZero.swift)| Medium| O(n^2)| O(1)| +[Set Matrix Zero](https://leetcode.com/problems/set-matrix-zeroes/)| [Swift](./Array/SetMatrixZeroes.swift)| Medium| O(n^2)| O(1)| [Next Permutation](https://leetcode.com/problems/next-permutation/)| [Swift](./Array/NextPermutation.swift)| Medium| O(n)| O(1)| [Gas Station](https://leetcode.com/problems/gas-station/)| [Swift](./Array/GasStation.swift)| Medium| O(n)| O(1)| [Game of Life](https://leetcode.com/problems/game-of-life/)| [Swift](./Array/GameLife.swift)| Medium| O(n)| O(1)| [Task Scheduler](https://leetcode.com/problems/task-scheduler/)| [Swift](./Array/TaskScheduler.swift)| Medium| O(nlogn)| O(n)| -[Sliding Window Maximum ](https://leetcode.com/problems/sliding-window-maximum/)| [Swift](./Array/SlidingWindowMaximum.swift)| Hard| O(n)| O(n)| +[Validate IP Address](https://leetcode.com/problems/validate-ip-address/)| [Swift](./Array/ValidateIPAddress.swift)| Medium| O(n)| O(1)| +[Sliding Window Maximum ](https://leetcode.com/problems/sliding-window-maximum/)| [Swift](./Array/SlidingWindowMaximum.swift)| Hard| O(n)| O(n)| [Longest Consecutive Sequence](https://leetcode.com/problems/longest-consecutive-sequence/)| [Swift](./Array/LongestConsecutiveSequence.swift)| Hard| O(n)| O(n)| [Create Maximum Number](https://leetcode.com/problems/create-maximum-number/)| [Swift](./Array/CreateMaximumNumber.swift)| Hard| O(n^2)| O(n)| - +[Find All Numbers Disappeared in an Array](https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/)| [Swift](./Array/FindDisappearedNumbers.swift)| Easy| O(n)| O(1)| ## String | Title | Solution | Difficulty | Time | Space | @@ -87,10 +105,11 @@ [Fizz Buzz](https://leetcode.com/problems/fizz-buzz/)| [Swift](./String/FizzBuzz.swift)| Easy| O(n)| O(1)| [First Unique Character in a String](https://leetcode.com/problems/first-unique-character-in-a-string/)| [Swift](./String/FirstUniqueCharacterInString.swift)| Easy| O(n)| O(1)| [Keyboard Row](https://leetcode.com/problems/keyboard-row/)| [Swift](./String/KeyboardRow.swift)| Easy| O(nm)| O(n)| +[Valid Word Abbreviation](https://leetcode.com/problems/valid-word-abbreviation/)| [Swift](./String/ValidWordAbbreviation.swift)| Easy| O(n)| O(n)| [Valid Palindrome](https://leetcode.com/problems/valid-palindrome/)| [Swift](./String/ValidPalindrome.swift)| Easy| O(n)| O(n)| [Valid Palindrome II](https://leetcode.com/problems/valid-palindrome-ii/)| [Swift](./String/ValidPalindromeII.swift)| Easy| O(n)| O(n)| [Detect Capital](https://leetcode.com/problems/detect-capital/)| [Swift](./String/DetectCapital.swift)| Easy| O(n)| O(1)| -[Count and Say](https://leetcode.com/problems/count-and-say/)| [Swift](./String/CountAndSay.swift)| Easy| O(n^2)| O(n)| +[Count and Say](https://leetcode.com/problems/count-and-say/)| [Swift](./String/CountAndSay.swift)| Easy| O(n^2)| O(1)| [Flip Game](https://leetcode.com/problems/flip-game/)| [Swift](./String/FlipGame.swift)| Easy| O(n)| O(n)| [Implement strStr()](https://leetcode.com/problems/implement-strstr/)| [Swift](./String/StrStr.swift)| Easy| O(nm)| O(n)| [Isomorphic Strings](https://leetcode.com/problems/isomorphic-strings/)| [Swift](./String/IsomorphicStrings.swift)| Easy| O(n)| O(n)| @@ -106,7 +125,7 @@ [Shortest Distance to a Character](https://leetcode.com/problems/shortest-distance-to-a-character/)| [Swift](./String/ShortestDistanceToACharacter.swift)| Easy| O(n)| O(1)| [Multiply Strings](https://leetcode.com/problems/multiply-strings/)| [Swift](./String/MultiplyStrings.swift)| Medium| O(n)| O(1)| [Palindrome Permutation](https://leetcode.com/problems/palindrome-permutation/)| [Swift](./String/PalindromePermutation.swift)| Easy| O(n)| O(n)| -[Valid Anagram](https://leetcode.com/problems/valid-anagram/)| [Swift](./String/ValidAnagram.swift)| Easy| O(nlogn)| O(1)| +[Valid Anagram](https://leetcode.com/problems/valid-anagram/)| [Swift](./String/ValidAnagram.swift)| Easy| O(n)| O(n)| [Ransom Note](https://leetcode.com/problems/ransom-note/)| [Swift](./String/RansomNote.swift)| Easy| O(n)| O(n)| [Group Anagrams](https://leetcode.com/problems/anagrams/)| [Swift](./String/GroupAnagrams.swift)| Medium| O(nmlogm + nlogn)| O(n) [Find Duplicate File in System](https://leetcode.com/problems/find-duplicate-file-in-system/)| [Swift](./String/FindDuplicateFileInSystem.swift)| Medium| O(nm)| O(n) @@ -114,10 +133,13 @@ [Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/)| [Swift](./String/LongestSubstringWithoutRepeatingCharacters.swift)| Medium| O(n)| O(n)| [One Edit Distance](https://leetcode.com/problems/one-edit-distance/)| [Swift](./String/OneEditDistance.swift)| Medium| O(n)| O(n)| [Word Pattern](https://leetcode.com/problems/word-pattern/)| [Swift](./String/WordPattern.swift)| Easy| O(n)| O(n)| -[Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/)| [Swift](./Array/MinimumWindowSubstring.swift)| Hard| O(n^2)| O(n)| +[Permutation in String](https://leetcode.com/problems/permutation-in-string/)| [Swift](/.String/PermutationInString.swift)| Medium| O(nm)| O(n)| +[Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/)| [Swift](/.String/FindAllAnagramsInAString.swift)| Medium| O(n)| O(n)| +[Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/)| [Swift](./String/MinimumWindowSubstring.swift)| Hard| O(n^2)| O(n)| [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)| +[Find the Closest Palindrome](https://leetcode.com/problems/find-the-closest-palindrome/)| [Swift](./String/FindClosestPalindrome.swift)| Hard| O(n)| O(n)| ## Linked List | Title | Solution | Difficulty | Time | Space | @@ -135,51 +157,68 @@ [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)| ## Stack | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | +[Min Stack](https://leetcode.com/problems/min-stack/)| [Swift](./Stack/MinStack.swift)| Easy| O(1)| O(n)| +[Max Stack](https://leetcode.com/problems/max-stack/)| [Swift](./Stack/MaxStack.swift)| Easy| O(n)| O(n)| [Valid Parentheses](https://leetcode.com/problems/valid-parentheses/)| [Swift](./Stack/ValidParentheses.swift)| Easy| O(n)| O(n)| [Longest Valid Parentheses](https://leetcode.com/problems/longest-valid-parentheses/)| [Swift](./Stack/LongestValidParentheses.swift)| Hard| O(n)| O(n)| [Evaluate Reverse Polish Notation](https://leetcode.com/problems/evaluate-reverse-polish-notation/)| [Swift](./Stack/EvaluateReversePolishNotation.swift)| Medium| O(n)| O(n)| +[Exclusive Time of Functions](https://leetcode.com/problems/exclusive-time-of-functions/)| [Swift](./Stack/ExclusiveTimeFunctions.swift) | Medium| O(n)| O(n)| [Simplify Path](https://leetcode.com/problems/simplify-path/)| [Swift](./Stack/SimplifyPath.swift)| Medium| O(n)| O(n)| [Remove K Digits](https://leetcode.com/problems/remove-k-digits/)| [Swift](./Stack/RemoveKDigits.swift)| Medium| O(n)| O(n)| [Ternary Expression Parser](https://leetcode.com/problems/ternary-expression-parser/)| [Swift](./Stack/TernaryExpressionParser.swift)| Medium| O(n)| O(n)| [Binary Tree Preorder Traversal](https://leetcode.com/problems/binary-tree-preorder-traversal/)| [Swift](./Stack/PreorderTraversal.swift)| Medium| O(n)| O(n)| [Binary Tree Inorder Traversal](https://leetcode.com/problems/binary-tree-inorder-traversal/)| [Swift](./Stack/InorderTraversal.swift)| Medium| O(n)| O(n)| +[Binary Search Tree Iterator](https://leetcode.com/problems/binary-search-tree-iterator/)| [Swift](./Stack/BinarySearchTreeIterator.swift)| Medium| O(n)| O(n)| [Binary Tree Postorder Traversal](https://leetcode.com/problems/binary-tree-postorder-traversal/)| [Swift](./Stack/PostorderTraversal.swift)| Hard| O(n)| O(n)| +[Decode String](https://leetcode.com/problems/decode-string/)| [Swift](./Stack/DecodeString.swift)| Medium| O(n)| O(n)| +[Basic Calculator](https://leetcode.com/problems/basic-calculator/)| [Swift](./Stack/BasicCalculator.swift)| Hard| O(n)| O(n)| +## Queue +| Title | Solution | Difficulty | Time | Space | +| ----- | -------- | ---------- | ---- | ----- | +[Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks)| [Swift](./Queue/ImplementQueueUsingStacks.swift)| Easy| O(n)| O(n)| ## Tree | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | [Same Tree](https://oj.leetcode.com/problems/same-tree/)| [Swift](./Tree/SameTree.swift)| Easy| O(n)| O(n)| [Symmetric Tree](https://leetcode.com/problems/symmetric-tree/)| [Swift](./Tree/SymmetricTree.swift)| Easy| O(n)| O(n)| -[Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/)| [Swift](./Tree/InvertBinaryTree)| Easy| O(n)| O(n)| -[Binary Tree Upside Down](https://leetcode.com/problems/binary-tree-upside-down/)| [Swift](./Tree/BinaryTreeUpsideDown)| Medium| O(n)| O(1)| +[Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/)| [Swift](./Tree/InvertBinaryTree.swift)| Easy| O(n)| O(n)| +[Binary Tree Upside Down](https://leetcode.com/problems/binary-tree-upside-down/)| [Swift](./Tree/BinaryTreeUpsideDown.swift)| Medium| O(n)| O(1)| [Minimum Depth of Binary Tree](https://leetcode.com/problems/minimum-depth-of-binary-tree/)| [Swift](./Tree/MinimumDepthOfBinaryTree.swift)| Easy| O(n)| O(1)| [Maximum Depth of Binary Tree](https://leetcode.com/problems/maximum-depth-of-binary-tree/)| [Swift](./Tree/MaximumDepthOfBinaryTree.swift)| Easy| O(n)| O(1)| [Diameter of Binary Tree](https://leetcode.com/problems/diameter-of-binary-tree/)| [Swift](./Tree/DiameterBinaryTree.swift)| Easy| O(n)| O(1)| [Balanced Binary Tree](https://leetcode.com/problems/balanced-binary-tree/)| [Swift](./Tree/BalancedBinaryTree.swift)| Easy| O(n)| O(n)| [Sum of Left Leaves](https://leetcode.com/problems/sum-of-left-leaves/)| [Swift](./Tree/SumLeftLeaves.swift)| Easy| O(n)| O(1)| [Flatten Binary Tree to Linked List](https://leetcode.com/problems/flatten-binary-tree-to-linked-list/)| [Swift](./Tree/FlattenBinaryTreeLinkedList.swift)| Medium| O(n)| O(1)| -[Validate Binary Search Tree](https://leetcode.com/problems/validate-binary-search-tree/)| [Swift](./Tree/ValidateBinarySearchTree.swift)| Medium| O(n)| O(n)| +[Convert Sorted Array to Binary Search Tree](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/)| [Swift](./Tree/ConvertSortedArrayBinarySearchTree.swift)| Easy| O(n)| O(1)| +[Validate Binary Search Tree](https://leetcode.com/problems/validate-binary-search-tree/)| [Swift](./Tree/ValidateBinarySearchTree.swift)| Medium| O(n)| O(log n)| [Binary Tree Level Order Traversal](https://leetcode.com/problems/binary-tree-level-order-traversal/)| [Swift](./Tree/BinaryTreeLevelOrderTraversal.swift)| Easy| O(n)| O(n)| [Binary Tree Level Order Traversal II](https://leetcode.com/problems/binary-tree-level-order-traversal-ii/)| [Swift](./Tree/BinaryTreeLevelOrderTraversalII.swift)| Easy| O(n)| O(n)| +[Merge Two Binary Trees](https://leetcode.com/problems/merge-two-binary-trees/description/) | [Swift](./Tree/MergeTwoBinaryTrees.swift) | Easy | O(n) | O(n) | [Binary Tree Zigzag Level Order Traversal](https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/)| [Swift](./Tree/BinaryTreeZigzagLevelOrderTraversal.swift)| Medium| O(n)| O(n)| [Binary Tree Vertical Order Traversal](https://leetcode.com/problems/binary-tree-vertical-order-traversal/)| [Swift](./Tree/BinaryTreeVerticalOrderTraversal.swift)| Medium| O(n)| O(n)| [Kth Smallest Element in a BST](https://leetcode.com/problems/kth-smallest-element-in-a-bst/)| [Swift](./Tree/KthSmallestElementBST.swift)| Medium| O(n)| O(n)| [Binary Tree Right Side View](https://leetcode.com/problems/binary-tree-right-side-view/)| [Swift](./Tree/BinaryTreeRightSideView.swift)| Medium| O(n)| O(n)| -[Construct Binary Tree from Preorder and Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)| [Swift](./Tree/ConstructBinaryTreePreorderInorder.swift)| Medium| O(n)| O(n)| -[Construct Binary Tree from Inorder and Postorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/)| [Swift](./Tree/ConstructBinaryTreeInorderPostorder.swift)| Medium| O(n)| O(n)| +[Construct Binary Tree from Preorder and Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)| [Swift](./Tree/ConstructBinaryTreePreorderInorder.swift)| Medium| O(nlogn)| O(1)| +[Construct Binary Tree from Inorder and Postorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/)| [Swift](./Tree/ConstructBinaryTreeInorderPostorder.swift)| Medium| O(nlogn)| O(1)| [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)| [Recover Binary Search Tree](https://leetcode.com/problems/recover-binary-search-tree/)| [Swift](./Tree/RecoverBinarySearchTree.swift)| Hard| O(n)| O(1)| -[Merge Two Binary Trees](https://leetcode.com/problems/merge-two-binary-trees/description/) | [Swift](./Tree/MergeTwoBinaryTrees.swift) | Easy | O(n) | O(n) | +[Serialize and Deserialize Binary Tree](https://leetcode.com/problems/serialize-and-deserialize-binary-tree/)| [Swift](./Tree/SerializeDeserializeBinaryTree.swift)| Hard| O(n)| O(n)| +[Serialize and Deserialize N-ary Tree](https://leetcode.com/problems/serialize-and-deserialize-n-ary-tree/)| [Swift](./Tree/SerializeDeserializeNAryTree.swift)| Hard| O(n)| O(n)| ## Dynamic programming | Title | Solution | Difficulty | Time | Space | @@ -189,9 +228,10 @@ [Min Cost Climbing Stairs](https://leetcode.com/problems/min-cost-climbing-stairs/)| [Swift](./DP/MinCostClimbingStairs.swift)| Easy| O(n)| O(n)| [Unique Paths](https://leetcode.com/problems/unique-paths/)| [Swift](./DP/UniquePaths.swift)| Medium| O(mn)| O(mn)| [Unique Paths II](https://leetcode.com/problems/unique-paths-ii/)| [Swift](./DP/UniquePathsII.swift)| Medium| O(mn)| O(mn)| +[Nested List Weight Sum II](https://leetcode.com/problems/nested-list-weight-sum-ii/)| [Swift](./DP/NestedListWeightSumII.swift)| Medium| O(n)| O(n)| [Flip Game II](https://leetcode.com/problems/flip-game-ii/)| [Swift](./DP/FlipGameII.swift)| Medium| O(n)| O(n)| [Can I Win](https://leetcode.com/problems/can-i-win/)| [Swift](./DP/CanIWin.swift)| Medium| O(2^n)| O(n)| -[Decode Ways](https://leetcode.com/problems/decode-ways/)| [Swift](./DP/DecodeWays.swift) | O(n)|O(n)| +[Decode Ways](https://leetcode.com/problems/decode-ways/)| [Swift](./DP/DecodeWays.swift) | Medium| O(n)|O(n)| [Minimum Path Sum](https://leetcode.com/problems/minimum-path-sum/)| [Swift](./DP/MinimumPathSum.swift)| Medium| O(mn)| O(mn)| [Generate Parentheses](https://leetcode.com/problems/generate-parentheses/)| [Swift](./DP/GenerateParentheses.swift)| Medium| O(2^n)| O(n)| [Different Ways to Add Parentheses](https://leetcode.com/problems/different-ways-to-add-parentheses/)| [Swift](./DP/DifferentWaysAddParentheses.swift)| Medium| O(n^n)| O(n)| @@ -200,10 +240,13 @@ [Best Time to Buy and Sell Stock III](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/)| [Swift](./DP/BestTimeBuySellStockIII.swift)| Hard| O(n)| O(n)| [Best Time to Buy and Sell Stock IV](https://leetcode.com/problems/https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/)| [Swift](./DP/BestTimeBuySellStockIV.swift)| Hard| O(n^2)| O(n)| [Best Time to Buy and Sell Stock with Cooldown](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/)| [Swift](./DP/BestTimeBuySellStockCooldown.swift)| Medium| O(n^2)| O(n)| -[Maximum Sum of 3 Non-Overlapping Subarrays](https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/)| [Swift](./DP/MaximumSumThreeNonOverlappingSubarrays.swift| Hard| O(n)| O(n)| +[Maximum Sum of 3 Non-Overlapping Subarrays](https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/)| [Swift](./DP/MaximumSumThreeNonOverlappingSubarrays.swift)| Hard| O(n)| O(n)| [Coin Change](https://leetcode.com/problems/coin-change/)| [Swift](./DP/CoinChange.swift)| Medium| O(n^2)| O(n)| [Coin Change II](https://leetcode.com/problems/coin-change-ii/)| [Swift](./DP/CoinChangeII.swift)| Medium| O(n^2)| O(n)| -[Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/)| [Swift](./DP/LongestIncreasingSubsequence.swift)| Medium| O(n^2)| O(n)| +[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)| @@ -212,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)| @@ -230,15 +274,15 @@ ## 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)| -[Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)| [Swift](./DFS/LetterCombinationsPhoneNumber.swift)| Medium| O(mn)| O(n)| +[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)| [Generalized Abbreviation](https://leetcode.com/problems/generalized-abbreviation/)| [Swift](./DFS/GeneralizedAbbreviation.swift)| Medium| O(n^n)| O(2^n)| @@ -249,9 +293,10 @@ [Word Search](https://leetcode.com/problems/word-search/)| [Swift](./DFS/WordSearch.swift)| Medium| O((mn * 4 ^ (k - 1))| O(mn)| [Word Search II](https://leetcode.com/problems/word-search-ii/)| [Swift](./DFS/WordSearchII.swift)| Hard| O(((mn)^2))| O(n^2)| [Add and Search Word - Data structure design](https://leetcode.com/problems/add-and-search-word-data-structure-design/)| [Swift](./DFS/WordDictionary.swift)| Medium| O(n)| O(n)| +[Partition to K Equal Sum Subsets](https://leetcode.com/problems/partition-to-k-equal-sum-subsets/)| [Swift](./DFS/PartitionKEqualSumSubsets.swift)| Medium| O(k^n)| O(n)| [N-Queens](https://leetcode.com/problems/n-queens/)| [Swift](./DFS/NQueens.swift)| Hard| O((n^n))| O(n^2)| [N-Queens II](https://leetcode.com/problems/n-queens-ii/)| [Swift](./DFS/NQueensII.swift)| Hard| O((n^n))| O(n)| -[Word Squares](https://leetcode.com/problems/word-squares/)| [Swift](./DFS/WordSquares.swift)| Hard| O((n^n))| O(n)| +[Word Squares](https://leetcode.com/problems/word-squares/)| [Swift](./DFS/WordSquares.swift)| Hard| O((n^2))| O(n^2)| [Word Pattern II](https://leetcode.com/problems/word-pattern-ii/)| [Swift](./DFS/WordPatternII.swift)| Hard| O(n^n)| O(n)| [Sudoku Solver](https://leetcode.com/problems/sudoku-solver/)| [Swift](./DFS/SudokuSolver.swift)| Hard| O(n^4)| O(1)| [Remove Invalid Parentheses](https://leetcode.com/problems/remove-invalid-parentheses/)| [Swift](./DFS/RemoveInvalidParentheses.swift)| Hard| O(n^n)| O(n)| @@ -260,6 +305,7 @@ ## Breadth-first search | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | +[Word Ladder](https://leetcode.com/problems/word-ladder/)| [Swift](./BFS/WordLadder.swift)| Medium| O(nm)| O(nm)| [Evaluate Division](https://leetcode.com/problems/evaluate-division/)| [Swift](./BFS/EvaluateDivision.swift)| Medium| O(n^2)| O(n)| [Shortest Distance from All Buildings](https://leetcode.com/problems/shortest-distance-from-all-buildings/)| [Swift](./BFS/ShortestDistanceAllBuildings.swift)| Hard| O((mn)^2)| O(mn)| @@ -270,10 +316,12 @@ [Add Two Numbers](https://leetcode.com/problems/add-two-numbers/)| [Swift](./Math/AddTwoNumbers.swift)| Medium| O(n)| O(1)| [Add Digits](https://leetcode.com/problems/add-digits/)| [Swift](./Math/AddDigits.swift)| Easy| O(1)| O(1)| [Plus One](https://leetcode.com/problems/plus-one/)| [Swift](./Math/PlusOne.swift)| Easy| O(n)| O(1)| +[Missing Number](https://leetcode.com/problems/missing-number/)| [Swift](./Math/MissingNumber.swift)| Easy| O(n)| O(1)| [Divide Two Integers](https://leetcode.com/problems/divide-two-integers/)| [Swift](./Math/DivideTwoIntegers.swift)| Medium| O(logn)| O(1)| [Number Complement](https://leetcode.com/problems/number-complement/)| [Swift](./Math/NumberComplement.swift)| Easy| O(n)| O(1)| [Hamming Distance](https://leetcode.com/problems/hamming-distance/)| [Swift](./Math/HammingDistance.swift)| Easy| O(n)| O(1)| [Integer Break](https://leetcode.com/problems/integer-break/)| [Swift](./Math/IntegerBreak.swift)| Medium| O(logn)| O(1)| +[Factorial Trailing Zeroes](https://leetcode.com/problems/factorial-trailing-zeroes/)| [Swift](./Math/FactorialTrailingZeroes.swift)| Medium | O(logn)| O(1)| [Happy Number](https://leetcode.com/problems/happy-number/)| [Swift](./Math/HappyNumber.swift)| Easy| O(n)| O(n)| [Single Number](https://leetcode.com/problems/single-number/)| [Swift](./Math/SingleNumber.swift)| Medium| O(n)| O(1)| [Ugly Number](https://leetcode.com/problems/ugly-number/)| [Swift](./Math/UglyNumber.swift)| Easy| O(logn)| O(1)| @@ -281,6 +329,7 @@ [Super Ugly Number](https://leetcode.com/problems/super-ugly-number/)| [Swift](./Math/SuperUglyNumber.swift)| Medium| O(n^2)| O(n)| [Count Primes](https://leetcode.com/problems/count-primes/)| [Swift](./Math/CountPrimes.swift)| Easy| O(n)| O(n)| [String to Integer (atoi)](https://leetcode.com/problems/string-to-integer-atoi/)| [Swift](./Math/Atoi.swift)| Easy| O(n)| O(1)| +[Fraction to Recurring Decimal](https://leetcode.com/problems/fraction-to-recurring-decimal/)| [Swift](./Math/FractionToRecurringDecimal.swift) | Medium| O(logn)| O(n)| [Pow(x, n)](https://leetcode.com/problems/isomorphic-strings/)| [Swift](./Math/Pow.swift)| Medium| O(logn)| O(1)| [Power of Two](https://leetcode.com/problems/power-of-two/)| [Swift](./Math/PowerTwo.swift)| Easy| O(1)| O(1)| [Power of Three](https://leetcode.com/problems/power-of-three/)| [Swift](./Math/PowerThree.swift)| Easy| O(1)| O(1)| @@ -291,12 +340,18 @@ [Integer to Roman](https://leetcode.com/problems/integer-to-roman/)| [Swift](./Math/IntegerToRoman.swift)| Medium| O(n)| O(1)| [Roman to Integer](https://leetcode.com/problems/roman-to-integer/)| [Swift](./Math/RomanToInteger.swift)| Easy| O(n)| O(n)| [Integer to English Words](https://leetcode.com/problems/integer-to-english-words/)| [Swift](./Math/IntegerEnglishWords.swift)| Hard| O(n)| O(1)| +[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)| +[Gary Code](https://leetcode.com/problems/gray-code/)| [Swift](./Math/GaryCode.swift)| Medium| O(n)| O(2^n)| +[Permutation Sequence](https://leetcode.com/problems/permutation-sequence/)| [Swift](./Math/PermutationSequence.swift)| Medium| O(n^2)| O(1)| +[Line Reflection](https://leetcode.com/problems/line-reflection/)| [Swift](./Math/LineReflection.swift)| Medium| O(n)| O(n)| +[Valid Number](https://leetcode.com/problems/valid-number/)| [Swift](./Math/ValidNumber.swift)| Hard| O(n)| O(1)| ## Search | Title | Solution | Difficulty | Time | Space | @@ -309,10 +364,11 @@ [Find Minimum in Rotated Sorted Array II](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/)| [Swift](./Search/FindMinimumRotatedSortedArrayII.swift)| Hard| O(logn)| O(1)| [Search a 2D Matrix](https://leetcode.com/problems/search-a-2d-matrix/)| [Swift](./Search/Search2DMatrix.swift)| Medium| O(log(m + n))| O(1)| [Search a 2D Matrix II](https://leetcode.com/problems/search-a-2d-matrix-ii/)| [Swift](./Search/Search2DMatrixII.swift)| Medium| O(m + n)| O(1)| -[Search for a Range](https://leetcode.com/problems/search-for-a-range/)| [Swift](./Search/SearchForARange.swift)| Medium| O(logn)| O(1)| -[Search Insert Position](https://leetcode.com/problems/search-insert-position/)| [Swift](./Search/SearchForARange.swift)| Medium| O(logn)| O(1)| +[Search for a Range](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/)| [Swift](./Search/SearchForARange.swift)| Medium| O(logn)| O(1)| +[Search Insert Position](https://leetcode.com/problems/search-insert-position/)| [Swift](./Search/SearchInsertPosition.swift)| Medium| O(logn)| O(1)| [Peak Index in a Mountain Array](https://leetcode.com/problems/peak-index-in-a-mountain-array/)| [Swift](./Search/PeakIndexMountainArray.swift)| Easy| O(logn)| O(1)| [Find Peak Element](https://leetcode.com/problems/find-peak-element/)| [Swift](./Search/FindPeakElement.swift)| Medium| O(logn)| O(1)| +[Random Pick with Weight](https://leetcode.com/problems/random-pick-with-weight/)| [Swift](./Search/RandomPickWeight.swift)| Medium| O(logn)| O(1)| [Sqrt(x)](https://leetcode.com/problems/sqrtx/)| [Swift](./Search/Sqrtx.swift)| Medium| O(logn)| O(1)| [Median of Two Sorted Arrays](https://leetcode.com/problems/median-of-two-sorted-arrays/)| [Swift](./Search/MedianTwoSortedArrays.swift)| Hard| O(log(m + n))| O(1)| [Minimize Max Distance to Gas Station](https://leetcode.com/problems/minimize-max-distance-to-gas-station/)| [Swift](./Search/MinimizeMaxDistanceGasStation.swift)| Hard| O(nlogm)| O(1)| @@ -325,24 +381,44 @@ [Wiggle Sort](https://leetcode.com/problems/wiggle-sort/)| [Swift](./Sort/WiggleSort.swift)| Medium| O(n)| O(1)| [Wiggle Sort II](https://leetcode.com/problems/wiggle-sort-ii/)| [Swift](./Sort/WiggleSortII.swift)| Medium| O(nlogn)| O(n)| [Sort Transformed Array](https://leetcode.com/problems/sort-transformed-array/)| [Swift](./Sort/SortTransformedArray.swift)| Medium| O(n)| O(1)| -[Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/)| [Swift](./Array/TopKFrequentElements.swift)| Medium| O(nlogn)| O(n)| +[Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/)| [Swift](./Sort/TopKFrequentElements.swift)| Medium| O(nlogn)| O(n)| [Meeting Rooms](https://leetcode.com/problems/meeting-rooms/)| [Swift](./Sort/MeetingRooms.swift)| Easy| O(nlogn)| O(1)| [Meeting Rooms II](https://leetcode.com/problems/meeting-rooms-ii/)| [Swift](./Sort/MeetingRoomsII.swift)| Medium| O(nlogn)| O(n)| [Merge Intervals](https://leetcode.com/problems/merge-intervals/)| [Swift](./Sort/MergeIntervals.swift)| Hard| O(nlogn)| O(n)| -[Alien Dictionary](https://leetcode.com/problems/alien-dictionary/)| [Swift](./Sort/AlienDictionary.swift)| Hard| O(nm)| O(nm)| +[Alien Dictionary](https://leetcode.com/problems/alien-dictionary/)| [Swift](./Graph/AlienDictionary.swift)| Hard| O(nm)| O(nm)| +[Kth Largest Element in an Array](https://leetcode.com/problems/kth-largest-element-in-an-array/)| [Swift](./Sort/KthLargestElementInArray.swift)| Medium| O(nlogn)| O(n)| [Array Partition I](https://leetcode.com/problems/array-partition-i/description/)| [Swift](./Sort/ArrayPartitionI.swift)|Easy| O(nlogn)| O(n)| [Insert Interval](https://leetcode.com/problems/insert-interval/description/)| [Swift](./Sort/InsertInterval.swift)|Hard| O(n)| O(1)| +[Largest Number](https://leetcode.com/problems/largest-number/)| [Swift](./Sort/LargestNumber.swift)| Medium| O(nlogn)| O(1)| -## Union Find +## Graph | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | -[Number of Connected Components in an Undirected Graph](https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/)| [Swift](./UnionFind/NumberConnectedComponentsUndirectedGraph.swift)| Medium| O(nlogn)| O(n)| -[Graph Valid Tree](https://leetcode.com/problems/graph-valid-tree/)| [Swift](./UnionFind/GraphValidTree.swift)| Medium| O(nlogn)| O(n)| -[Number of Islands II](https://leetcode.com/problems/number-of-islands-ii/)| [Swift](./UnionFind/NumberIslandsII.swift)| Hard| O(klogmn)| O(mn)| +[Number of Connected Components in an Undirected Graph](https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/)| [Swift](./Graph/NumberConnectedComponentsUndirectedGraph.swift)| Medium| O(nlogn)| O(n)| +[Graph Valid Tree](https://leetcode.com/problems/graph-valid-tree/)| [Swift](./Graph/GraphValidTree.swift)| Medium| O(nlogn)| O(n)| +[Number of Islands II](https://leetcode.com/problems/number-of-islands-ii/)| [Swift](./Graph/NumberIslandsII.swift)| Hard| O(klogmn)| O(mn)| +[Course Schedule](https://leetcode.com/problems/course-schedule/)| [Swift](./Graph/CourseSchedule.swift)| Medium| O(n)| O(n)| +[Course Schedule II](https://leetcode.com/problems/course-schedule-ii/)| [Swift](./Graph/CourseScheduleII.swift)| Medium| O(n)| O(n)| + +## Design +| 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 HashMap](https://leetcode.com/problems/design-hashmap/)| [Swift](./Design/DesignHashMap.swift)| Easy| O(n)| O(n)| +[Design Tic-Tac-Toe](https://leetcode.com/problems/design-tic-tac-toe/)| [Swift](./Design/DesignTicTacToe.swift)| Medium| O(1)| O(n)| +[Flatten Nested List Iterator](https://leetcode.com/problems/flatten-nested-list-iterator)| [Swift](./Design/FlattenNestedListIterator.swift)| Medium| O(n)| 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)| +[Add and Search Word - Data structure design](https://leetcode.com/problems/add-and-search-word-data-structure-design/)| [Swift](./Design/AddSearchWord.swift)| Medium | O(24^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)| +[LRU Cache](https://leetcode.com/problems/lru-cache/)| [Swift](./Design/LRUCache.swift)| Hard| O(1)| O(n)| +[All O`one Data Structure](https://leetcode.com/problems/all-oone-data-structure/)| [Swift](./Design/AllOne.swift)| Hard| O(1)| O(n)| + ## 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| ★★★★| @@ -370,7 +446,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| ★★★| @@ -388,12 +464,12 @@ [Meeting Rooms II](https://leetcode.com/problems/meeting-rooms-ii/)| [Swift](./Sort/MeetingRoomsII.swift)| Medium| ★★★★★★| [Valid Sudoku](https://leetcode.com/problems/valid-sudoku/)| [Swift](./Array/ValidSudoku.swift)| Easy| ★★★★★| [Binary Tree Vertical Order Traversal](https://leetcode.com/problems/binary-tree-vertical-order-traversal/)| [Swift](./Tree/BinaryTreeVerticalOrderTraversal.swift)| Medium| ★★★★| -[Alien Dictionary](https://leetcode.com/problems/alien-dictionary/)| [Swift](./Sort/AlienDictionary.swift)| Hard| ★★★★| +[Alien Dictionary](https://leetcode.com/problems/alien-dictionary/)| [Swift](./Graph/AlienDictionary.swift)| Hard| ★★★★| [One Edit Distance](https://leetcode.com/problems/one-edit-distance/)| [Swift](./String/OneEditDistance.swift)| Medium| ★★★| [Sudoku Solver](https://leetcode.com/problems/sudoku-solver/)| [Swift](./Math/SudokuSolver.swift)| Hard| ★★★| [Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/)| [Swift](./LinkedList/ReverseLinkedList.swift)| Easy| ★★| [Unique Binary Search Trees](https://leetcode.com/problems/unique-binary-search-trees/)| [Swift](./Tree/UniqueBinarySearchTrees.swift)| Medium| ★★| -[Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/)| [Swift](./Array/MinimumWindowSubstring.swift)| Hard| ★★| +[Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/)| [Swift](./String/MinimumWindowSubstring.swift)| Hard| ★★| [Remove K Digits](https://leetcode.com/problems/remove-k-digits/)| [Swift](./Stack/RemoveKDigits.swift)| Medium| ★| [Ternary Expression Parser](https://leetcode.com/problems/ternary-expression-parser/)| [Swift](./Stack/TernaryExpressionParser.swift)| Medium| ★| @@ -426,7 +502,7 @@ [Merge Intervals](https://leetcode.com/problems/merge-intervals/)| [Swift](./Sort/MergeIntervals.swift)| Hard| ★★★★★★| [Isomorphic Strings](https://leetcode.com/problems/isomorphic-strings/)| [Swift](./String/IsomorphicStrings.swift)| Easy| ★★★★★★| [Search in Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/)| [Swift](./Search/SearchInRotatedSortedArray.swift)| Hard| ★★★★★| -[Search for a Range](https://leetcode.com/problems/search-for-a-range/)| [Swift](./Search/SearchForARange.swift)| Medium| ★★★★★| +[Search for a Range](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/)| [Swift](./Search/SearchForARange.swift)| Medium| ★★★★★| [Two Sum](https://leetcode.com/problems/two-sum/)| [Swift](./Array/TwoSum.swift)| Easy| ★★★★| [Binary Tree Level Order Traversal](https://leetcode.com/problems/binary-tree-level-order-traversal/)| [Swift](./Tree/BinaryTreeLevelOrderTraversal.swift)| Easy| ★★★★| [Evaluate Reverse Polish Notation](https://leetcode.com/problems/evaluate-reverse-polish-notation/)| [Swift](./Stack/EvaluateReversePolishNotation.swift)| Medium| ★★★| @@ -469,6 +545,8 @@ ## Problem Status | Solution | Number | Title | Difficulty | | -------- | ------ | ----- | ---------- | +| [Swift](./LinkedList/LFUCache.swift) | 460 | [LFU Cache](https://oj.leetcode.com/problems/lfu-cache/) | Hard | +| [Swift](./Array/FindDisappearedNumbers.swift)| 448| [Find All Numbers Disappeared in an Array](https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/)| Easy| | [Swift](./DFS/CombinationSumIV.swift) | 377 | [Combination Sum IV](https://leetcode.com/problems/combination-sum-iv/) | Medium | | 376 | [Wiggle Subsequence](https://leetcode.com/problems/wiggle-subsequence/) | Medium | [Swift](./DP/GuessNumberHigherOrLowerII.swift) | 375 | [Guess Number Higher or Lower II](https://leetcode.com/problems/guess-number-higher-or-lower-ii/) | Medium @@ -482,7 +560,7 @@ | | 367 | [Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/) | Medium | | 366 | [Find Leaves of Binary Tree](https://leetcode.com/problems/find-leaves-of-binary-tree/) ♥ | Medium | | 365 | [Water and Jug Problem](https://leetcode.com/problems/water-and-jug-problem/) | Medium -| | 364 | [Nested List Weight Sum II](https://leetcode.com/problems/nested-list-weight-sum-ii/) ♥ | Medium +| [Swift](./DP/NestedListWeightSumII.swift) | 364 | [Nested List Weight Sum II](https://leetcode.com/problems/nested-list-weight-sum-ii/) ♥ | Medium | | 363 | [Max Sum of Rectangle No Larger Than K](https://leetcode.com/problems/max-sum-of-sub-matrix-no-larger-than-k/) | Hard | | 362 | [Design Hit Counter](https://leetcode.com/problems/design-hit-counter/) ♥ | Medium | | 361 | [Bomb Enemy](https://leetcode.com/problems/bomb-enemy/) ♥ | Medium @@ -505,14 +583,14 @@ | [Swift](./String/ReverseString.swift) | 344 | [Reverse String](https://leetcode.com/problems/reverse-string/) | Easy | [Swift](./Math/IntegerBreak.swift) | 343 | [Integer Break](https://leetcode.com/problems/integer-break/) | Medium | | 342 | [Power of Four](https://leetcode.com/problems/power-of-four/) | Easy -| | 341 | [Flatten Nested List Iterator](https://leetcode.com/problems/flatten-nested-list-iterator/) | Medium +| [Swift](./Design/FlattenNestedListIterator.swift) | 341 | [Flatten Nested List Iterator](https://leetcode.com/problems/flatten-nested-list-iterator/) | Medium | [Swift](./String/LongestSubstringMostKDistinctCharacters.swift) | 340 | [Longest Substring with At Most K Distinct Characters](https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/) ♥ | Hard | [Swift](./DP/NestedListWeightSum.swift) | 339 | [Nested List Weight Sum](https://leetcode.com/problems/nested-list-weight-sum/) ♥ | Easy | [Swift](./Math/CountingBits.swift) | 338 | [Counting Bits](https://leetcode.com/problems/counting-bits/) | Medium | [Swift](./Tree/HouseRobberIII.swift) | 337 | [House Robber III](https://leetcode.com/problems/house-robber-iii/) | Medium | | 336 | [Palindrome Pairs](https://leetcode.com/problems/palindrome-pairs/) | Hard | | 335 | [Self Crossing](https://leetcode.com/problems/self-crossing/) | Hard -| | 334 | [Increasing Triplet Subsequence](https://leetcode.com/problems/increasing-triplet-subsequence/) | Medium +| [Swift](./Tree/IncreasingTripletSubsequence.swift) | 334 | [Increasing Triplet Subsequence](https://leetcode.com/problems/increasing-triplet-subsequence/) | Medium | | 333 | [Largest BST Subtree](https://leetcode.com/problems/largest-bst-subtree/) ♥ | Medium | | 332 | [Reconstruct Itinerary](https://leetcode.com/problems/reconstruct-itinerary/) | Medium | | 331 | [Verify Preorder Serialization of a Binary Tree](https://leetcode.com/problems/verify-preorder-serialization-of-a-binary-tree/) | Medium @@ -535,13 +613,13 @@ | [Swift](./Tree/BinaryTreeVerticalOrderTraversal.swift) | 314 | [Binary Tree Vertical Order Traversal](https://leetcode.com/problems/binary-tree-vertical-order-traversal/) ♥ | Medium | [Swift](./Math/SuperUglyNumber.swift) | 313 | [Super Ugly Number](https://leetcode.com/problems/super-ugly-number/) | Medium | [Swift](./DP/GuessNumberHigherOrLowerII.swift) | 312 | [Burst Balloons](https://leetcode.com/problems/burst-balloons/) | Hard -| | 311 | [Sparse Matrix Multiplication](https://leetcode.com/problems/sparse-matrix-multiplication/) ♥ | Medium +| [Swift](./Math/SparseMatrixMultiplication.swift) | 311 | [Sparse Matrix Multiplication](https://leetcode.com/problems/sparse-matrix-multiplication/) ♥ | Medium | | 310 | [Minimum Height Trees](https://leetcode.com/problems/minimum-height-trees/) | Medium | [Swift](./DP/BestTimeBuySellStockCooldown.swift) | 309 | [Best Time to Buy and Sell Stock with Cooldown](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/) | Medium | | 308 | [Range Sum Query 2D - Mutable](https://leetcode.com/problems/range-sum-query-2d-mutable/) ♥ | Hard | | 307 | [Range Sum Query - Mutable](https://leetcode.com/problems/range-sum-query-mutable/) | Medium | | 306 | [Additive Number](https://leetcode.com/problems/additive-number/) | Medium -| [Swift](./UnionFind/NumberIslandsII.swift) | 305 | [Number of Islands II](https://leetcode.com/problems/number-of-islands-ii/) ♥ | Hard +| [Swift](./Graph/NumberIslandsII.swift) | 305 | [Number of Islands II](https://leetcode.com/problems/number-of-islands-ii/) ♥ | Hard | [Swift](./Array/NumMatrix.swift) | 304 | [Range Sum Query 2D - Immutable](https://leetcode.com/problems/range-sum-query-2d-immutable/) | Medium | | 303 | [Range Sum Query - Immutable](https://leetcode.com/problems/range-sum-query-immutable/) | Easy | | 302 | [Smallest Rectangle Enclosing Black Pixels](https://leetcode.com/problems/smallest-rectangle-enclosing-black-pixels/) ♥ | Hard @@ -577,31 +655,31 @@ | [Swift](./Search/ClosestBinarySearchTreeValueII.swift) | 272 | [Closest Binary Search Tree Value II](https://leetcode.com/problems/closest-binary-search-tree-value-ii/) ♥ | Hard | | | 271 | [Encode and Decode Strings](https://leetcode.com/problems/encode-and-decode-strings/) ♥ | Medium | | [Swift](./Search/ClosestBinarySearchTreeValue.swift) | 270 | [Closest Binary Search Tree Value](https://leetcode.com/problems/closest-binary-search-tree-value/) ♥ | Easy | -| [Swift](./Sort/AlienDictionary.swift) | 269 | [Alien Dictionary](https://leetcode.com/problems/alien-dictionary/) ♥ | Hard | -| | 268 | [Missing Number](https://leetcode.com/problems/missing-number/) | Medium | +| [Swift](./Graph/AlienDictionary.swift) | 269 | [Alien Dictionary](https://leetcode.com/problems/alien-dictionary/) ♥ | Hard | +| [Swift](./Math/MissingNumber.swift) | 268 | [Missing Number](https://leetcode.com/problems/missing-number/) | Easy | | | 267 | [Palindrome Permutation II](https://leetcode.com/problems/palindrome-permutation-ii/) ♥ | Medium | | [Swift](./String/PalindromePermutation.swift) | 266 | [Palindrome Permutation](https://leetcode.com/problems/palindrome-permutation/) ♥ | Easy | -| | 265 | [Paint House II](https://leetcode.com/problems/paint-house-ii/) ♥ | Hard | +| [Swift](./DP/PaintHouseII.swift) | 265 | [Paint House II](https://leetcode.com/problems/paint-house-ii/) ♥ | Hard | | [Swift](./Math/UglyNumberII.swift) | 264 | [Ugly Number II](https://leetcode.com/problems/ugly-number-ii/) | Medium | | [Swift](./Math/UglyNumber.swift) | 263 | [Ugly Number](https://leetcode.com/problems/ugly-number/) | Easy | | [Swift](./Sort/GraphValidTree.swift) | 261 | [Graph Valid Tree](https://leetcode.com/problems/graph-valid-tree/) ♥ | Medium | | | 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 | -| | 256 | [Paint House](https://leetcode.com/problems/paint-house/) ♥ | Medium | +| [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 | | [Swift](./Sort/MeetingRoomsII.swift) | 253 | [Meeting Rooms II](https://leetcode.com/problems/meeting-rooms-ii/) ♥ | Medium | | [Swift](./Sort/MeetingRooms.swift) | 252 | [Meeting Rooms](https://leetcode.com/problems/meeting-rooms/) ♥ | Easy | -| | 251 | [Flatten 2D Vector](https://leetcode.com/problems/flatten-2d-vector/) ♥ | Medium | +| [Swift](./Design/Vector2D.swift) | 251 | [Flatten 2D Vector](https://leetcode.com/problems/flatten-2d-vector/) ♥ | Medium | | | 250 | [Count Univalue Subtrees](https://leetcode.com/problems/count-univalue-subtrees/) ♥ | Medium | | | 249 | [Group Shifted Strings](https://leetcode.com/problems/group-shifted-strings/) ♥ | Easy | | | 248 | [Strobogrammatic Number III](https://leetcode.com/problems/strobogrammatic-number-iii/) ♥ | Hard | | [Swift](./DFS/StrobogrammaticNumberII.swift) | 247 | [Strobogrammatic Number II](https://leetcode.com/problems/strobogrammatic-number-ii/) ♥ | Medium | | [Swift](./Array/StrobogrammaticNumber.swift) | 246 | [Strobogrammatic Number](https://leetcode.com/problems/strobogrammatic-number/) ♥ | Easy | | [Swift](./Array/ShortestWordDistanceIII.swift) | 245 | [Shortest Word Distance III](https://leetcode.com/problems/shortest-word-distance-iii/) ♥ | Medium | -| | 244 | [Shortest Word Distance II](https://leetcode.com/problems/shortest-word-distance-ii/) ♥ | Medium | +| [Swift](./String/ShortestWordDistanceII.swift) | 244 | [Shortest Word Distance II](https://leetcode.com/problems/shortest-word-distance-ii/) ♥ | Medium | | [Swift](./String/ShortestWordDistance.swift) | 243 | [Shortest Word Distance](https://leetcode.com/problems/shortest-word-distance/) ♥ | Easy | | [Swift](./String/ValidAnagram.swift) | 242 | [Valid Anagram](https://leetcode.com/problems/valid-anagram/) | Easy | | [Swift](./DP/DifferentWaysAddParentheses.swift) | 241 | [Different Ways to Add Parentheses](https://leetcode.com/problems/different-ways-to-add-parentheses/) | Medium | @@ -610,10 +688,10 @@ | [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 | -| | 232 | [Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks/) | Easy | +| [Swift](./Queue/ImplementQueueUsingStacks.swift) | 232 | [Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks/) | Easy | | [Swift](./Math/PowerTwo.swift) | 231 | [Power of Two](https://leetcode.com/problems/power-of-two/) | Easy | | [Swift](./Tree/KthSmallestElementBST.swift) | 230 | [Kth Smallest Element in a BST](https://leetcode.com/problems/kth-smallest-element-in-a-bst/) | Medium | | [Swift](./Array/MajorityElementII.swift) | 229 | [Majority Element II](https://leetcode.com/problems/majority-element-ii/) | Medium | @@ -621,7 +699,7 @@ | | 227 | [Basic Calculator II](https://leetcode.com/problems/basic-calculator-ii/) | Medium | | [Swift](./Tree/InvertBinaryTree.swift) | 226 | [Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/) | Easy | | | 225 | [Implement Stack using Queues](https://leetcode.com/problems/implement-stack-using-queues/) | Easy | -| | 224 | [Basic Calculator](https://leetcode.com/problems/basic-calculator/) | Hard | +| [Swift](./Stack/BasicCalculator.swift) | 224 | [Basic Calculator](https://leetcode.com/problems/basic-calculator/) | Hard | | [Swift](./Math/RectangleArea.swift) | 223 | [Rectangle Area](https://leetcode.com/problems/rectangle-area/) | Easy | | | 222 | [Count Complete Tree Nodes](https://leetcode.com/problems/count-complete-tree-nodes/) | Medium | | [Swift](./DP/MaximalSquare.swift) | 221 | [Maximal Square](https://leetcode.com/problems/maximal-square/) | Medium | @@ -630,15 +708,15 @@ | | 218 | [The Skyline Problem](https://leetcode.com/problems/the-skyline-problem/) | Hard | | [Swift](./Array/ContainsDuplicate.swift) | 217 | [Contains Duplicate](https://leetcode.com/problems/contains-duplicate/) | Easy | | [Swift](./DFS/combinationSumIII.swift) | 216 | [Combination Sum III](https://leetcode.com/problems/combination-sum-iii/) | Medium | -| | 215 | [Kth Largest Element in an Array](https://leetcode.com/problems/kth-largest-element-in-an-array/) | Medium | +| [Swift](./Sort/KthLargestElementInArray.swift) | 215 | [Kth Largest Element in an Array](https://leetcode.com/problems/kth-largest-element-in-an-array/) | Medium | | | 214 | [Shortest Palindrome](https://leetcode.com/problems/shortest-palindrome/) | Hard | | [Swift](./DP/HouseRobberII.swift) | 213 | [House Robber II](https://leetcode.com/problems/house-robber-ii/) | Medium | | [Swift](./DFS/WordSearchII.swift) | 212 | [Word Search II](https://leetcode.com/problems/word-search-ii/) | Hard | | [Swift](./DFS/WordDictionary.swift) | 211 | [Add and Search Word - Data structure design](https://leetcode.com/problems/add-and-search-word-data-structure-design/) | Medium | -| | 210 | [Course Schedule II](https://leetcode.com/problems/course-schedule-ii/) | Medium | +| [Swift](./Graph/CourseScheduleII.swift) | 210 | [Course Schedule II](https://leetcode.com/problems/course-schedule-ii/) | Medium | | [Swift](./Array/MinimumSizeSubarraySum.swift) | 209 | [Minimum Size Subarray Sum](https://leetcode.com/problems/minimum-size-subarray-sum/) | Medium | -| | 208 | [Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/) | Medium | -| | 207 | [Course Schedule](https://leetcode.com/problems/course-schedule/) | Medium | +| [Swift](./Design/ImplementTrie.swift) | 208 | [Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/) | Medium | +| [Swift](./Graph/CourseSchedule.swift) | 207 | [Course Schedule](https://leetcode.com/problems/course-schedule/) | Medium | | [Swift](./LinkedList/ReverseLinkedList.swift) | 206 | [Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/) | Easy | | [Swift](./String/IsomorphicStrings.swift) | 205 | [Isomorphic Strings](https://leetcode.com/problems/isomorphic-strings/) | Easy | | [Swift](./Math/CountPrimes.swift) | 204 | [Count Primes](https://leetcode.com/problems/count-primes/) | Easy | @@ -654,19 +732,19 @@ | [Swift](./DP/BestTimeBuySellStockIV.swift) | 188 | [Best Time to Buy and Sell Stock IV](https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/) | Hard | | | 187 | [Repeated DNA Sequences](https://oj.leetcode.com/problems/repeated-dna-sequences/) | Medium | | [Swift](./String/ReverseWordsStringII.swift) | 186 | [Reverse Words in a String II](https://oj.leetcode.com/problems/reverse-words-in-a-string-ii/) ♥ | Medium | -| | 179 | [Largest Number](https://oj.leetcode.com/problems/largest-number/) | Medium | +| [Swift]((./Sort/LargestNumber.swift)) | 179 | [Largest Number](https://oj.leetcode.com/problems/largest-number/) | Medium | | | 174 | [Dungeon Game](https://oj.leetcode.com/problems/dungeon-game/) | Hard | -| | 173 | [Binary Search Tree Iterator](https://oj.leetcode.com/problems/binary-search-tree-iterator/) | Medium | -| | 172 | [Factorial Trailing Zeroes](https://oj.leetcode.com/problems/factorial-trailing-zeroes/) | Easy | +| [Swift](./Stack/BinarySearchTreeIterator.swift) | 173 | [Binary Search Tree Iterator](https://oj.leetcode.com/problems/binary-search-tree-iterator/) | Medium | +| [Swift](./Math/FactorialTrailingZeroes.swift) | 172 | [Factorial Trailing Zeroes](https://oj.leetcode.com/problems/factorial-trailing-zeroes/) | Easy | | [Swift](./Math/ExcelSheetColumnNumber.swift) | 171 | [Excel Sheet Column Number](https://oj.leetcode.com/problems/excel-sheet-column-number/) | Easy | -| | 170 | [Two Sum III - Data structure design](https://oj.leetcode.com/problems/two-sum-iii-data-structure-design/) ♥ | Easy | -| [Swift](./Array/MajorityElement.swift) | 169 | [Majority Element](https://oj.leetcode.com/problems/majority-element/) | Easy | +| [Swift](./Array/TwoSumIII.swift) | 170 | [Two Sum III - Data structure design](https://oj.leetcode.com/problems/two-sum-iii-data-structure-design/) ♥ | Easy | +| [Swift](./Array/MajorityElement.swift) | 169 | [Majority Element](https://oj.leetcode.com/problems/majority-element/) | Easy | | | 168 | [Excel Sheet Column Title](https://oj.leetcode.com/problems/excel-sheet-column-title/) | Easy | -| | 167 | [Two Sum II - Input array is sorted](https://oj.leetcode.com/problems/two-sum-ii-input-array-is-sorted/) ♥ | Medium | -| | 166 | [Fraction to Recurring Decimal](https://oj.leetcode.com/problems/fraction-to-recurring-decimal/) | Medium | +| [Swift](./Array/TwoSumII.swift) | 167 | [Two Sum II - Input array is sorted](https://oj.leetcode.com/problems/two-sum-ii-input-array-is-sorted/) ♥ | Medium | +| [Swift](./Math/FractionToRecurringDecimal.swift) | 166 | [Fraction to Recurring Decimal](https://oj.leetcode.com/problems/fraction-to-recurring-decimal/) | Medium | | | 165 | [Compare Version Numbers](https://oj.leetcode.com/problems/compare-version-numbers/) | Easy | | | 164 | [Maximum Gap](https://oj.leetcode.com/problems/maximum-gap/) | Hard | -| | 163 | [Missing Ranges](https://oj.leetcode.com/problems/missing-ranges/) ♥ | Medium | +| [Swift](./Array/MissingRanges.swift) | 163 | [Missing Ranges](https://oj.leetcode.com/problems/missing-ranges/) ♥ | Medium | | [Swift](./Search/FindPeakElement.swift) | 162 | [Find Peak Element](https://oj.leetcode.com/problems/find-peak-element/) | Medium | | [Swift](./String/OneEditDistance.swift) | 161 | [One Edit Distance](https://oj.leetcode.com/problems/one-edit-distance/)♥ | Medium | | | 160 | [Intersection of Two Linked Lists](https://oj.leetcode.com/problems/intersection-of-two-linked-lists/) | Easy | @@ -674,7 +752,7 @@ | | 158 | [Read N Characters Given Read4 II - Call multiple times](https://oj.leetcode.com/problems/read-n-characters-given-read4-ii-call-multiple-times/) ♥ | Hard | | | 157 | [Read N Characters Given Read4](https://oj.leetcode.com/problems/read-n-characters-given-read4/) ♥ | Easy | | [Swift](./Tree/BinaryTreeUpsideDown) | 156 | [Binary Tree Upside Down](https://oj.leetcode.com/problems/binary-tree-upside-down/) ♥ | Medium | -| | 155 | [Min Stack](https://oj.leetcode.com/problems/min-stack/) | Easy | +| [Swift](./Stack/MinStack.swift) | 155 | [Min Stack](https://oj.leetcode.com/problems/min-stack/) | Easy | | [Swift](./Search/FindMinimumRotatedSortedArrayII.swift) | 154 | [Find Minimum in Rotated Sorted Array II](https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/) | Hard | | [Swift](./Search/FindMinimumRotatedSortedArray.swift) | 153 | [Find Minimum in Rotated Sorted Array](https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array/) | Medium | | [Swift](./DP/MaximumProductSubarray.swift) | 152 | [Maximum Product Subarray](https://oj.leetcode.com/problems/maximum-product-subarray/) | Medium | @@ -683,7 +761,7 @@ | | 149 | [Max Points on a Line](https://oj.leetcode.com/problems/max-points-on-a-line/) | Hard | | | 148 | [Sort List](https://oj.leetcode.com/problems/sort-list/) | Medium | | | 147 | [Insertion Sort List](https://oj.leetcode.com/problems/insertion-sort-list/) | Medium | -| | 146 | [LRU Cache](https://oj.leetcode.com/problems/lru-cache/) | Hard | +| [Swift](./LinkedList/LRUCache.swift) | 146 | [LRU Cache](https://oj.leetcode.com/problems/lru-cache/) | Hard | | [Swift](./Stack/PostorderTraversal.swift) | 145 | [Binary Tree Postorder Traversal](https://oj.leetcode.com/problems/binary-tree-postorder-traversal/) | Hard | | [Swift](./Stack/PreorderTraversal.swift) | 144 | [Binary Tree Preorder Traversal](https://oj.leetcode.com/problems/binary-tree-preorder-traversal/) | Medium | | [Swift](./LinkedList/ReorderList.swift) | 143 | [Reorder List](https://oj.leetcode.com/problems/reorder-list/) | Medium | @@ -702,10 +780,10 @@ | | 130 | [Surrounded Regions](https://oj.leetcode.com/problems/surrounded-regions/) | Medium | | | 129 | [Sum Root to Leaf Numbers](https://oj.leetcode.com/problems/sum-root-to-leaf-numbers/) | Medium | | [Swift](./Array/LongestConsecutiveSequence.swift) | 128 | [Longest Consecutive Sequence](https://oj.leetcode.com/problems/longest-consecutive-sequence/) | Hard | -| | 127 | [Word Ladder](https://oj.leetcode.com/problems/word-ladder/) | Medium | +| [Swift](./BFS/WordLadder.swift) | 127 | [Word Ladder](https://oj.leetcode.com/problems/word-ladder/) | Medium | | | 126 | [Word Ladder II](https://oj.leetcode.com/problems/word-ladder-ii/) | Hard | | [Swift](./String/ValidPalindrome.swift) | 125 | [Valid Palindrome](https://oj.leetcode.com/problems/valid-palindrome/) | Easy | -| | 124 | [Binary Tree Maximum Path Sum](https://oj.leetcode.com/problems/binary-tree-maximum-path-sum/) | Hard | +| [Swift](./Tree/BinaryTreeMaximumPathSum.swift) | 124 | [Binary Tree Maximum Path Sum](https://oj.leetcode.com/problems/binary-tree-maximum-path-sum/) | Hard | | [Swift](./DP/BestTimeBuySellStockIII.swift) | 123 | [Best Time to Buy and Sell Stock III](https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/) | Hard | | [Swift](./DP/BestTimeBuySellStockII.swift) | 122 | [Best Time to Buy and Sell Stock II](https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/) | Medium | | [Swift](./DP/BestTimeBuySellStock.swift) | 121 | [Best Time to Buy and Sell Stock](https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock/) | Easy | @@ -721,7 +799,7 @@ | [Swift](./Tree/MinimumDepthOfBinaryTree.swift) | 111 | [Minimum Depth of Binary Tree](https://oj.leetcode.com/problems/minimum-depth-of-binary-tree/) | Easy | | [Swift](./Tree/BalancedBinaryTree.swift) | 110 | [Balanced Binary Tree](https://oj.leetcode.com/problems/balanced-binary-tree/) | Easy | | | 109 | [Convert Sorted List to Binary Search Tree](https://oj.leetcode.com/problems/convert-sorted-list-to-binary-search-tree/) | Medium | -| | 108 | [Convert Sorted Array to Binary Search Tree](https://oj.leetcode.com/problems/convert-sorted-array-to-binary-search-tree/) | Medium | +| [Swift](./Tree/ConvertSortedArrayBinarySearchTree.swift) | 108 | [Convert Sorted Array to Binary Search Tree](https://oj.leetcode.com/problems/convert-sorted-array-to-binary-search-tree/) | Medium | | [Swift](./Tree/BinaryTreeLevelOrderTraversalII.swift) | 107 | [Binary Tree Level Order Traversal II](https://oj.leetcode.com/problems/binary-tree-level-order-traversal-ii/) | Easy | | [Swift](./Tree/ConstructBinaryTreeInorderPostorder.swift) | 106 | [Construct Binary Tree from Inorder and Postorder Traversal](https://oj.leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/) | Medium | | [Swift](./Tree/ConstructBinaryTreePreorderInorder.swift) | 105 | [Construct Binary Tree from Preorder and Inorder Traversal](https://oj.leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) | Medium | @@ -738,9 +816,9 @@ | [Swift](./Stack/InorderTraversal.swift) | 94 | [Binary Tree Inorder Traversal](https://oj.leetcode.com/problems/binary-tree-inorder-traversal/) | Medium | | | 93 | [Restore IP Addresses](https://oj.leetcode.com/problems/restore-ip-addresses/) | Medium | | | 92 | [Reverse Linked List II](https://oj.leetcode.com/problems/reverse-linked-list-ii/) | Medium | -| (./DP/DecodeWays.swift) | 91 | [Decode Ways](https://oj.leetcode.com/problems/decode-ways/) | Medium | +| [Swift](./DP/DecodeWays.swift) | 91 | [Decode Ways](https://oj.leetcode.com/problems/decode-ways/) | Medium | | [Swift](./DFS/SubsetsII.swift) | 90 | [Subsets II](https://oj.leetcode.com/problems/subsets-ii/) | Medium | -| | 89 | [Gray Code](https://oj.leetcode.com/problems/gray-code/) | Medium | +| [Swift](./Math/GaryCode.swift) | 89 | [Gray Code](https://oj.leetcode.com/problems/gray-code/) | Medium | | [Swift](./Sort/MergeSortedArray.swift) | 88 | [Merge Sorted Array](https://oj.leetcode.com/problems/merge-sorted-array/) | Easy | | | 87 | [Scramble String](https://oj.leetcode.com/problems/scramble-string/) | Hard | | [Swift](./LinkedList/PartitionList.swift) | 86 | [Partition List](https://oj.leetcode.com/problems/partition-list/) | Medium | @@ -764,12 +842,12 @@ | [Swift](./String/TextJustification.swift) | 68 | [Text Justification](https://oj.leetcode.com/problems/text-justification/) | Hard | | [Swift](./Math/AddBinary.swift) | 67 | [Add Binary](https://oj.leetcode.com/problems/add-binary/) | Easy | | [Swift](./Math/PlusOne.swift) | 66 | [Plus One](https://oj.leetcode.com/problems/plus-one/) | Easy | -| | 65 | [Valid Number](https://oj.leetcode.com/problems/valid-number/) | Hard | +| [Swift](./Math/ValidNumber.swift) | 65 | [Valid Number](https://oj.leetcode.com/problems/valid-number/) | Hard | | [Swift](./DP/MinimumPathSum.swift) | 64 | [Minimum Path Sum](https://oj.leetcode.com/problems/minimum-path-sum/) | Medium | | [Swift](./DP/UniquePathsII.swift) | 63 | [Unique Paths II](https://oj.leetcode.com/problems/unique-paths-ii/) | Medium | | [Swift](./DP/UniquePaths.swift) | 62 | [Unique Paths](https://oj.leetcode.com/problems/unique-paths/) | Medium | | [Swift](./LinkedList/RotateList.swift) | 61 | [Rotate List](https://oj.leetcode.com/problems/rotate-list/) | Medium | -| | 60 | [Permutation Sequence](https://oj.leetcode.com/problems/permutation-sequence/) | Medium | +| [Swift](./Math/PermutationSequence.swift) | 60 | [Permutation Sequence](https://oj.leetcode.com/problems/permutation-sequence/) | Medium | | [Swift](./Array/SpiralMatrixII.swift) | 59 | [Spiral Matrix II](https://oj.leetcode.com/problems/spiral-matrix-ii/) | Medium | | [Swift](./String/LengthLastWord.swift) | 58 | [Length of Last Word](https://oj.leetcode.com/problems/length-of-last-word/) | Easy | | [Swift](./Sort/InsertInterval.swift) | 57 | [Insert Interval](https://oj.leetcode.com/problems/insert-interval/) | Hard | @@ -795,7 +873,7 @@ | [Swift](./Math/SudokuSolver.swift) | 37 | [Sudoku Solver](https://oj.leetcode.com/problems/sudoku-solver/) | Hard | | [Swift](./Array/ValidSudoku.swift) | 36 | [Valid Sudoku](https://oj.leetcode.com/problems/valid-sudoku/) | Easy | | [Swift](./Search/SearchInsertPosition.swift) | 35 | [Search Insert Position](https://oj.leetcode.com/problems/search-insert-position/) | Medium | -| [Swift](./Search/SearchForARange.swift) | 34 | [Search for a Range](https://oj.leetcode.com/problems/search-for-a-range/) | Medium | +| [Swift](./Search/SearchForARange.swift) | 34 | [Search for a Range](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/) | Medium | | [Swift](./Search/SearchInRotatedSortedArray.swift) | 33 | [Search in Rotated Sorted Array](https://oj.leetcode.com/problems/search-in-rotated-sorted-array/) | Hard | | [Swift](./Stack/LongestValidParentheses.swift) | 32 | [Longest Valid Parentheses](https://oj.leetcode.com/problems/longest-valid-parentheses/) | Hard | | [Swift](./Array/NextPermutation.swift) | 31 | [Next Permutation](https://oj.leetcode.com/problems/next-permutation/) | Medium | @@ -804,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 | diff --git a/Search/MedianOfTwoSortedArrays.swift b/Search/MedianOfTwoSortedArrays.swift index 458e8014..b1d17ced 100644 --- a/Search/MedianOfTwoSortedArrays.swift +++ b/Search/MedianOfTwoSortedArrays.swift @@ -15,7 +15,7 @@ */ class Solution { - func findMedianSortedArrays(nums1: [Int], _ nums2: [Int]) -> Double { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { let m = nums1.count let n = nums2.count @@ -62,4 +62,4 @@ } return Double(maxOfLeft + minOfRight) / 2.0 } -} \ No newline at end of file +} diff --git a/Search/RandomPickWeight.swift b/Search/RandomPickWeight.swift new file mode 100644 index 00000000..797bd85c --- /dev/null +++ b/Search/RandomPickWeight.swift @@ -0,0 +1,50 @@ +/** + * Question Link: https://leetcode.com/problems/random-pick-with-weight/ + * Primary idea: Random select a number from sum of the array, and search the first number + * greater than the number in sums array constructed from the original array. + * + * Time Complexity: O(logn), Space Complexity: O(n) + */ + + +class RandomPickWeight { + + var sums: [Int] + + init(_ w: [Int]) { + sums = w + + for i in 1.. Int { + guard let sum = sums.last else { + return -1 + } + + return findFirstGreaterThan(Int.random(in: 0.. Int { + var left = 0, right = sums.count - 1 + + while left < right { + let mid = (right - left) / 2 + left + if sums[mid] > num { + right = mid + } else { + left = mid + 1 + } + } + + return left + } +} + +/** + * Your Solution object will be instantiated and called as such: + * let obj = Solution(w) + * let ret_1: Int = obj.pickIndex() + */ \ No newline at end of file diff --git a/Search/SearchForARange.swift b/Search/SearchForARange.swift index 8ac36b0d..69530a54 100644 --- a/Search/SearchForARange.swift +++ b/Search/SearchForARange.swift @@ -1,64 +1,48 @@ /** - * Question Link: https://leetcode.com/problems/search-for-a-range/ + * Question Link: https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/ * Primary idea: Binary Search, check left or right separately * * Time Complexity: O(logn), Space Complexity: O(1) */ class SearchForARange { - func searchRange(nums: [Int], _ target: Int) -> [Int] { - var res = [-1, -1] - - guard nums.count > 0 else { - return res + func searchRange(_ nums: [Int], _ target: Int) -> [Int] { + guard !nums.isEmpty else { + return [-1, -1] } - res[0] = _search(nums, target, true) - res[1] = _search(nums, target, false) - - return res + return [searchStartIdx(nums, target), searchEndIdx(nums, target)] } - private func _search(nums: [Int], _ target: Int, _ isLeft: Bool) -> Int { - var left = 0 - var right = nums.count - 1 - var mid = 0 + private func searchStartIdx(_ nums: [Int], _ target: Int) -> Int { + var left = 0, right = nums.count - 1 while left + 1 < right { - mid = (right - left) / 2 + left - if nums[mid] == target { - if isLeft { - right = mid - } else { - left = mid - } - } else if nums[mid] > target { - right = mid - 1 - } else { + let mid = (right - left) / 2 + left + + if nums[mid] < target { left = mid + 1 + } else { + right = mid } } - if isLeft { - if _isIndexValid(left, nums, target) { - return left - } - if _isIndexValid(right, nums, target) { - return right - } - } else { - if _isIndexValid(right, nums, target) { - return right - } - if _isIndexValid(left, nums, target) { - return left + return nums[left] == target ? left : nums[right] == target ? right : -1 + } + + private func searchEndIdx(_ nums: [Int], _ target: Int) -> Int { + var left = 0, right = nums.count - 1 + + while left + 1 < right { + let mid = (right - left) / 2 + left + + if nums[mid] > target { + right = mid - 1 + } else { + left = mid } } - return -1 - } - - private func _isIndexValid(index: Int, _ nums: [Int], _ target: Int) -> Bool { - return index >= 0 && index < nums.count && nums[index] == target + return nums[right] == target ? right : nums[left] == target ? left : -1 } -} \ No newline at end of file +} diff --git a/Search/Sqrtx.swift b/Search/Sqrtx.swift index db1d4475..d2bfc336 100644 --- a/Search/Sqrtx.swift +++ b/Search/Sqrtx.swift @@ -9,17 +9,15 @@ */ class Sqrtx { - func mySqrt(x: Int) -> Int { - if x <= 0 { - return x + func mySqrt(_ x: Int) -> Int { + guard x >= 0 else { + return 0 } - var left = 0 - var right = x / 2 + 1 - var mid = 0 - + var left = 0, right = x / 2 + 1 + while left <= right { - mid = (right - left) / 2 + left + let mid = (right - left) / 2 + left if mid * mid == x { return mid @@ -32,4 +30,4 @@ class Sqrtx { return right } -} \ No newline at end of file +} 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 new file mode 100644 index 00000000..362c72fc --- /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/ + * 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, res = 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 + } + + res = max(res, i - left + 1) + } + + return res + } +} diff --git a/SlidingWindow/LongestSubstringMostKDistinctCharacters.swift b/SlidingWindow/LongestSubstringMostKDistinctCharacters.swift new file mode 100644 index 00000000..addab4a0 --- /dev/null +++ b/SlidingWindow/LongestSubstringMostKDistinctCharacters.swift @@ -0,0 +1,45 @@ +/** + * Question Link: https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/ + * Primary idea: Slding window, use dictionary to check substring is valid or not, and + note to handle the end of string edge case + * + * Note: k may be invalid, mention that with interviewer + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class LongestSubstringMostKDistinctCharacters { + func lengthOfLongestSubstringKDistinct(_ s: String, _ k: Int) -> Int { + guard k > 0 else { + return 0 + } + + 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 { + + // 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() + } + } + + // update window for current char + charFreqMap[char] = 1 + } + } + + return max(res, s.count - left) + } +} diff --git a/SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift b/SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift new file mode 100644 index 00000000..6bf3fe2c --- /dev/null +++ b/SlidingWindow/LongestSubstringMostTwoDistinctCharacters.swift @@ -0,0 +1,40 @@ +/** + * Question Link: https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/ + * 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) + * + */ + + class LongestSubstringMostTwoDistinctCharacters { + func lengthOfLongestSubstringTwoDistinct(_ s: String) -> Int { + 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 { + + // 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() + } + } + + // update window for current char + charFreqMap[char] = 1 + } + } + + return max(res, s.count - left) + } +} diff --git a/SlidingWindow/LongestSubstringWithoutRepeatingCharacters.swift b/SlidingWindow/LongestSubstringWithoutRepeatingCharacters.swift new file mode 100644 index 00000000..2a68b834 --- /dev/null +++ b/SlidingWindow/LongestSubstringWithoutRepeatingCharacters.swift @@ -0,0 +1,29 @@ +/** + * Question Link: https://leetcode.com/problems/longest-substring-without-repeating-characters/ + * Primary idea: Use a dictionary to hold the next possible valid position of characters of the non-repeating substring, + * and then iterate the string to update maxLen, dictionary, and startIdx encountering duplicates + * + * Note: Swift does not have a way to access a character in a string with O(1), + * thus we have to first transfer the string to a character array + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class LongestSubstringWithoutRepeatingCharacters { + func lengthOfLongestSubstring(_ s: String) -> Int { + var maxLen = 0, startIdx = 0, charToPos = [Character: Int]() + let sChars = Array(s) + + for (i, char) in sChars.enumerated() { + if let pos = charToPos[char] { + startIdx = max(startIdx, pos) + } + + // update to next valid position + charToPos[char] = i + 1 + maxLen = max(maxLen, i - startIdx + 1) + } + + return maxLen + } +} 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 75% rename from Array/SlidingWindowMaximum.swift rename to SlidingWindow/SlidingWindowMaximum.swift index 31f553aa..e1fc04f4 100644 --- a/Array/SlidingWindowMaximum.swift +++ b/SlidingWindow/SlidingWindowMaximum.swift @@ -8,7 +8,7 @@ */ class SlidingWindowMaximum { - func maxSlidingWindow(nums: [Int], _ k: Int) -> [Int] { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { var maxIdx = [Int]() var res = [Int]() @@ -20,14 +20,14 @@ class SlidingWindowMaximum { maxIdx.append(i) if i >= k - 1 { - if decreaseIndexQueue.first! + k == i { - decreaseIndexQueue.removeFirst() + if maxIdx.first! + k == i { + maxIdx.removeFirst() } - res.append(nums[decreaseIndexQueue.first!]) + res.append(nums[maxIdx.first!]) } } return res } -} \ No newline at end of file +} diff --git a/SlidingWindow/SubarraysKDifferentIntegers.swift b/SlidingWindow/SubarraysKDifferentIntegers.swift new file mode 100644 index 00000000..e003a600 --- /dev/null +++ b/SlidingWindow/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 + } + } + } + } +} + diff --git a/Sort/AlienDictionary.swift b/Sort/AlienDictionary.swift deleted file mode 100644 index 97600fde..00000000 --- a/Sort/AlienDictionary.swift +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Question Link: https://leetcode.com/problems/alien-dictionary/ - * Primary idea: Topological sort, keep each character for a inDegree number and - * to characters list, use a queue to decrease inDegree and form the result - * - * Time Complexity: O(nm), Space Complexity: O(m), - * n represents words number, m represents length of a word - */ - -class AlienDictionary { - var inDegrees = [Character: Int]() - var toWords = [Character: [Character]]() - var valid = true - - func alienOrder(_ words: [String]) -> String { - var res = "", qChars = [Character]() - - guard words.count > 0 else { - return res - } - - initGraph(words) - - for char in inDegrees.keys { - if inDegrees[char] == 0 { - qChars.append(char) - } - } - - while !qChars.isEmpty { - let char = qChars.removeFirst() - res += String(char) - - guard let toChars = toWords[char] else { - continue - } - - for c in toChars { - inDegrees[c]! -= 1 - if inDegrees[c] == 0 { - qChars.append(c) - } - } - } - - return res.characters.count == inDegrees.count && valid ? res : "" - } - - private func initGraph(_ words: [String]) { - for word in words { - for char in word.characters { - inDegrees[char] = 0 - } - } - - for i in 0.. [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 + } +} 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/Sort/KthLargestElementInArray.swift b/Sort/KthLargestElementInArray.swift new file mode 100644 index 00000000..5799ff47 --- /dev/null +++ b/Sort/KthLargestElementInArray.swift @@ -0,0 +1,29 @@ +/** + * Question Link: https://leetcode.com/problems/kth-largest-element-in-an-array/ + * Primary idea: Quick sort + * Time Complexity: O(nlogn), Space Complexity: O(n) + */ + +class KthLargestElementInArray { + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + guard let pivot = nums.first else { + fatalError("Invalid Input") + } + + let leftPart = nums.filter { $0 > pivot } + let middlePart = nums.filter { $0 == pivot} + let rightPart = nums.filter { $0 < pivot } + + if nums.count == middlePart.count { + return pivot + } + + if leftPart.count > k - 1 { + return findKthLargest(leftPart, k) + } else if k - leftPart.count <= middlePart.count { + return findKthLargest(middlePart, k - leftPart.count) + } else { + return findKthLargest(rightPart, k - leftPart.count - middlePart.count) + } + } +} \ No newline at end of file diff --git a/Sort/LargestNumber.swift b/Sort/LargestNumber.swift new file mode 100644 index 00000000..4b342dd3 --- /dev/null +++ b/Sort/LargestNumber.swift @@ -0,0 +1,14 @@ +/** + * Question Link: https://leetcode.com/problems/largest-number/ + * Primary idea: map and sort the array inplace to form the largest number. + * + * Time Complexity: O(nlogn), Space Complexity: O(1) + * + */ + +class Solution { + func largestNumber(_ A: [Int]) -> String { + let result = A.map { "\($0)" }.sorted(by: { $0 + $1 > $1 + $0 }).joined() + return result.first == "0" ? "0" : result + } +} diff --git a/Sort/MeetingRoomsII.swift b/Sort/MeetingRoomsII.swift index d098e4fe..5513c40e 100644 --- a/Sort/MeetingRoomsII.swift +++ b/Sort/MeetingRoomsII.swift @@ -15,21 +15,23 @@ */ class MeetingRoomsII { - func minMeetingRooms(_ intervals: [Interval]) -> Int { - let starts = intervals.map { interval in interval.start }.sorted() - let ends = intervals.map { interval in interval.end }.sorted() - var i = 0, j = 0, count = 0 + func minMeetingRooms(_ intervals: [[Int]]) -> Int { + let startingTimes = intervals.map { interval in interval[0] }.sorted() + let endingTimes = intervals.map { interval in interval[1] }.sorted() + let intervalsCount = intervals.count - while i < starts.count && j < ends.count { - if starts[i] < ends[j] { - count += 1 - i += 1 + var i = 0, j = 0, meetingRoomsNum = 0 + + while i < intervalsCount && j < intervalsCount { + if startingTimes[i] < endingTimes[j] { + meetingRoomsNum += 1 } else { - i += 1 j += 1 } - } + + i += 1 + } - return count + return meetingRoomsNum } -} \ No newline at end of file +} 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.. 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) + } +} 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 +} diff --git a/Sort/TopKFrequentElements.swift b/Sort/TopKFrequentElements.swift index 03d68bfd..99f7fb0e 100644 --- a/Sort/TopKFrequentElements.swift +++ b/Sort/TopKFrequentElements.swift @@ -7,24 +7,13 @@ */ class TopKFrequentElements { - func topKFrequent(nums: [Int], _ k: Int) -> [Int] { - var map = [Int: Int]() - - for num in nums { - guard let times = map[num] else { - map[num] = 1 - continue - } - map[num] = times + 1 + func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] { + let numFreq = Dictionary(nums.map { ($0, 1) }, uniquingKeysWith: +) + + let sortedNums = numFreq.keys.sorted { + return numFreq[$0]! > numFreq[$1]! } - - var keys = Array(map.keys) - keys.sortInPlace() { - let value1 = map[$0] - let value2 = map[$1] - return value1 > value2 - } - - return Array(keys[0.. Int { + var result = 0 + var num = 0 + var sign = 1 + var stack = [sign] + + for char in s { + switch char { + case "+", "-": + result += num * sign + sign = stack.last! * (char == "+" ? 1 : -1) + num = 0 + case "(": + stack.append(sign) + case ")": + stack.removeLast() + case " ": + break + default: + num = num * 10 + char.wholeNumberValue! + } + } + + return result + num * sign + } +} diff --git a/Stack/BinarySearchTreeIterator.swift b/Stack/BinarySearchTreeIterator.swift new file mode 100644 index 00000000..38b2f6ad --- /dev/null +++ b/Stack/BinarySearchTreeIterator.swift @@ -0,0 +1,58 @@ +/** + * Question Link: https://leetcode.com/problems/binary-search-tree-iterator/ + * Primary idea: In order iteration using a stack. + * 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 BSTIterator { + + var stack: [TreeNode] + + init(_ root: TreeNode?) { + stack = [TreeNode]() + + loadAllLeftToStack(from: root) + } + + /** @return the next smallest number */ + func next() -> Int { + let node = stack.removeLast() + + loadAllLeftToStack(from: node.right) + + return node.val + } + + /** @return whether we have a next smallest number */ + func hasNext() -> Bool { + return !stack.isEmpty + } + + private func loadAllLeftToStack(from root: TreeNode?) { + var node = root + + while let current = node { + stack.append(current) + node = current.left + } + } +} + +/** + * Your BSTIterator object will be instantiated and called as such: + * let obj = BSTIterator(root) + * let ret_1: Int = obj.next() + * let ret_2: Bool = obj.hasNext() + */ \ No newline at end of file diff --git a/Stack/DecodeString.swift b/Stack/DecodeString.swift new file mode 100644 index 00000000..7758bc69 --- /dev/null +++ b/Stack/DecodeString.swift @@ -0,0 +1,52 @@ +/** + * Question Link: https://leetcode.com/problems/decode-string/ + * Primary idea: Primary idea is to maintain two stacks[i.e. countStack and characterStack]. + * Traverse the given string and process the elements into the respective stacks, and accordingly update the result. + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + + +class Solution { + func decodeString(_ s: String) -> String { + var result = "" + var countStack = [Int]() + var characterStack = [String]() + let allowedDigits = Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]) + var arrayString = Array(s), i = 0 + + while i < arrayString.count{ + + if allowedDigits.contains(String(arrayString[i])){ + + var count = 0 + while allowedDigits.contains(String(arrayString[i])){ + count = 10 * count + Int(String(arrayString[i]))! + i += 1 + } + countStack.append(count) + }else if arrayString[i] == "["{ + + characterStack.append(result) + result = "" + i += 1 + }else if arrayString[i] == "]"{ + + if var temp = characterStack.popLast(), let repeatTime = countStack.popLast(){ + + for _ in 0.. Int{ + private func operate(_ token: String, _ prevNum: Int, _ postNum: Int) -> Int { switch token { case "+": - return prev + post + return prevNum + postNum case "-": - return prev - post + return prevNum - postNum case "*": - return prev * post + return prevNum * postNum + case "/": + return prevNum / postNum default: - return prev / post - } + fatalError("Invalid Input") + } } -} \ No newline at end of file +} diff --git a/Stack/ExclusiveTimeFunctions.swift b/Stack/ExclusiveTimeFunctions.swift new file mode 100644 index 00000000..b3222b13 --- /dev/null +++ b/Stack/ExclusiveTimeFunctions.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/exclusive-time-of-functions/ + * Primary idea: Use a stack to keep task start time, update it when a new task starts. + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class ExclusiveTimeFunctions { + func exclusiveTime(_ n: Int, _ logs: [String]) -> [Int] { + var stack = [(Int, Int)](), res = Array(repeating: 0, count: n) + + for log in logs { + // parse log + let logInfo = log.components(separatedBy: ":") + let id = Int(logInfo[0])!, isStart = logInfo[1] == "start", time = Int(logInfo[2])! + + if isStart { + if let last = stack.last { + res[last.0] += time - last.1 + } + + stack.append((id, time)) + } else { + let startTime = stack.removeLast().1 + + res[id] += time - startTime + 1 + + if var last = stack.last { + last.1 = time + 1 + stack[stack.count - 1] = last + } + } + } + + return res + } +} \ No newline at end of file diff --git a/Stack/InorderTraversal.swift b/Stack/InorderTraversal.swift index 0b028d14..751f9086 100644 --- a/Stack/InorderTraversal.swift +++ b/Stack/InorderTraversal.swift @@ -16,22 +16,20 @@ * } */ class InorderTraversal { - func inorderTraversal(root: TreeNode?) -> [Int] { - var stack = [TreeNode]() - var res = [Int]() - var node = root + func inorderTraversal(_ root: TreeNode?) -> [Int] { + var res = [Int](), stack = [TreeNode](), node = root - while !stack.isEmpty || node != nil { - if node != nil { - stack.append(node!) - node = node!.left + while node != nil || !stack.isEmpty { + if let currentNode = node { + stack.append(currentNode) + node = currentNode.left } else { - node = stack.removeLast() - res.append(node!.val) - node = node!.right + let prevNode = stack.removeLast() + res.append(prevNode.val) + node = prevNode.right } } return res } -} \ No newline at end of file +} diff --git a/Stack/LongestValidParentheses.swift b/Stack/LongestValidParentheses.swift index 50d1ced2..0c74b1f8 100644 --- a/Stack/LongestValidParentheses.swift +++ b/Stack/LongestValidParentheses.swift @@ -6,23 +6,26 @@ class LongestValidParentheses { func longestValidParentheses(_ s: String) -> Int { - let sChars = Array(s.characters) - var stack = [Int]() - var longest = 0 + var stack = [Int](), longest = 0, start = 0 - for (i, char) in sChars.enumerated() { - if char == "(" || stack.isEmpty || sChars[stack.last!] == ")" { + for (i, char) in s.enumerated() { + if char == "(" { stack.append(i) } else { - let _ = stack.removeLast() - if stack.isEmpty { - longest = max(longest, i + 1) + if !stack.isEmpty { + stack.removeLast() + + if let last = stack.last { + longest = max(longest, i - last) + } else { + longest = max(longest, i - start + 1) + } } else { - longest = max(longest, i - stack.last!) + start = i + 1 } } } return longest } -} \ No newline at end of file +} diff --git a/Stack/MaxStack.swift b/Stack/MaxStack.swift new file mode 100644 index 00000000..328949a3 --- /dev/null +++ b/Stack/MaxStack.swift @@ -0,0 +1,54 @@ +/** + * Question Link: https://leetcode.com/problems/max-stack/ + * Primary idea: Use a helper stack to keep track of the max value overall when a new + * element is pushed and also updates the helper stack when pop API is called + * + * Time Complexity: push - O(1), pop - O(1), top - O(1), peekMax - O(1), popMax - O(n) + * Space Complexity: O(n) + */ + + class MaxStack { + + var stack: [Int] + var maxStack: [Int] + + /** initialize your data structure here. */ + init() { + stack = [Int]() + maxStack = [Int]() + } + + func push(_ x: Int) { + stack.append(x) + maxStack.append(maxStack.isEmpty ? x : max(x, maxStack.last!)) + } + + func pop() -> Int { + maxStack.removeLast() + return stack.removeLast() + } + + func top() -> Int { + return stack.last! + } + + func peekMax() -> Int { + return maxStack.last! + } + + func popMax() -> Int { + let maxVal = peekMax() + + // remove max from stack + var buffer = [Int]() + while top() != maxVal { + buffer.append(pop()) + } + pop() + while !buffer.isEmpty { + push(buffer.removeLast()) + } + + return maxVal + } +} \ No newline at end of file diff --git a/Stack/MinStack.swift b/Stack/MinStack.swift new file mode 100644 index 00000000..dd293771 --- /dev/null +++ b/Stack/MinStack.swift @@ -0,0 +1,46 @@ +/** + * Question Link: https://leetcode.com/problems/min-stack/ + * Primary idea: Use a helper stack to help save the minimum values -- + * only when the new pushed value is less than the top value, + * and remove values correspond to pop operation + * Time Complexity: O(1), Space Complexity: O(n) + */ + +class MinStack { + var stack: [Int] + var minStack: [Int] + + /** initialize your data structure here. */ + init() { + stack = [Int]() + minStack = [Int]() + } + + func push(_ x: Int) { + stack.append(x) + + if minStack.isEmpty || x <= minStack.last! { + minStack.append(x) + } + } + + func pop() { + guard !stack.isEmpty else { + return + } + + let removedVal = stack.removeLast() + + if let last = minStack.last, last == removedVal { + minStack.removeLast() + } + } + + func top() -> Int { + return stack.isEmpty ? -1 : stack.last! + } + + func getMin() -> Int { + return minStack.isEmpty ? -1 : minStack.last! + } +} \ No newline at end of file diff --git a/Stack/SimplifyPath.swift b/Stack/SimplifyPath.swift index 84449340..2c51d4ec 100644 --- a/Stack/SimplifyPath.swift +++ b/Stack/SimplifyPath.swift @@ -6,25 +6,18 @@ class SimplifyPath { func simplifyPath(_ path: String) -> String { - let dirs = path.components(separatedBy: "/") - var stack = [String]() - - for dir in dirs { - if dir == "." { - continue - } else if dir == ".." { - if !stack.isEmpty { - stack.removeLast() - } - } else { - if dir != "" { - stack.append(dir) - } + var directories = [String]() + let components = path.split(separator: "/") + for component in components { + switch component { + case "": break // do nothing + case ".": break // do nothing, pointing to the current directory + case "..": + directories.popLast() // if empty, does nothing + default: + directories.append(String(component)) } } - - let res = stack.reduce("") { total, dir in "\(total)/\(dir)" } - - return res.isEmpty ? "/" : res + return "/" + String(directories.joined(separator: "/")) } -} \ No newline at end of file +} 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 diff --git a/String/CountAndSay.swift b/String/CountAndSay.swift index bdd2da98..eb43ddd3 100644 --- a/String/CountAndSay.swift +++ b/String/CountAndSay.swift @@ -1,48 +1,33 @@ /** * Question Link: https://leetcode.com/problems/count-and-say/ - * Primary idea: Recursive iteration, use outer iteration to count times, - * use inner iteration to get the right string for specific index + * Primary idea: Recursion to get previous string, then iterate and generate current one. * - * Note: Swift does not have a way to access a character in a string with O(1), - * thus we have to first transfer the string to a character array - * Time Complexity: O(n^2), Space Complexity: O(n) + * Time Complexity: O(n^2), Space Complexity: O(1) * */ class CountAndSay { - func countAndSay(n: Int) -> String { - guard n > 0 else { - return "" + func countAndSay(_ n: Int) -> String { + if n == 1 { + return "1" } - - var res = "1" - var temp: String - var count: Int - var chars: [Character] - var current: Character - - for _ in 1.. Bool { var capitalNum = 0, isFirstUpperCased = false - - for char in word.characters { + + for char in word { if char.isUpperCased() { capitalNum += 1 } } - - if let firstChar = word.characters.first { + + if let firstChar = word.first { isFirstUpperCased = firstChar.isUpperCased() } - - return capitalNum == 0 || (capitalNum == 1 && isFirstUpperCased) || capitalNum == word.characters.count + + return capitalNum == 0 || (capitalNum == 1 && isFirstUpperCased) || capitalNum == word.count } } fileprivate extension Character { - func isUpperCased() -> Bool { - return String(self).uppercased() == String(self) - } -} \ No newline at end of file + func isUpperCased() -> Bool { + return String(self).uppercased() == String(self) + } +} diff --git a/String/FindAllAnagramsInAString.swift b/String/FindAllAnagramsInAString.swift new file mode 100644 index 00000000..519df3e7 --- /dev/null +++ b/String/FindAllAnagramsInAString.swift @@ -0,0 +1,46 @@ +/** + * Question Link: https://leetcode.com/problems/find-all-anagrams-in-a-string/ + * Primary idea: Use a dictionary to store characters with frequencies, + * then compare p and them to determine if they are anagrams + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + + class FindAllAnagramsInAString { + func findAnagrams(_ s: String, _ p: String) -> [Int] { + var res = [Int]() + let s = Array(s) + + guard s.count >= p.count else { + return res + } + + let pCharsFreq = Dictionary(p.map { ($0, 1) }, uniquingKeysWith: +) + var sCharsFreq = Dictionary(s[0.. String { + let chars = Array(n) + var candidates = Set() + + candidates.insert(buildLeftNum(n)) + candidates.insert(buildRightNum(n)) + + for distance in -1...1 { + candidates.insert(buildPalindromeOnMiddle(chars, distance)) + } + + return findNearest(n, candidates) + } + + private func buildLeftNum(_ num: String) -> String { + return String(repeating: "9", count: num.count - 1) + } + + private func buildRightNum(_ num: String) -> String { + return "1" + String(repeating: "0", count: num.count - 1) + "1" + } + + private func buildPalindromeOnMiddle(_ n: [Character], _ distance: Int) -> String { + let middleIdx = n.count % 2 == 0 ? n.count / 2 - 1: n.count / 2 + let leftPart = String(Int(String(n[0...middleIdx]))! + distance) + + if n.count % 2 != 0 { + return String(leftPart.dropLast() + leftPart.reversed()) + } else { + return String(leftPart + leftPart.reversed()) + } + } + + private func findNearest(_ n: String, _ candidates: Set) -> String { + guard let num = Int(n) else { + fatalError("Invalid Input") + } + + var nearest = 0 + + for candidate in candidates { + guard let cNum = Int(candidate) else { + continue + } + if cNum == num { + continue + } + + if abs(cNum - num) < abs(nearest - num) { + nearest = cNum + } else if abs(cNum - num) == abs(nearest - num) { + nearest = min(cNum, nearest) + } + } + + return String(nearest) + } +} \ No newline at end of file diff --git a/String/GroupAnagrams.swift b/String/GroupAnagrams.swift index ffb476d2..3b8b6ba3 100644 --- a/String/GroupAnagrams.swift +++ b/String/GroupAnagrams.swift @@ -2,25 +2,12 @@ * Question Link: https://leetcode.com/problems/anagrams/ * Primary idea: Iterate the string array and categories strings with the same sorted one * - * Time Complexity: O(nmlogm + nlogn), n stands for number of words, m stands for the length of a word - * Space Complexity: O(n) + * Time Complexity: O(nmlogm), n stands for number of words, m stands for the length of a word + * Space Complexity: O(n) */ class GroupAnagrams { func groupAnagrams(_ strs: [String]) -> [[String]] { - var map = [String: [String]]() - - for str in strs { - let sortedStr = String(str.sorted()) - - var anagrams = [String]() - if let list = map[sortedStr] { - anagrams = list - } - anagrams.append(str) - map[sortedStr] = anagrams - } - - return map.values.map { value in value.sorted() } + return Array(Dictionary(strs.map { (String($0.sorted()), [$0]) }, uniquingKeysWith: +).values) } -} \ No newline at end of file +} diff --git a/String/IsomorphicStrings.swift b/String/IsomorphicStrings.swift index 42e429f3..49f73ef2 100644 --- a/String/IsomorphicStrings.swift +++ b/String/IsomorphicStrings.swift @@ -5,25 +5,21 @@ */ class IsomorphicStrings { - func isIsomorphic(s: String, _ t: String) -> Bool { - var stDict = [Character: Character]() - var tsDict = [Character: Character]() - - let sChars = [Character](s.characters) - let tChars = [Character](t.characters) - - guard sChars.count == tChars.count else { + func isIsomorphic(_ s: String, _ t: String) -> Bool { + guard s.count == t.count else { return false } - for i in 0.. [String] { - let rowOne = "qwertyuiop", rowTwo = "asdfghjkl", rowThree = "zxcvbnm" + func findWords(_ words: [String]) -> [String] { + let rowOne = "qwertyuiop", rowTwo = "asdfghjkl", rowThree = "zxcvbnm" - return words.filter { word in rowOne.contains(word) || rowTwo.contains(word) || rowThree.contains(word) } - } + return words.filter { word in rowOne.contains(word.lowercased()) || rowTwo.contains(word.lowercased()) || rowThree.contains(word.lowercased()) } + } +} - extension String { - func contains(_ word: String) -> Bool { - return word.filter { c in !self.contains(c) }.characters.count == 0 - } +extension String { + func contains(_ word: String) -> Bool { + return word.filter { c in !self.contains(c) }.count == 0 } -} \ No newline at end of file +} diff --git a/String/LicenseKeyFormatting.swift b/String/LicenseKeyFormatting.swift new file mode 100644 index 00000000..d7502269 --- /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 LicenseKeyFormatting { + 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) + } +} diff --git a/String/LongestCommonPrefix.swift b/String/LongestCommonPrefix.swift index e5f9cf2b..74c6b859 100644 --- a/String/LongestCommonPrefix.swift +++ b/String/LongestCommonPrefix.swift @@ -6,32 +6,29 @@ class LongestCommonPrefix { func longestCommonPrefix(_ strs: [String]) -> String { - var longestPrefix = [Character](), index = 0 - guard let firstStr = strs.first else { - return String(longestPrefix) + return "" } - let firstStrChars = Array(firstStr) - let strsChars = strs.map { Array($0) } + var res = "" - while index < firstStr.count { - - longestPrefix.append(firstStrChars[index]) - - for str in strsChars { - if index >= str.count { - return String(longestPrefix.dropLast()) + for (i, char) in firstStr.enumerated() { + // dropFirst(_ k: Int = 1) returns a Substring struct + for str in strs.dropFirst() { + if i == str.count { + return res } - if str[index] != longestPrefix[index] { - return String(longestPrefix.dropLast()) + // Another easy way: Array(str)[i], time complexity is linear though + let currentStrChar = str[str.index(str.startIndex, offsetBy: i)] + + if char != currentStrChar { + return res } } - - index += 1 + res.append(char) } - return String(longestPrefix) + return res } } \ No newline at end of file diff --git a/String/LongestSubstringMostKDistinctCharacters.swift b/String/LongestSubstringMostKDistinctCharacters.swift deleted file mode 100644 index 12f328af..00000000 --- a/String/LongestSubstringMostKDistinctCharacters.swift +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Question Link: https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/ - * Primary idea: Slding window, use dictionary to check substring is valid or not, and - note to handle the end of string edge case - * - * Note: k may be invalid, mention that with interviewer - * Time Complexity: O(n), Space Complexity: O(n) - * - */ - -class LongestSubstringMostKDistinctCharacters { - func lengthOfLongestSubstringKDistinct(_ s: String, _ k: Int) -> Int { - var start = 0, longest = 0, charsFreq = [Character: Int]() - let sChars = Array(s) - - guard k > 0 else { - return longest - } - - for (i, char) in sChars.enumerated() { - if let freq = charsFreq[char] { - charsFreq[char] = freq + 1 - } else { - if charsFreq.count == k { - longest = max(longest, i - start) - - while charsFreq.count == k { - let charStart = sChars[start] - charsFreq[charStart]! -= 1 - - if charsFreq[charStart] == 0 { - charsFreq[charStart] = nil - } - - start += 1 - } - } - - charsFreq[char] = 1 - } - } - - return max(longest, s.count - start) - } -} \ No newline at end of file diff --git a/String/LongestSubstringMostTwoDistinctCharacters.swift b/String/LongestSubstringMostTwoDistinctCharacters.swift deleted file mode 100644 index ec4537c0..00000000 --- a/String/LongestSubstringMostTwoDistinctCharacters.swift +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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 - note to handle the end of string edge case - * - * Time Complexity: O(n), Space Complexity: O(n) - * - */ - - 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 - } 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 - } - } - - charFreq[char] = 1 - } - } - - return max(longest, sChars.count - start) - } -} \ No newline at end of file diff --git a/String/LongestSubstringWithoutRepeatingCharacters.swift b/String/LongestSubstringWithoutRepeatingCharacters.swift deleted file mode 100644 index 390f5e3a..00000000 --- a/String/LongestSubstringWithoutRepeatingCharacters.swift +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Question Link: https://leetcode.com/problems/longest-substring-without-repeating-characters/ - * Primary idea: Use a set to hold characters and then iterate the string, - * update maxLen, set, startIdx encountering duplicates - * - * Note: Swift does not have a way to access a character in a string with O(1), - * thus we have to first transfer the string to a character array - * Time Complexity: O(n), Space Complexity: O(n) - * - */ - -class LongestSubstringWithoutRepeatingCharacters { - func lengthOfLongestSubstring(_ s: String) -> Int { - var longest = 0, left = 0, set = Set() - let sChars = Array(s) - - for (i, char) in sChars.enumerated() { - if set.contains(char) { - - longest = max(longest, i - left) - - while sChars[left] != char { - set.remove(sChars[left]) - left += 1 - } - left += 1 - - } else { - set.insert(char) - } - } - - return max(longest, sChars.count - left) - } -} \ No newline at end of file diff --git a/String/MinimumStepsMakeTwoStringsAnagram.swift b/String/MinimumStepsMakeTwoStringsAnagram.swift new file mode 100644 index 00000000..68ff5ede --- /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 } + } +} diff --git a/String/MultiplyStrings.swift b/String/MultiplyStrings.swift index 7f08be6c..c1c0302d 100644 --- a/String/MultiplyStrings.swift +++ b/String/MultiplyStrings.swift @@ -12,32 +12,42 @@ class MultiplyStrings { func multiply(_ num1: String, _ num2: String) -> String { - guard num1 != "0" && num2 != "0" else { - return "0" - } - - let num1Chars = Array(num1.characters.reversed()) - let num2Chars = Array(num2.characters.reversed()) - var res = Array(repeating: 0, count: num1Chars.count + num2Chars.count) - var finalChars = [String](), carry = 0, sum = 0 + let num1 = num1.reversed(), num2 = num2.reversed() + var res = Array(repeating: 0, count: num1.count + num2.count), resStr = "" - for (i, char1) in num1Chars.enumerated() { - let n1 = Int(String(char1)) - for (j, char2) in num2Chars.enumerated() { - res[i + j] += n1! * Int(String(char2))! + // calculate product for every digit + for (i, char1) in num1.enumerated() { + + guard let digit1 = char1.wholeNumberValue else { + fatalError("Invalid Input num1") + } + + for (j, char2) in num2.enumerated() { + guard let digit2 = char2.wholeNumberValue else { + fatalError("Invalid Input num2") + } + + res[i + j] += digit1 * digit2 } } - for num in res { - sum = (carry + num) % 10 - carry = (carry + num) / 10 - finalChars.insert(String(sum), at: 0) + // update digits + for i in 0.. Bool { - let sChars = Array(s.characters), tChars = Array(t.characters) - var foundDiff = false, i = 0, j = 0 - - let shorter = sChars.count < tChars.count ? sChars : tChars - let longer = sChars.count < tChars.count ? tChars : sChars + guard s != t else { + return false + } + var editType = Edit.insert - guard longer.count - shorter.count < 2 && s != t else { - return false + if s.count == t.count { + editType = .replace + } else if s.count - t.count == 1 { + editType = .delete + } else if t.count - s.count == 1 { + editType = .insert + } else { + return false } - while i < shorter.count && j < longer.count { - if shorter[i] != longer[j] { - if foundDiff { - return false + let arr = Array(s), brr = Array(t) + var flag = false, aIdx = 0, bIdx = 0 + + while aIdx < arr.count && bIdx < brr.count { + + if arr[aIdx] != brr[bIdx] { + + guard !flag else { + return false } - foundDiff = true - if shorter.count < longer.count { - j += 1 - } else { - i += 1 - j += 1 + flag = true + + switch editType { + case .insert: + bIdx += 1 + case .delete: + aIdx += 1 + case .replace: + aIdx += 1 + bIdx += 1 } } else { - i += 1 - j += 1 + aIdx += 1 + bIdx += 1 } + } - - return true + return true } -} \ No newline at end of file +} diff --git a/String/PermutationInString.swift b/String/PermutationInString.swift new file mode 100644 index 00000000..9fd1b648 --- /dev/null +++ b/String/PermutationInString.swift @@ -0,0 +1,44 @@ +/** + * Question Link: https://leetcode.com/problems/permutation-in-string/ + * Primary idea: Iterate through s2 and scan the length of s1 to check whether the + * substring of s2 and s1 are permutation by + * comparing the characters' number of two strings. + * + * Time Complexity: O(nm), Space Complexity: O(n) + * + */ + +class PermutationInString{ + func checkInclusion(_ s1: String, _ s2: String) -> Bool { + var s1charsFreq = Dictionary(s1.map { ($0, 1) }, uniquingKeysWith: +), mattersCount = 0 + let s2 = Array(s2) + + for i in 0..= s1.count { + let leftChar = s2[i - s1.count] + + if let freq = s1charsFreq[leftChar] { + s1charsFreq[leftChar] = freq + 1 + + if freq >= 0 { + mattersCount -= 1 + } + } + } + + if let freq = s1charsFreq[s2[i]] { + s1charsFreq[s2[i]] = freq - 1 + + if freq > 0 { + mattersCount += 1 + } + } + + if mattersCount == s1.count { + return true + } + } + + return false + } +} \ No newline at end of file diff --git a/String/ReverseVowelsOfAString.swift b/String/ReverseVowelsOfAString.swift index 38c4127d..03a6b1d7 100644 --- a/String/ReverseVowelsOfAString.swift +++ b/String/ReverseVowelsOfAString.swift @@ -8,41 +8,30 @@ */ class ReverseVowelsOfAString { - func reverseVowels(s: String) -> String { - var left = 0 - var right = s.characters.count - 1 - var chars = [Character](s.characters) + func reverseVowels(_ s: String) -> String { + var left = 0, right = s.count - 1 + var chars = Array(s) while left < right { - while left < right && !_isVowel(chars[left]) { + let isVowelLeft = isVowel(chars[left]), isVowelRight = isVowel(chars[right]) + + if isVowelLeft && isVowelRight { + chars.swapAt(left, right) left += 1 - } - while left < right && !_isVowel(chars[right]) { right -= 1 + } else { + if !isVowelLeft { + left += 1 + } else { + right -= 1 + } } - guard left < right else { - break - } - _swap(&chars, left, right) - left += 1 - right -= 1 } return String(chars) } - - private func _isVowel(char: Character) -> Bool { - var char = String(char).lowercaseString - if char == "a" || char == "e" || char == "i" || char == "o" || char == "u" { - return true - } - - return false - } - - private func _swap(inout chars: Array, _ p: Int, _ q: Int) { - var temp = chars[p] - chars[p] = chars[q] - chars[q] = temp + + private func isVowel(_ char: Character) -> Bool { + return Set("aeiouAEIOU").contains(char) } } \ No newline at end of file diff --git a/String/ReverseWordsString.swift b/String/ReverseWordsString.swift index 864427a2..2d7e9358 100644 --- a/String/ReverseWordsString.swift +++ b/String/ReverseWordsString.swift @@ -1,6 +1,7 @@ /** * Question Link: https://leetcode.com/problems/reverse-words-in-a-string/ - * Primary idea: Trim and split the original string, add word from end to start + * Primary idea: Trim and split the original string to a string array, + * reverse it and then join strings in the array to a single one. * * Time Complexity: O(n), Space Complexity: O(1) * @@ -8,6 +9,6 @@ class ReverseWordsString { func reverseWords(_ s: String) -> String { - return String(s.split(separator: " ").reversed().reduce("") { total, word in total + word + " "}.dropLast()) + return s.split(separator: " ").reversed().joined(separator: " ") } } 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.. 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 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.. Bool { - guard s.characters.count == t.characters.count else { - return false - } + let sCharsFreq = Dictionary(s.map { ($0, 1) }, uniquingKeysWith: +) + let tCharsFreq = Dictionary(t.map { ($0, 1) }, uniquingKeysWith: +) - return sortStr(s) == sortStr(t) - } - - private func sortStr(s: String) -> [Character] { - var sChars = [Character](s.characters) - sChars.sortInPlace({$0 < $1}) - return sChars - } + return sCharsFreq == tCharsFreq + } } \ No newline at end of file diff --git a/String/ValidPalindrome.swift b/String/ValidPalindrome.swift index 42613892..07c64474 100644 --- a/String/ValidPalindrome.swift +++ b/String/ValidPalindrome.swift @@ -8,21 +8,32 @@ class ValidPalindrome { func isPalindrome(_ s: String) -> Bool { - // Make String into an array of lowercase Characters - let characters = s.lowercased().characters + var i = 0, j = s.count - 1 + let sChars = Array(s.lowercased()) - // Only keep alphanumeric characters. - let cleaned = characters.filter { character in - return character.description.rangeOfCharacter(from: CharacterSet.alphanumerics) != nil - } - - // Compare values at mirroring indices. - let total = cleaned.count - for i in 0 ..< total/2 { - if cleaned[i] != cleaned[total - 1 - i] { + while i < j { + while !sChars[i].isAlphanumeric && i < j { + i += 1 + } + + while !sChars[j].isAlphanumeric && i < j { + j -= 1 + } + + if sChars[i] != sChars[j] { return false + } else { + i += 1 + j -= 1 } } + return true } } + +extension Character { + var isAlphanumeric: Bool { + return isLetter || isNumber + } +} \ No newline at end of file diff --git a/String/ValidPalindromeII.swift b/String/ValidPalindromeII.swift index 8b05988c..8a18495e 100644 --- a/String/ValidPalindromeII.swift +++ b/String/ValidPalindromeII.swift @@ -1,40 +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 - let sChars = Array(s) - - while i < j { - if sChars[i] != sChars[j] { - return isPalindrome(sChars, i + 1, j) || isPalindrome(sChars, i, j - 1) - } else { - i += 1 - j -= 1 - } - } - - return true - } - - fileprivate func isPalindrome(_ s: [Character], _ i: Int, _ j: Int) -> Bool { - var i = i, j = j - + var i = 0, j = s.count - 1, isDeleted = false + let s = Array(s) + while i < j { if s[i] != s[j] { - return false + if isDeleted { + return false + } 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 } -} \ No newline at end of file +} diff --git a/String/ValidWordAbbreviation.swift b/String/ValidWordAbbreviation.swift new file mode 100644 index 00000000..7b4c780c --- /dev/null +++ b/String/ValidWordAbbreviation.swift @@ -0,0 +1,43 @@ +/** + * Question Link: https://leetcode.com/problems/valid-word-abbreviation/ + * Primary idea: Go through both string and compare characters or skip by the number + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class ValidWordAbbreviation { + func validWordAbbreviation(_ word: String, _ abbr: String) -> Bool { + var i = 0, j = 0 + let word = Array(word), abbr = Array(abbr) + + while i < word.count && j < abbr.count { + if abbr[j].isNumber { + // edge case: "abbc" vs. "a02c" + if abbr[j] == "0" { + return false + } + + let start = j + + while j < abbr.count && abbr[j].isNumber { + j += 1 + } + + let end = j - 1 + + i += Int(String(abbr[start...end]))! + } else { + if abbr[j] != word[i] { + return false + } else { + i += 1 + j += 1 + } + } + } + + // edge case: "hi" vs. "hi1" + return i == word.count && j == abbr.count + } +} \ No newline at end of file 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 diff --git a/Tree/BinaryTreeLevelOrderTraversal.swift b/Tree/BinaryTreeLevelOrderTraversal.swift index f8bba8f0..23e2dad3 100644 --- a/Tree/BinaryTreeLevelOrderTraversal.swift +++ b/Tree/BinaryTreeLevelOrderTraversal.swift @@ -17,36 +17,25 @@ */ class BinaryTreeLevelOrderTraversal { - func levelOrder(root: TreeNode?) -> [[Int]] { + func levelOrder(_ root: TreeNode?) -> [[Int]] { var res = [[Int]]() - var queue = [TreeNode]() - if let root = root { - queue.append(root) + guard let root = root else { + return res } - while queue.count > 0 { - var size = queue.count - var level = [Int]() + var currentLevel = [root] + + while !currentLevel.isEmpty { + let currentLevelVals = currentLevel.map { $0.val } + + // add current level vals + res.append(currentLevelVals) - for _ in 1...size { - let node = queue[0] - queue.removeAtIndex(0) - - // add val - level.append(node.val) - - // add TreeNodes in next level - if let left = node.left { - queue.append(left) - } - if let right = node.right { - queue.append(right) - } - } - res.append(level) + // add next level nodes + currentLevel = currentLevel.flatMap { [$0.left, $0.right] }.compactMap { $0 } } return res } -} \ No newline at end of file +} diff --git a/Tree/BinaryTreeLevelOrderTraversalII.swift b/Tree/BinaryTreeLevelOrderTraversalII.swift index 8c98b8a4..d9eb9018 100644 --- a/Tree/BinaryTreeLevelOrderTraversalII.swift +++ b/Tree/BinaryTreeLevelOrderTraversalII.swift @@ -20,36 +20,25 @@ */ class BinaryTreeLevelOrderTraversalII { - func levelOrderBottom(root: TreeNode?) -> [[Int]] { + func levelOrderBottom(_ root: TreeNode?) -> [[Int]] { var res = [[Int]]() - var queue = [TreeNode]() - if let root = root { - queue.append(root) + guard let root = root else { + return res } - while queue.count > 0 { - var size = queue.count - var level = [Int]() + var currentLevel = [root] + + while !currentLevel.isEmpty { + let currentLevelVals = currentLevel.map { $0.val } + + // add current level vals + res.insert(currentLevelVals, at: 0) - for _ in 1...size { - let node = queue[0] - queue.removeAtIndex(0) - - // add val - level.append(node.val) - - // add TreeNodes in next level - if let left = node.left { - queue.append(left) - } - if let right = node.right { - queue.append(right) - } - } - res.insert(level, atIndex:0) + // add next level nodes + currentLevel = currentLevel.flatMap { [$0.left, $0.right] }.compactMap { $0 } } return res } -} \ No newline at end of file +} diff --git a/Tree/BinaryTreeMaximumPathSum.swift b/Tree/BinaryTreeMaximumPathSum.swift new file mode 100644 index 00000000..e6f21cb5 --- /dev/null +++ b/Tree/BinaryTreeMaximumPathSum.swift @@ -0,0 +1,41 @@ +/** + * Question Link: https://leetcode.com/problems/binary-tree-maximum-path-sum/ + * Primary idea: Iterate all tree nodes and calculate each node's maximum weight value + * and update maxmum path sum along the iteration + * Time Complexity: O(n), Space Complexity: O(1) + * + * 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 BinaryTreeMaximumPathSum { + + var maxValue = Int.min + + func maxPathSum(_ root: TreeNode?) -> Int { + maxWeight(root) + return maxValue + } + + private func maxWeight(_ node: TreeNode?) -> Int { + guard let node = node else { + return 0 + } + + let leftMaxWeight = max(maxWeight(node.left), 0) + let rightMaxWeight = max(maxWeight(node.right), 0) + + maxValue = max(maxValue, leftMaxWeight + node.val + rightMaxWeight) + + return node.val + max(leftMaxWeight, rightMaxWeight) + } +} \ No newline at end of file diff --git a/Tree/BinaryTreePaths.swift b/Tree/BinaryTreePaths.swift index 38b4ec8d..f5e9c223 100644 --- a/Tree/BinaryTreePaths.swift +++ b/Tree/BinaryTreePaths.swift @@ -18,37 +18,28 @@ class BinaryTreePaths { func binaryTreePaths(_ root: TreeNode?) -> [String] { - var path = [Character](), paths = [String]() + var paths = [String]() - dfs(root, &path, &paths) - - return paths - } - - fileprivate func dfs(_ root: TreeNode?, _ path: inout [Character], _ paths: inout [String]) { guard let root = root else { - return + return paths } - append(&path, root) + dfs(root, &paths, "\(root.val)") + return paths + } + + private func dfs(_ root: TreeNode, _ paths: inout [String], _ path: String) { if root.left == nil && root.right == nil { - paths.append(String(path)) + paths.append(path) return } - var pathLeft = path, pathRight = path - - dfs(root.left, &pathLeft, &paths) - dfs(root.right, &pathRight, &paths) - } - - fileprivate func append(_ path: inout [Character], _ node: TreeNode) { - if !path.isEmpty { - path.append(Character("-")) - path.append(Character(">")) + if let left = root.left { + dfs(left, &paths, path + "->\(left.val)") + } + if let right = root.right { + dfs(right, &paths, path + "->\(right.val)") } - - path += Array(String(node.val)) } -} \ No newline at end of file +} diff --git a/Tree/BinaryTreeVerticalOrderTraversal.swift b/Tree/BinaryTreeVerticalOrderTraversal.swift index 9ffd433a..aca714a3 100644 --- a/Tree/BinaryTreeVerticalOrderTraversal.swift +++ b/Tree/BinaryTreeVerticalOrderTraversal.swift @@ -20,42 +20,30 @@ class BinaryTreeVerticalOrderTraversal { func verticalOrder(_ root: TreeNode?) -> [[Int]] { - var res = [[Int]](), qNodes = [TreeNode](), qVals = [Int]() - var colVals = [Int: [Int]](), minCol = 0, maxCol = 0 - + func verticalOrder(_ root: TreeNode?) -> [[Int]] { guard let root = root else { - return res + return [[Int]]() } - qNodes.append(root) - qVals.append(0) + var orderToVals = [0: [root.val]], nodes = [(root, 0)] - while !qNodes.isEmpty { - let node = qNodes.removeFirst() - let col = qVals.removeFirst() + while !nodes.isEmpty { + let size = nodes.count - if colVals[col] != nil { - colVals[col]!.append(node.val) - } else { - colVals[col] = [node.val] + for _ in 0.. TreeNode? { - guard preorder.count > 0 && inorder.count > 0 && preorder.count == inorder.count else { + func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? { + guard preorder.count == inorder.count else { return nil } return _buildHelper(preorder, 0, preorder.count - 1, inorder, 0, inorder.count - 1) } - private func _buildHelper(preorder: [Int], _ preStart: Int, _ preEnd: Int, _ inorder: [Int], _ inStart: Int, _ inEnd: Int) -> TreeNode? { + private func _buildHelper(_ preorder: [Int], _ preStart: Int, _ preEnd: Int, _ inorder: [Int], _ inStart: Int, _ inEnd: Int) -> TreeNode? { guard preStart <= preEnd && inStart <= inEnd else { return nil } - let root = TreeNode(preorder[preStart]) - - var mid = 0 - for i in inStart...inEnd { - if inorder[i] == preorder[preStart] { - mid = i - break - } + guard let rootIndex = inorder.firstIndex(of: preorder[preStart]) else { + return nil } + let root = TreeNode(preorder[preStart]) - root.left = _buildHelper(preorder, preStart + 1, preStart + mid - inStart, inorder, inStart, mid - 1) - root.right = _buildHelper(preorder, preStart + mid - inStart + 1, preEnd, inorder, mid + 1, inEnd) + root.left = _buildHelper(preorder, preStart + 1, preStart + rootIndex - inStart, inorder, inStart, rootIndex - 1) + root.right = _buildHelper(preorder, preStart + rootIndex - inStart + 1, preEnd, inorder, rootIndex + 1, inEnd) return root } -} \ No newline at end of file +} diff --git a/Tree/ConvertSortedArrayBinarySearchTree.swift b/Tree/ConvertSortedArrayBinarySearchTree.swift new file mode 100644 index 00000000..9828050b --- /dev/null +++ b/Tree/ConvertSortedArrayBinarySearchTree.swift @@ -0,0 +1,37 @@ +/** + * Question Link: https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/ + * Primary idea: recursion, the root of subtree should always be mid point of the subarray + * Time Complexity: O(n), Space Complexity: O(1) + * + * 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 ConvertSortedArrayBinarySearchTree { + func sortedArrayToBST(_ nums: [Int]) -> TreeNode? { + return sortedArrayToBST(nums, 0, nums.count - 1) + } + + private func sortedArrayToBST(_ nums: [Int], _ leftIdx: Int, _ rightIdx: Int) -> TreeNode? { + guard leftIdx <= rightIdx else { + return nil + } + + let mid = (rightIdx - leftIdx) / 2 + leftIdx + let root = TreeNode(nums[mid]) + + root.left = sortedArrayToBST(nums, leftIdx, mid - 1) + root.right = sortedArrayToBST(nums, mid + 1, rightIdx) + + return root + } +} \ No newline at end of file diff --git a/Tree/PathSum.swift b/Tree/PathSum.swift index 125608f2..23fe7820 100644 --- a/Tree/PathSum.swift +++ b/Tree/PathSum.swift @@ -17,7 +17,7 @@ */ class PathSum { - func hasPathSum(root: TreeNode?, _ sum: Int) -> Bool { + func hasPathSum(_ root: TreeNode?, _ sum: Int) -> Bool { guard let root = root else { return false } @@ -27,4 +27,4 @@ class PathSum { return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val) } -} \ No newline at end of file +} diff --git a/Tree/SerializeDeserializeBinaryTree.swift b/Tree/SerializeDeserializeBinaryTree.swift new file mode 100644 index 00000000..49cf9f95 --- /dev/null +++ b/Tree/SerializeDeserializeBinaryTree.swift @@ -0,0 +1,103 @@ +/** + * Question Link: https://leetcode.com/problems/serialize-and-deserialize-binary-tree/ + * Primary idea: Preorder or level order + * 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 + * } + * } + */ + +# Solution 1 - Preorder +class SerializeDeserializeBinaryTree { + func serialize(_ root: Node?) -> String { + guard let root = root else { + return "#" + } + + return String(root.val) + serialize(root.left) + serialize(root.right) + } + + func deserialize(_ vals: inout String) -> Node? { + guard let rootVal = Int(String(vals.removeFirst())) else { + return nil + } + + let root = Node(rootVal) + root.left = deserialize(&vals) + root.right = deserialize(&vals) + + return root + } +} + +# Solution 2 - Level order, BFS +class SerializeDeserializeBinaryTree { + func serialize(_ root: Node?) -> String { + guard let root = root else { + return "" + } + + var res = "", nodes: [Node?] = [root] + + while !nodes.isEmpty { + let currentLevelSize = nodes.count + + for _ in 0.. Node? { + guard let firstVal = vals.first, let rootVal = Int(String(firstVal)) else { + return nil + } + + let root = Node(rootVal), vals = Array(vals) + var nodes: [Node?] = [root], i = 1 + + while !nodes.isEmpty { + guard let node = nodes.removeFirst() else { + continue + } + + var left: Node? + if let leftVal = Int(String(vals[i])) { + left = Node(leftVal) + } + node.left = left + nodes.append(left) + i += 1 + + var right: Node? + if let rightVal = Int(String(vals[i])) { + right = Node(rightVal) + } + node.right = right + nodes.append(right) + i += 1 + } + + return root + } +} diff --git a/Tree/SerializeDeserializeNAryTree.swift b/Tree/SerializeDeserializeNAryTree.swift new file mode 100644 index 00000000..d05118b6 --- /dev/null +++ b/Tree/SerializeDeserializeNAryTree.swift @@ -0,0 +1,42 @@ +/** + * Question Link: https://leetcode.com/problems/serialize-and-deserialize-n-ary-tree/ + * Primary idea: Preorder + * Time Complexity: O(n), Space Complexity: O(n) + * + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var children: [TreeNode] + + * public init(_ val: Int) { + * self.val = val + * self.children = [TreeNode]() + * } + * } + */ + +class SerializeDeserializeNAryTree { + func serialize(_ root: Node?) -> String { + guard let root = root else { + return "" + } + + return String(root.val) + String(root.children.count) + root.children.map { serialize($0) }.joined() + } + + func deserialize(_ vals: inout String) -> Node? { + guard let rootVal = Int(String(vals.removeFirst())) else { + return nil + } + + let root = Node(rootVal), size = String(vals.removeFirst()) + + for _ in 0.. 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 "" + } +} diff --git a/Tree/SymmetricTree.swift b/Tree/SymmetricTree.swift index d5919f88..a2f1037e 100644 --- a/Tree/SymmetricTree.swift +++ b/Tree/SymmetricTree.swift @@ -18,20 +18,23 @@ */ class SymmetricTree { - func isSymmetric(root: TreeNode?) -> Bool { + func isSymmetric(_ root: TreeNode?) -> Bool { guard let root = root else { return true } - return _helper(root.left, root.right) + + return isSymmetricHelper(root.left, root.right) } - func _helper(p: TreeNode?, _ q:TreeNode?) -> Bool { - if p == nil && q == nil { + private func isSymmetricHelper(_ left: TreeNode?, _ right: TreeNode?) -> Bool { + if left == nil && right == nil { return true } - if p == nil || q == nil || p!.val != q!.val { + + if let left = left, let right = right, left.val == right.val { + return isSymmetricHelper(left.left, right.right) && isSymmetricHelper(left.right, right.left) + } else { return false } - return _helper(p!.left, q!.right) && _helper(p!.right, q!.left) } } \ No newline at end of file diff --git a/Tree/ValidateBinarySearchTree.swift b/Tree/ValidateBinarySearchTree.swift index 7a89ae3a..dd9afe06 100644 --- a/Tree/ValidateBinarySearchTree.swift +++ b/Tree/ValidateBinarySearchTree.swift @@ -1,7 +1,7 @@ /** * Question Link: https://leetcode.com/problems/validate-binary-search-tree/ * Primary idea: Keep min to go right and keep max to go left - * Time Complexity: O(n), Space Complexity: O(n) + * Time Complexity: O(n), Space Complexity: O(log n) * * Definition for a binary tree node. * public class TreeNode { @@ -21,18 +21,18 @@ class ValidateBinarySearchTree { return _helper(root, nil, nil) } - private func _helper(node: TreeNode?, _ min: Int?, _ max: Int?) -> Bool { + private func _helper(_ node: TreeNode?, _ min: Int?, _ max: Int?) -> Bool { guard let node = node else { return true } - if let min = min, root.val <= min { + if let min = min, node.val <= min { return false } - if let max = max, root.val >= max { + if let max = max, node.val >= max { return false } return _helper(node.left, min, node.val) && _helper(node.right, node.val, max) } -} \ No newline at end of file +}