diff --git a/Array/AsteroidCollision.swift b/Array/AsteroidCollision.swift new file mode 100644 index 00000000..4cddd47b --- /dev/null +++ b/Array/AsteroidCollision.swift @@ -0,0 +1,42 @@ +/** + * Question Link: https://leetcode.com/problems/asteroid-collision/ + * Primary idea: traverse the array and handle positive and negative separately + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class AsteroidCollision { + func asteroidCollision(_ asteroids: [Int]) -> [Int] { + var positives = [Int]() + var negatives = [Int]() + + for asteroid in asteroids { + if asteroid > 0 { + positives.append(asteroid) + } else { + var shouldAppendToNegative = true + + while positives.count > 0 { + if positives.last! > -asteroid { + shouldAppendToNegative = false + break + } else { + let last = positives.removeLast() + + if -asteroid == last { + shouldAppendToNegative = false + break + } + } + } + + if shouldAppendToNegative { + negatives.append(asteroid) + } + } + } + + return negatives + positives + } +} \ No newline at end of file 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/ContainsDuplicate.swift b/Array/ContainsDuplicate.swift index 809b1cea..b22c82bf 100644 --- a/Array/ContainsDuplicate.swift +++ b/Array/ContainsDuplicate.swift @@ -8,20 +8,6 @@ class ContainsDuplicate { func containsDuplicate(nums: [Int]) -> Bool { - if nums.count <= 1 { - return false - } - - var set = Set() - - for i in 0 ..< nums.count { - if set.contains(nums[i]) { - return true - } else { - set.insert(nums[i]) - } - } - - return false + return nums.count > Set(nums).count } } \ No newline at end of file diff --git a/Array/ContainsDuplicateII.swift b/Array/ContainsDuplicateII.swift index 8a820f08..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 ..< nums.count { - guard let index = dict[nums[i]] where i - index <= k else { - dict[nums[i]] = i - continue - } - - return true + for (i, num) in nums.enumerated() { + if let lastIndex = numToLastIndex[num], i - lastIndex <= k { + return true + } else { + numToLastIndex[num] = i + } } return false } -} \ No newline at end of file +} diff --git a/Array/CreateMaximumNumber.swift b/Array/CreateMaximumNumber.swift new file mode 100644 index 00000000..c4580cad --- /dev/null +++ b/Array/CreateMaximumNumber.swift @@ -0,0 +1,104 @@ +/** + * Question Link: https://leetcode.com/problems/create-maximum-number/ + * Primary idea: Convert the problem to three sub problems: merge two arrays, + * get max number from a single array, and compare two arrays as a number + * + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class CreateMaximumNumber { + func maxNumber(_ nums1: [Int], _ nums2: [Int], _ k: Int) -> [Int] { + let len1 = nums1.count, len2 = nums2.count + + if len1 + len2 < k { + return Array(repeating: 0, count: k) + } else if len1 + len2 == k { + return mergeTwoArrays(nums1, nums2) + } else { + var res = [Int]() + + let nums1Start = k - len2 > 0 ? k - len2 : 0 + let nums1End = k - len1 > 0 ? len1 : k + + for i in nums1Start...nums1End { + let currentMax = maxNumber(nums1, i, nums2, k - i) + if compareArrays(currentMax, 0, res, 0) { + res = currentMax + } + } + + return res + } + } + + fileprivate func mergeTwoArrays(_ nums1: [Int], _ nums2: [Int]) -> [Int] { + var i = 0, j = 0, res = [Int]() + + while i < nums1.count || j < nums2.count { + if i == nums1.count { + res.append(nums2[j]) + j += 1 + } else if j == nums2.count { + res.append(nums1[i]) + i += 1 + } else { + + if compareArrays(nums1, i, nums2, j) { + res.append(nums1[i]) + i += 1 + } else { + res.append(nums2[j]) + j += 1 + } + } + + } + + return res + } + + fileprivate func maxNumber(_ nums1: [Int], _ len1: Int, _ nums2: [Int], _ len2: Int) -> [Int] { + return mergeTwoArrays(maxNumberOfArray(nums1, len1), maxNumberOfArray(nums2, len2)) + } + + fileprivate func maxNumberOfArray(_ nums: [Int], _ len: Int) -> [Int] { + var res = Array(repeating: 0, count: len) + if len == 0 { + return res + } + + var index = 0 + + for i in 0.. len && index > 0 && nums[i] > res[index - 1] { + index -= 1 + } + + if index < len { + res[index] = nums[i] + index += 1 + } + } + + return res + } + + fileprivate func compareArrays(_ nums1: [Int], _ idx1: Int, _ nums2: [Int], _ idx2: Int) -> Bool { + var i = idx1, j = idx2 + + while i < nums1.count && j < nums2.count { + if nums1[i] > nums2[j] { + return true + } + if nums1[i] < nums2[j] { + return false + } + i += 1 + j += 1 + } + + + return j >= nums2.count + } +} \ No newline at end of file diff --git a/Array/DiagonalTraverse.swift b/Array/DiagonalTraverse.swift new file mode 100644 index 00000000..efb0e072 --- /dev/null +++ b/Array/DiagonalTraverse.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/diagonal-traverse/ + * Primary idea: use sum to track matrix index, note to set lower and upper bound to avoid duplicates + * + * Time Complexity: O(mn), Space Complexity: O(1) + * + */ + +class DiagonalTraverse { + func findDiagonalOrder(_ matrix: [[Int]]) -> [Int] { + var res = [Int]() + + guard matrix.count > 0 && matrix[0].count > 0 else { + return res + } + + let m = matrix.count, n = matrix[0].count + var fromTop = false + + for sum in 0...m + n - 2 { + if fromTop { + for i in max(sum - n + 1, 0)...min(m - 1, sum) { + res.append(matrix[i][sum - i]) + } + } else { + for i in (max(sum - n + 1, 0)...min(m - 1, sum)).reversed() { + res.append(matrix[i][sum - i]) + } + } + + fromTop = !fromTop + } + + return res + } +} \ No newline at end of file diff --git a/Array/ExamRoom.swift b/Array/ExamRoom.swift new file mode 100644 index 00000000..c4f63dfc --- /dev/null +++ b/Array/ExamRoom.swift @@ -0,0 +1,51 @@ +/** + * Question Link: https://leetcode.com/problems/exam-room/ + * Primary idea: Calculate and compare middle point between two taken seats. + * + * Time Complexity: O(n) for seat(), O(1) for leave(at:), Space Complexity: O(n) + * + */ + + class ExamRoom { + var seats: [Int] + + init(_ n: Int) { + seats = Array(repeating: 0, count: n) + } + + func seat() -> Int { + var maxDistance = 0, maxIndex = 0, lastOne = -1 + + for (i, seat) in seats.enumerated() { + if seat == 1 { + if lastOne == -1 { + if maxDistance < i { + maxDistance = i + maxIndex = 0 + } + } else { + if maxDistance < (i - lastOne) / 2 { + maxDistance = (i - lastOne) / 2 + maxIndex = lastOne + (i - lastOne) / 2 + } + } + } + + lastOne = i + } + + if lastOne != -1 { + if maxDistance < (seats.count - 1 - lastOne) / 2 { + maxIndex = seats.count - 1 + } + } + + seats[maxIndex] = 1 + + return maxIndex + } + + func leave(_ seat: Int) { + seats[seat] = 0 + } +} \ No newline at end of file diff --git a/Array/FindDisappearedNumbers.swift b/Array/FindDisappearedNumbers.swift new file mode 100644 index 00000000..e9241a32 --- /dev/null +++ b/Array/FindDisappearedNumbers.swift @@ -0,0 +1,30 @@ +/** + * Question Link: https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/ + * primary idea: Traverse the array and get num really position in array, then set negative. + * In the final, filter greater than 0 num. + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class FindDisappearedNumbers { + func findDisappearedNumbers(_ nums: [Int]) -> [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 new file mode 100644 index 00000000..c5a04e35 --- /dev/null +++ b/Array/FirstMissingPositive.swift @@ -0,0 +1,20 @@ +/** + * Question Link: https://leetcode.com/problems/first-missing-positive/ + * Primary idea: Use a set to hold number in the array and iterate through 1...nums.count to find the missing one + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class FirstMissingPositive { + func firstMissingPositive(_ nums: [Int]) -> Int { + let set = Set(nums) + + for i in 0.. [[Int]] { - let nums = nums.sort({$0 < $1}) + func fourSum(_ nums: [Int], _ target: Int) -> [[Int]] { + let nums = nums.sorted(by: <) var threeSum = 0 var twoSum = 0 var left = 0 @@ -18,13 +18,13 @@ class FourSum { return res } - for i in 0 ..< nums.count - 3 { + for i in 0.. 0 else { + return + } + + let m = board.count, n = board[0].count + + for i in 0..= m || y < 0 || y >= n { + continue + } + if x == i && y == j { + continue + } + + liveNum = board[x][y] == 1 || board[x][y] == 2 ? liveNum + 1 : liveNum + } + } + + if board[i][j] == 1 { + board[i][j] = liveNum < 2 || liveNum > 3 ? 2 : 1 + } else { + board[i][j] = liveNum == 3 ? 3 : 0 + } + } +} \ No newline at end of file diff --git a/Array/GasStation.swift b/Array/GasStation.swift new file mode 100644 index 00000000..e9a1cdfb --- /dev/null +++ b/Array/GasStation.swift @@ -0,0 +1,34 @@ +/** + * Question Link: https://leetcode.com/problems/gas-station/ + * Primary idea: use currentSum and total to keep track of the gas and cost, + * change start index when currentSum is less than 0 + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class GasStation { + func canCompleteCircuit(_ gas: [Int], _ cost: [Int]) -> Int { + let totalGas = gas.reduce(0) { $0 + $1 }, totalCost = cost.reduce(0) { $0 + $1 } + guard totalGas >= totalCost else { + return -1 + } + + var start = 0, gasSum = 0, costSum = 0 + + for (i, currentGas) in gas.enumerated() { + let currentCost = cost[i] + + gasSum += currentGas + costSum += currentCost + + if gasSum < costSum { + start = i + 1 + gasSum = 0 + costSum = 0 + } + } + + return start + } +} \ No newline at end of file diff --git a/Array/Heaters.swift b/Array/Heaters.swift new file mode 100644 index 00000000..ad5f3700 --- /dev/null +++ b/Array/Heaters.swift @@ -0,0 +1,23 @@ +/** + * Question Link: https://leetcode.com/problems/heaters/ + * Primary idea: Two pointers, get the closest heater for the house, and update radius + * Time Complexity: O(nlogn), Space Complexity: O(1) + * + */ + +class Heaters { + func findRadius(_ houses: [Int], _ heaters: [Int]) -> Int { + var i = 0, radius = 0 + let houses = houses.sorted(), heaters = heaters.sorted() + + for house in houses { + while i < heaters.count - 1 && 2 * house >= heaters[i] + heaters[i + 1] { + i += 1 + } + + radius = max(radius, abs(house - heaters[i])) + } + + return radius + } +} \ No newline at end of file diff --git a/Array/IncreasingTripletSubsequence.swift b/Array/IncreasingTripletSubsequence.swift new file mode 100644 index 00000000..6e46e797 --- /dev/null +++ b/Array/IncreasingTripletSubsequence.swift @@ -0,0 +1,25 @@ +/** + * Question Link: https://leetcode.com/problems/increasing-triplet-subsequence/ + * Primary idea: Two pointers. One is to store the smallest value, + * the other is to store the second smallest value. + * Return true once find a value greater than both. + * Time Complexity: O(n), Space Complexity: O(1) + */ + +class IncreasingTripletSubsequence { + func increasingTriplet(_ nums: [Int]) -> 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 9aa109a1..a5ec9159 100644 --- a/Array/IntersectionTwoArraysII.swift +++ b/Array/IntersectionTwoArraysII.swift @@ -1,33 +1,25 @@ /** * Question Link: https://leetcode.com/problems/intersection-of-two-arrays-ii/ - * Primary idea: Sort and iterate to find all common elements + * Primary idea: Use dictionary to get frequencies of elements of one array, and + * compare with another array to filter the intersection * Note: Set cannot help you to find the number of common elements; thus it is not effective * - * Time Complexity: O(nlogn), Space Complexity: O(n) + * Time Complexity: O(n), Space Complexity: O(n) * */ class IntersectionTwoArraysII { - func intersect(nums1: [Int], _ nums2: [Int]) -> [Int] { - var nums1 = nums1.sort({$0 < $1}) - var nums2 = nums2.sort({$0 < $1}) - - var i = 0 - var j = 0 + func intersect(_ nums1: [Int], _ nums2: [Int]) -> [Int] { + var numsFreq = Dictionary(nums1.map { ($0, 1) }, uniquingKeysWith: +) var res = [Int]() - while i < nums1.count && j < nums2.count { - if nums1[i] < nums2[j] { - i += 1 - } else if nums1[i] > nums2[j] { - j += 1 - } else { - res.append(nums1[i]) - i += 1 - j += 1 + for num in nums2 { + 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/IslandPerimeter.swift b/Array/IslandPerimeter.swift new file mode 100644 index 00000000..792c2558 --- /dev/null +++ b/Array/IslandPerimeter.swift @@ -0,0 +1,28 @@ +/** + * Question Link: https://leetcode.com/problems/island-perimeter/ + * Primary idea: Go through the matrix and check right and down neighbors. + * Time Complexity: O(nm), Space Complexity: O(1) + * + */ + +class IslandPerimeter { + func islandPerimeter(_ grid: [[Int]]) -> Int { + var islands = 0, neighbors = 0 + + for i in 0 ..< grid.count { + for j in 0 ..< grid[0].count { + if grid[i][j] == 1 { + islands += 1 + if i < grid.count - 1 && grid[i + 1][j] == 1 { + neighbors += 1 + } + if (j < grid[0].count - 1 && grid[i][j + 1] == 1) { + neighbors += 1 + } + } + } + } + + return islands * 4 - neighbors * 2 + } +} \ No newline at end of file diff --git a/Array/LongestConsecutiveSequence.swift b/Array/LongestConsecutiveSequence.swift new file mode 100644 index 00000000..3c79709f --- /dev/null +++ b/Array/LongestConsecutiveSequence.swift @@ -0,0 +1,33 @@ +/** + * Question Link: https://leetcode.com/problems/longest-consecutive-sequence/ + * Primary idea: Iterate the array and check all neighbor numbers with the help of set + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class LongestConsecutiveSequence { + func longestConsecutive(_ nums: [Int]) -> Int { + var set = Set(nums), longest = 0 + + for num in nums { + var currentLength = 1 + dfs(num, &set, &longest, ¤tLength) + } + + return longest + } + + private func dfs(_ num: Int, _ set: inout Set, _ longest: inout Int, _ length: inout Int) { + if !set.contains(num) { + return + } + + 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 new file mode 100644 index 00000000..39b42e9d --- /dev/null +++ b/Array/MajorityElement.swift @@ -0,0 +1,23 @@ +/** + * Question Link: https://leetcode.com/problems/majority-element/ + * Primary idea: traverse the array and track the majority element accordingly + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class MajorityElement { + func majorityElement(_ nums: [Int]) -> Int { + var count = 0, candidate = 0 + + for num in nums { + if count == 0 { + candidate = num + } + + count += (candidate == num) ? 1 : -1 + } + + return candidate + } +} diff --git a/Array/MajorityElementII.swift b/Array/MajorityElementII.swift new file mode 100644 index 00000000..fb250614 --- /dev/null +++ b/Array/MajorityElementII.swift @@ -0,0 +1,56 @@ +/** + * Question Link: https://leetcode.com/problems/majority-element-ii/ + * Primary idea: traverse the array and track the majority element accordingly, do not + * forget to verify they are valid after first iteration + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class MajorityElementII { + func majorityElement(_ nums: [Int]) -> [Int] { + var num0: Int? + var num1: Int? + var count0 = 0 + var count1 = 0 + var res = [Int]() + + for num in nums { + if let num0 = num0, num0 == num { + count0 += 1 + } else if let num1 = num1, num1 == num { + count1 += 1 + } else if count0 == 0 { + num0 = num + count0 = 1 + } else if count1 == 0 { + num1 = num + count1 = 1 + } else { + count0 -= 1 + count1 -= 1 + } + } + + count0 = 0 + count1 = 0 + + for num in nums { + if num == num0 { + count0 += 1 + } + if num == num1 { + count1 += 1 + } + } + + if count0 > nums.count / 3 { + res.append(num0!) + } + if count1 > nums.count / 3 { + res.append(num1!) + } + + return res + } +} \ No newline at end of file diff --git a/Array/MaxConsecutiveOnes.swift b/Array/MaxConsecutiveOnes.swift new file mode 100644 index 00000000..6da8ee90 --- /dev/null +++ b/Array/MaxConsecutiveOnes.swift @@ -0,0 +1,23 @@ +/** + * Question Link: https://leetcode.com/problems/max-consecutive-ones/ + * Primary idea: Iterate the whole array and summarize consective ones locally and update globally encountering 0 + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class MaxConsecutiveOnes { + func findMaxConsecutiveOnes(_ nums: [Int]) -> Int { + var globalMax = 0, localMax = 0 + + for num in nums { + if num == 1 { + localMax += 1 + globalMax = max(globalMax, localMax) + } else { + localMax = 0 + } + } + + return globalMax + } +} \ No newline at end of file diff --git a/Array/MaximizeDistanceToClosestPerson.swift b/Array/MaximizeDistanceToClosestPerson.swift new file mode 100644 index 00000000..62cb45d9 --- /dev/null +++ b/Array/MaximizeDistanceToClosestPerson.swift @@ -0,0 +1,30 @@ +/** + * Question Link: https://leetcode.com/problems/maximize-distance-to-closest-person/ + * Primary idea: Calculate and compare middle point between two taken seats. + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class MaximizeDistanceToClosestPerson { + func maxDistToClosest(_ seats: [Int]) -> Int { + var lastOne = -1, maxDistance = 0 + + for (i, seat) in seats.enumerated() { + if seat == 1 { + if lastOne == -1 { + maxDistance = max(maxDistance, i) + } else { + maxDistance = max(maxDistance, (i - lastOne) / 2) + } + + lastOne = i + } + } + + // edge case: only one sitting person + maxDistance = max(maxDistance, seats.count - lastOne - 1) + + return maxDistance + } +} \ No newline at end of file diff --git a/Array/MaximumSizeSubarraySumEqualsK.swift b/Array/MaximumSizeSubarraySumEqualsK.swift index dc49ca7d..43883ef9 100644 --- a/Array/MaximumSizeSubarraySumEqualsK.swift +++ b/Array/MaximumSizeSubarraySumEqualsK.swift @@ -5,21 +5,19 @@ */ class MaximumSizeSubarraySumEqualsK { - func maxSubArrayLen(nums: [Int], _ k: Int) -> Int { - var longestLen = 0 - var dict = [Int: Int]() - dict[0] = -1 - var sum = 0 + func maxSubArrayLen(_ nums: [Int], _ k: Int) -> Int { + var longestLen = 0, sum = 0, sumToIdx = [Int: Int]() + sumToIdx[0] = -1 - for i in 0 ..< nums.count { - sum += nums[i] + for (i, num) in nums.enumerated() { + sum += num - if let lastIndex = dict[sum - k] { - longestLen = max(longestLen, i - lastIndex) + if let idx = sumToIdx[sum - k] { + longestLen = max(longestLen, i - idx) } - guard let index = dict[sum] else { - dict[sum] = i + guard let idx = sumToIdx[sum] else { + sumToIdx[sum] = i continue } } 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 new file mode 100644 index 00000000..538c3230 --- /dev/null +++ b/Array/MinimumSizeSubarraySum.swift @@ -0,0 +1,26 @@ +/** + * 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 valid subarray which sum >= target + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class MinimumSizeSubarraySum { + func minSubArrayLen(_ s: Int, _ nums: [Int]) -> Int { + var miniSize = Int.max, start = 0, currentSum = 0 + + for (i, num) in nums.enumerated() { + currentSum += num + + while currentSum >= s, start <= i { + miniSize = min(miniSize, i - start + 1) + + currentSum -= nums[start] + start += 1 + } + } + + return miniSize == Int.max ? 0 : miniSize + } +} 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 09bc3eff..2354e18d 100644 --- a/Array/MoveZeroes.swift +++ b/Array/MoveZeroes.swift @@ -1,26 +1,23 @@ /** * Question Link: https://leetcode.com/problems/move-zeroes/ - * Primary idea: keep index zeroIndex, traverse through the array and swap the value + * Primary idea: keep index for element not equal to 0, traverse and set up the index * * Time Complexity: O(n), Space Complexity: O(1) * */ class MoveZeroes { - func moveZeroes(inout nums: [Int]) { - var zeroIndex = 0 + func moveZeroes(_ nums: inout [Int]) { + var nonZeroIdx = 0 - for i in 0 ..< nums.count { - if nums[i] != 0 { - _swap(&nums, zeroIndex, i) - zeroIndex += 1 - } + for num in nums where num != 0 { + nums[nonZeroIdx] = num + nonZeroIdx += 1 + } + + while nonZeroIdx < nums.count { + nums[nonZeroIdx] = 0 + nonZeroIdx += 1 } - } - - private func _swap(inout nums: [Int], _ p: Int, _ q: Int) { - let temp = nums[p] - nums[p] = nums[q] - nums[q] = temp } } \ 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 new file mode 100644 index 00000000..4a76991e --- /dev/null +++ b/Array/NextPermutation.swift @@ -0,0 +1,44 @@ +/** + * Question Link: https://leetcode.com/problems/next-permutation/ + * Primary idea: Traverse the number from right to left, and replace the first smaller one + * with the least bigger one, then reverse all number afterwards + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class NextPermutation { + func nextPermutation(_ nums: inout [Int]) { + guard let violateIdx = findViolate(nums) else { + nums.reverse() + return + } + + 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 + } + + private func findViolate(_ nums: [Int]) -> Int? { + for i in (1.. nums[i - 1] { + return i - 1 + } + } + + return nil + } + + private func swap(_ nums: inout [Int], _ l: Int, _ r: Int) { + (nums[l], nums[r]) = (nums[r], nums[l]) + } +} diff --git a/Array/NumMatrix.swift b/Array/NumMatrix.swift new file mode 100644 index 00000000..c06f637f --- /dev/null +++ b/Array/NumMatrix.swift @@ -0,0 +1,49 @@ +/** + * Question Link: https://leetcode.com/problems/range-sum-query-2d-immutable/ + * Primary idea: Prebuild a sum matrix by original as top left point and current as bottom right point + * + * Time Complexity: O(mn) for init(), O(1) for sumRegion(), Space Complexity: O(mn) + */ + +class NumMatrix { + fileprivate var sum: [[Int]] + fileprivate var m: Int + fileprivate var n: Int + + init(_ matrix: [[Int]]) { + 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 { + if row1 == 0 && col1 == 0 { + return sum[row2][col2] + } else if row1 == 0 { + return sum[row2][col2] - sum[row2][col1 - 1] + } else if col1 == 0 { + return sum[row2][col2] - sum[row1 - 1][col2] + } else { + return sum[row2][col2] - sum[row2][col1 - 1] - sum[row1 - 1][col2] + sum[row1 - 1][col1 - 1] + } + } +} \ No newline at end of file diff --git a/Array/NumberBoomerangs.swift b/Array/NumberBoomerangs.swift new file mode 100644 index 00000000..4eaab040 --- /dev/null +++ b/Array/NumberBoomerangs.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/number-of-boomerangs/ + * Primary idea: traverse the array and compare distance one by one + * + * Time Complexity: O(n^2), Space Complexity: O(n) + * + */ + + class NumberBoomerangs { + func numberOfBoomerangs(_ points: [[Int]]) -> Int { + var num = 0 + + for (i, point) in points.enumerated() { + var dict = [Int: Int]() + for (j, anotherpoint) in points.enumerated() { + if i == j { + continue + } + + let distance = (anotherpoint[0] - point[0]) * (anotherpoint[0] - point[0]) + (anotherpoint[1] - point[1]) * (anotherpoint[1] - point[1]) + + if let sameDistancePoints = dict[distance] { + dict[distance] = sameDistancePoints + 1 + } else { + dict[distance] = 1 + } + } + + for key in dict.keys { + num += dict[key]! * (dict[key]! - 1) + } + } + return num + } +} \ No newline at end of file diff --git a/Array/ProductExceptSelf.swift b/Array/ProductExceptSelf.swift index 3cd12312..957ec476 100644 --- a/Array/ProductExceptSelf.swift +++ b/Array/ProductExceptSelf.swift @@ -1,46 +1,63 @@ /** - * Question Link: https://leetcode.com/problems/product-of-array-except-self/ - * Primary idea: Use two arrays to hold multiplication result from left and right sides - * while iterating the original array - * Time Complexity: O(n), Space Complexity: O(n) - */ +* Question Link: https://leetcode.com/problems/product-of-array-except-self/ +* Primary idea: Use two arrays to hold multiplication result from left and right sides +* while iterating the original array +* Time Complexity: O(n), Space Complexity: O(1) +*/ class ProductExceptSelf { - func productExceptSelf(nums: [Int]) -> [Int] { - var res = [Int]() - - guard nums.count > 0 else { - return res - } - - let left = _initLeft(nums) - let right = _initRight(nums) - - for i in 0 ..< nums.count { - res.append(left[i] * right[i]) - } - - return res - } - - private func _initLeft(nums: [Int]) -> [Int] { - var left = [Int]() - left.append(1) - - for i in 1 ..< nums.count { - left.append(left[i - 1] * nums[i - 1]) - } - - return left - } - - private func _initRight(nums: [Int]) -> [Int] { - var right = Array(count: nums.count, repeatedValue: 1) - - for i in (nums.count - 2).stride(through: 0, by: -1) { - right[i] = right[i + 1] * nums[i + 1] - } - - return right - } -} \ No newline at end of file + func productExceptSelf(_ nums: [Int]) -> [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.. Int { - if nums.count <= 1 { - return nums.count + guard nums.count > 0 else { + return 0 } - - var lastIndex = 0 - for num in nums { - if num != nums[lastIndex] { - lastIndex += 1 - nums[lastIndex] = num - } + var index = 0 + + for num in nums where num != nums[index] { + index += 1 + nums[index] = num } - return lastIndex + 1 + return index + 1 } } \ No newline at end of file diff --git a/Array/RemoveDuplicatesFromSortedArrayII.swift b/Array/RemoveDuplicatesFromSortedArrayII.swift index dbbe392b..3a52bbb5 100644 --- a/Array/RemoveDuplicatesFromSortedArrayII.swift +++ b/Array/RemoveDuplicatesFromSortedArrayII.swift @@ -8,19 +8,19 @@ class RemoveDuplicatesFromSortedArrayII { func removeDuplicates(inout nums: [Int]) -> Int { - // edge case - if nums.count <= 2 { + guard nums.count > 2 else { return nums.count } - var lastIndex = 1 - for i in 2 ..< nums.count { - if nums[lastIndex] != nums[i] || nums[lastIndex] != nums[lastIndex - 1] { - lastIndex += 1 - nums[lastIndex] = nums[i] - } + var index = 1 + + for i in 2.. Int { - var lastIndex = 0 - - for num in nums { - if num != val { - nums[lastIndex] = num - lastIndex += 1 - } - } - - return lastIndex + nums = nums.filter { (num) in num != val } + return nums.count } } \ No newline at end of file diff --git a/Array/RotateArray.swift b/Array/RotateArray.swift index da4b3718..a66774a1 100644 --- a/Array/RotateArray.swift +++ b/Array/RotateArray.swift @@ -9,15 +9,20 @@ */ class RotateArray { - func rotate(inout nums: [Int], _ k: Int) { - var k = k % nums.count - + func rotate(_ nums: inout [Int], _ k: Int) { + guard nums.count > 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) } - private func _reverse(inout nums: [Int], _ startIdx: Int, _ endIdx: Int) { + private func _reverse(_ nums: inout [Int], _ startIdx: Int, _ endIdx: Int) { // edge case if startIdx < 0 || endIdx > nums.count || startIdx >= endIdx { return @@ -33,9 +38,7 @@ class RotateArray { } } - private func _swap(inout nums: [Int], _ p: Int, _ q: Int) { - var temp = nums[p] - nums[p] = nums[q] - nums[q] = temp + 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/RotateImage.swift b/Array/RotateImage.swift index 4679dba4..fb2538d5 100644 --- a/Array/RotateImage.swift +++ b/Array/RotateImage.swift @@ -6,37 +6,15 @@ */ class RotateImage { - func rotate(inout matrix: [[Int]]) { + func rotate(_ matrix: inout [[Int]]) { let n = matrix.count - - guard n > 0 else { - return - } - - var start = 0 - var end = 0 - var offset = 0 - var temp = 0 - for layer in 0 ..< n / 2 { - start = layer - end = n - 1 - layer - - for i in start ..< end { - offset = i - start - temp = matrix[start][i] - - // left -> top - matrix[start][i] = matrix[end - offset][start] - - // bottom -> left - matrix[end - offset][start] = matrix[end][end - offset] - - // right -> bottom - matrix[end][end - offset] = matrix[i][end] + for layer in 0.. right - matrix[i][end] = temp + (matrix[start][i], matrix[i][end], matrix[end][end - offset], matrix[end - offset][start]) = (matrix[end - offset][start], matrix[start][i], matrix[i][end], matrix[end][end - offset]) } } } diff --git a/Array/SetMatrixZeroes.swift b/Array/SetMatrixZeroes.swift new file mode 100644 index 00000000..a8690139 --- /dev/null +++ b/Array/SetMatrixZeroes.swift @@ -0,0 +1,53 @@ +/** + * Question Link: https://leetcode.com/problems/set-matrix-zeroes/ + * Primary idea: Use first row and col to track if row and col should be set as 0, + * remember they should be separate from other parts of matrix + * + * Time Complexity: O(n^2), Space Complexity: O(1) + */ + +class SetMatrixZeroes { + func setZeroes(_ matrix: inout [[Int]]) { + var rowHasZero = false, colHasZero = false + let m = matrix.count, n = matrix[0].count + + for i in 0.. Int { + var minDistance = Int.max, word1Idx = -1, word2Idx = -1 + + for (i, word) in words.enumerated() { + if word == word1 { + 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 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/ShortestWordDistanceIII.swift b/Array/ShortestWordDistanceIII.swift new file mode 100644 index 00000000..a148e71e --- /dev/null +++ b/Array/ShortestWordDistanceIII.swift @@ -0,0 +1,34 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-word-distance-iii/ + * Primary idea: Iterate and update index and distance when encounter word1 or word2, use + * a temp variable to memorize the previous postion if word1 == word2 + * + * Time Complexity: O(n), Space Complexity: O(1) + */ + + class ShortestWordDistanceIII { + func shortestWordDistance(_ words: [String], _ word1: String, _ word2: String) -> Int { + var idx1 = -1, idx2 = -1, res = Int.max + + for (i, word) in words.enumerated() { + var prev = idx1 + + if word == word1 { + idx1 = i + } + if word == word2 { + idx2 = i + } + + if idx1 != -1 && idx2 != -1 { + if word1 == word2 && prev != -1 && prev != idx1 { + res = min(res, idx1 - prev) + } else if idx1 != idx2 { + res = min(res, abs(idx1 - idx2)) + } + } + } + + return res + } +} \ No newline at end of file diff --git a/Array/SmallestRange.swift b/Array/SmallestRange.swift new file mode 100644 index 00000000..e7415da9 --- /dev/null +++ b/Array/SmallestRange.swift @@ -0,0 +1,51 @@ +/** + * Question Link: https://leetcode.com/problems/smallest-range/ + * Primary idea: Merge K lists + Minimum Window Substring + * Time Complexity: O(nm), Space Complexity: O(nm) + * + */ + + class SmallestRange { + func smallestRange(_ nums: [[Int]]) -> [Int] { + let mergedNums = merge(nums) + + var left = 0, diff = Int.max, res = [Int]() + var numsIndexFreq = Dictionary((0.. mergedNums[right].0 - mergedNums[left].0 { + diff = mergedNums[right].0 - mergedNums[left].0 + res = [mergedNums[left], mergedNums[right]].map { $0.0 } + } + + if numsIndexFreq[mergedNums[left].1]! == 1 { + count -= 1 + } + numsIndexFreq[mergedNums[left].1]! -= 1 + + left += 1 + } + } + + return res + } + + fileprivate func merge(_ nums: [[Int]]) -> [(Int, Int)] { + var res = [(Int, Int)]() + + for (numsIndex, nums) in nums.enumerated() { + for num in nums { + res.append((num, numsIndex)) + } + } + + return res.sorted { return $0.0 < $1.0 } + } +} \ 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 18dda780..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 { @@ -20,7 +20,7 @@ class SpiralMatrix { while true { // top - for i in startY ... endY { + for i in startY...endY { res.append(matrix[startX][i]) } startX += 1 @@ -29,7 +29,7 @@ class SpiralMatrix { } // right - for i in startX ... endX { + for i in startX...endX { res.append(matrix[i][endY]) } endY -= 1 @@ -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/SpiralMatrixII.swift b/Array/SpiralMatrixII.swift index 202f805a..310aa900 100644 --- a/Array/SpiralMatrixII.swift +++ b/Array/SpiralMatrixII.swift @@ -6,42 +6,38 @@ */ class SpiralMatrixII { - func generateMatrix(n: Int) -> [[Int]] { + func generateMatrix(_ n: Int) -> [[Int]] { guard n > 0 else { return [[Int]]() } var num = 1 - var res = Array(count: n, repeatedValue: Array(count: n, repeatedValue: 0)) + var res = Array(repeating: Array(repeating: 0, count: n), count: n) - var start = 0 - var end = 0 - var offset = 0 - - for layer in 0 ..< n / 2 { - start = layer - end = n - layer - 1 + for layer in 0.. [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 new file mode 100644 index 00000000..c655a78c --- /dev/null +++ b/Array/StrobogrammaticNumber.swift @@ -0,0 +1,38 @@ +/** + * Question Link: https://leetcode.com/problems/strobogrammatic-number/ + * Primary idea: Two pointers, compare two characters until they are all valid + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class StrobogrammaticNumber { + func isStrobogrammatic(_ num: String) -> Bool { + let numChars = Array(num) + var i = 0, j = num.count - 1 + + while i <= j { + if isValid(numChars[i], numChars[j]) { + i += 1 + j -= 1 + } else { + return false + } + } + + return true + } + + fileprivate func isValid(_ charA: Character, _ charB: Character) -> Bool { + 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 { + return false + } + } +} + diff --git a/Array/SummaryRanges.swift b/Array/SummaryRanges.swift index 2a401d24..520b4c11 100644 --- a/Array/SummaryRanges.swift +++ b/Array/SummaryRanges.swift @@ -16,7 +16,7 @@ class SummaryRanges { return res } - for i in 0 ... nums.count { + for i in 0...nums.count { if i == nums.count || (i > 0 && nums[i] != nums[i - 1] + 1) { str = "\(nums[start])" if i - 1 != start { diff --git a/Array/TaskScheduler.swift b/Array/TaskScheduler.swift new file mode 100644 index 00000000..1ab3a7fd --- /dev/null +++ b/Array/TaskScheduler.swift @@ -0,0 +1,32 @@ +/** + * Question Link: https://leetcode.com/problems/task-scheduler/ + * 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) + * + */ + + class TaskScheduler { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + guard tasks.count > 0 else { + return 0 + } + + let taskFreqs = Dictionary(tasks.map { ($0, 1) }, uniquingKeysWith: +) + 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 + } + + 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) + } +} diff --git a/Array/ThreeSum.swift b/Array/ThreeSum.swift index 69f57b5e..d9ef51d0 100644 --- a/Array/ThreeSum.swift +++ b/Array/ThreeSum.swift @@ -6,42 +6,38 @@ */ class ThreeSum { - func threeSum(nums: [Int]) -> [[Int]] { - var nums = nums.sort({$0 < $1}) + 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/ThreeSumClosest.swift b/Array/ThreeSumClosest.swift new file mode 100644 index 00000000..78b6db49 --- /dev/null +++ b/Array/ThreeSumClosest.swift @@ -0,0 +1,45 @@ +/** + * Question Link: https://leetcode.com/problems/3sum-closest/ + * Primary idea: Sort the array, and iterate through it while updating the diff, + * increment left or decrease right predicated on + * their sum is greater or less than the target + * Time Complexity: O(n^2), Space Complexity: O(nC3) + */ + + class ThreeSumClosest { + func threeSumClosest(_ nums: [Int], _ target: Int) -> Int { + var minDiff = Int.max + + let nums = nums.sorted() + + for i in 0.. 0 { + repeat { + right -= 1 + } while left < right && nums[right] == nums[right + 1] + } else { + repeat { + left += 1 + } while left < right && nums[left] == nums[left - 1] + } + } + } + } + + return target + minDiff + } +} \ No newline at end of file diff --git a/Array/TwoSum.swift b/Array/TwoSum.swift index e41e625e..8e1741fc 100644 --- a/Array/TwoSum.swift +++ b/Array/TwoSum.swift @@ -6,20 +6,17 @@ */ class TwoSum { - func twoSum(nums: [Int], _ target: Int) -> [Int] { - var res = [Int]() + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { var dict = [Int: Int]() - - for i in 0 ..< nums.count { - guard let lastIndex = dict[target - nums[i]] else { - dict[nums[i]] = i - continue + + for (i, num) in nums.enumerated() { + if let lastIndex = dict[target - num] { + return [lastIndex, i] } - - res.append(lastIndex) - res.append(i) + + dict[num] = i } - - return res + + fatalError("No valid outputs") } } \ No newline at end of file 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 ce7d79f3..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 ..< size { - visited = Array(count: size, repeatedValue: false) - for j in 0 ..< size { - if !_isValidChar(board[i][j], &visited) { - return false - } - } - } + var rowSet = Array(repeating: Set(), 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 ..< size { - visited = Array(count: size, repeatedValue: false) - for j in 0 ..< size { - if !_isValidChar(board[j][i], &visited) { - return 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 ..< i + 3 { - for n in j ..< j + 3 { - if !_isValidChar(board[m][n], &visited) { - return false - } - } + + // check row + if !isValid(&rowSet[i], currentChar) { + return false + } + + // check column + if !isValid(&colSet[j], currentChar) { + return false + } + + // check sub-box + let idx = 3 * (i / 3) + j / 3 + if !isValid(&boxSet[idx], currentChar) { + return false } } } @@ -61,21 +42,12 @@ class ValidSudoku { return true } - private func _isValidChar(char: Character, inout _ visited: [Bool]) -> 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 new file mode 100644 index 00000000..d24d1c1a --- /dev/null +++ b/BFS/EvaluateDivision.swift @@ -0,0 +1,81 @@ +/** + * Question Link: https://leetcode.com/problems/evaluate-division/ + * Primary idea: Build dict to maintain divide result, and use BFS to get specific query value + * + * Time Complexity: O(n^2), Space Complexity: O(n) + * + */ + +class EvaluateDivision { + func calcEquation(_ equations: [[String]], _ values: [Double], _ queries: [[String]]) -> [Double] { + let dict = initDict(equations, values) + var rest = [Double]() + + for query in queries { + guard let first = query.first, let last = query.last else { + rest.append(-1.0) + continue + } + + guard let _ = dict[first], let _ = dict[last] else { + rest.append(-1.0) + continue + } + + bfs(query, dict, &rest) + } + + return rest + } + + fileprivate func initDict(_ equations: [[String]], _ values: [Double]) -> [String: [(String, Double)]] { + var dict = [String: [(String, Double)]]() + + for (i, equation) in equations.enumerated() { + guard let dividend = equation.first, let divisor = equation.last else { + continue + } + + dict[dividend] = dict[dividend, default: []] + [(divisor, values[i])] + dict[divisor] = dict[divisor, default: []] + [(dividend, 1.0 / values[i])] + } + + return dict + } + + fileprivate func bfs(_ query: [String], _ dict: [String: [(String, Double)]], _ rest: inout [Double]) { + guard let first = query.first, let last = query.last else { + rest.append(-1.0) + return + } + + var visited = Set([first]) + var qStrs = [first] + var qVals = [1.0] + + while !qStrs.isEmpty { + let currentStr = qStrs.removeFirst() + let currentVal = qVals.removeFirst() + + if currentStr == last { + rest.append(currentVal) + return + } + + guard let candidates = dict[currentStr] else { + continue + } + + for (str, val) in candidates { + guard !visited.contains(str) else { + continue + } + + visited.insert(str) + qStrs.append(str) + qVals.append(currentVal * val) + } + } + 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/ShortestDistanceAllBuildings.swift b/BFS/ShortestDistanceAllBuildings.swift new file mode 100644 index 00000000..11ad4f65 --- /dev/null +++ b/BFS/ShortestDistanceAllBuildings.swift @@ -0,0 +1,74 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-distance-from-all-buildings + * Primary idea: Use BFS to caculate distance from building to all valid points, and + * then figuare out the smallest one of all candidates + * + * Time Complexity: O((mn)^2), Space Complexity: O(mn) + * + */ + +class ShortestDistanceAllBuildings { + func shortestDistance(_ grid: [[Int]]) -> Int { + guard grid.count > 0 && grid[0].count > 0 else { + return -1 + } + + let m = grid.count, n = grid[0].count + var distances = Array(repeating: Array(repeating: 0, count: n), count: m), reachableNums = Array(repeating: Array(repeating: 0, count: n), count: m) + var shortestDistance = Int.max, buildingNums = 0 + + for i in 0..= 0 && xx < m && yy >= 0 && yy < n && !visited[xx][yy] else { + continue + } + + guard grid[xx][yy] == 0 else { + continue + } + + visited[xx][yy] = true + pointsQueue.append((xx, yy)) + distancesQueue.append(currentDistance + 1) + } + } + } +} \ No newline at end of file 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 69163dec..ac076d1b 100644 --- a/DFS/CombinationSum.swift +++ b/DFS/CombinationSum.swift @@ -2,34 +2,33 @@ * 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.sort({$0 < $1}), 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 } - for i in index ..< candidates.count { + for i in index.. [[Int]] { - var res = [[Int]]() - var path = [Int]() + var res = [[Int]](), path = [Int]() - _dfs(&res, &path, target, candidates.sort({$0 < $1}), 0) + dfs(&res, &path, target, candidates.sorted(), 0) return res } - private func _dfs(inout res: [[Int]], inout _ path: [Int], _ target: Int, _ candidates: [Int], _ index: Int) { + fileprivate func dfs(_ res: inout [[Int]], _ path: inout [Int], _ target: Int, _ candidates: [Int], _ index: Int) { if target == 0 { res.append(Array(path)) return } - for i in index ..< candidates.count { + for i in index.. [[Int]] { + let candidates = [Int](1...9) + var res = [[Int]](), path = [Int]() + + dfs(&res, &path, candidates, n, 0, k) + + return res + } + + fileprivate func dfs(_ res: inout [[Int]], _ path: inout [Int], _ candidates: [Int], _ target: Int, _ index: Int, _ size: Int) { + if target == 0 && path.count == size { + res.append(Array(path)) + return + } + + guard path.count < size else { + return + } + + for i in index.. [[Int]] { - var res = [[Int]]() - var path = [Int]() - let nums = _init(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 _init(n: Int) -> [Int] { - var res = [Int]() - - for i in 1 ... n { - res.append(i) - } - - 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 ..< nums.count { + for i in idx.. 5 + * 2. Multiplication's priority is higher than addiction + * + * Time Complexity: O(n^n), Space Complexity: O(n) + * + */ + +class ExpressionAddOperators { + func addOperators(_ num: String, _ target: Int) -> [String] { + var res = [String]() + + dfs(Array(num), 0, target, 0, 0, &res, "") + + return res + } + + 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(candidate) + } + + return + } + + for i in index.. [] + if i != index && nums[index] == "0" { + break + } + + let curStr = String(nums[index...i]) + + guard let cur = Int(curStr) else { + fatalError("Invalid input: num") + } + + if index == 0 { + dfs(nums, i + 1, target, cur, cur, &res, curStr) + } else { + dfs(nums, i + 1, target, eval + cur, cur, &res, candidate + "+" + curStr) + dfs(nums, i + 1, target, eval - cur, -cur, &res, candidate + "-" + curStr) + dfs(nums, i + 1, target, eval - mul + mul * cur, mul * cur, &res, candidate + "*" + curStr) + } + } + } +} diff --git a/DFS/FactorCombinations.swift b/DFS/FactorCombinations.swift new file mode 100644 index 00000000..e4941901 --- /dev/null +++ b/DFS/FactorCombinations.swift @@ -0,0 +1,34 @@ +/** + * Question Link: https://leetcode.com/problems/factor-combinations/ + * Primary idea: Classic Depth-first Search + * + * Time Complexity: O(n^n), Space Complexity: O(2^n - 1) + * + */ + +class FactorCombinations { + func getFactors(_ n: Int) -> [[Int]] { + var paths = [[Int]](), path = [Int]() + + dfs(&paths, path, 2, n) + + return paths + } + + private func dfs(_ paths: inout [[Int]], _ path: [Int], _ start: Int, _ target: Int) { + if target == 1 { + if path.count > 1 { + paths.append(path) + } + return + } + + guard start <= target else { + return + } + + 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/GeneralizedAbbreviation.swift b/DFS/GeneralizedAbbreviation.swift new file mode 100644 index 00000000..531c11ec --- /dev/null +++ b/DFS/GeneralizedAbbreviation.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/generalized-abbreviation/ + * Primary idea: Classic Depth-first Search + * + * Time Complexity: O(n^n), Space Complexity: O(2^n) + * + */ + +class GeneralizedAbbreviation { + func generateAbbreviations(_ word: String) -> [String] { + var res = [String]() + let chars = Array(word.characters) + + dfs(chars, &res, "", 0) + + return res + } + + private func dfs(_ word: [Character], _ res: inout [String], _ subset: String, _ index: Int) { + if word.count == index { + res.append(String(subset)) + return + } + + res.append(subset + String(word.count - index)) + + for i in index.. Bool { + var colors = Array(repeating: -1, count: graph.count) + + for i in 0.. Bool { + if colors[index] != -1 { + return colors[index] == color + } + + colors[index] = color + + for neighbor in graph[index] { + if !validColor(&colors, 1 - color, graph, neighbor) { + return false + } + } + + return true + } +} \ No newline at end of file diff --git a/DFS/LetterCombinationsPhoneNumber.swift b/DFS/LetterCombinationsPhoneNumber.swift index 4297f0a5..df88df14 100644 --- a/DFS/LetterCombinationsPhoneNumber.swift +++ b/DFS/LetterCombinationsPhoneNumber.swift @@ -2,58 +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] { - var res = [String]() - let chars = [Character](digits.characters) - - guard chars.count > 0 else { - return res + func letterCombinations(_ digits: String) -> [String] { + guard digits.count > 0 else { + return [String]() } - let board = _init() + var combinations = [String](), combination = "" + let numberToStr = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] - _dfs(&res, board, chars, "", 0) + dfs(&combinations, &combination, numberToStr, digits, 0) - return res - } - - private func _init() -> [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 + return combinations } - private func _dfs(inout res: [String], _ board: [String], _ chars: [Character], _ temp: String, _ index: Int) { - // termination case - if index == chars.count { - res.append(temp) + private func dfs(_ combinations: inout [String], _ combination: inout String, _ numberToStr: [String], _ digits: String, _ index: Int) { + if combination.count == digits.count { + combinations.append(combination) return } - var temp = temp - let current = [Character](board[Int(String(chars[index]))!].characters) + let currentStr = fetchCurrentStr(from: digits, at: index, numberToStr) + + for char in currentStr { + combination.append(char) + dfs(&combinations, &combination, numberToStr, digits, index + 1) + combination.removeLast() + } + } + + private func fetchCurrentStr(from digits: String, at index: Int, _ numberToStr: [String]) -> String { + guard index >= 0 && index < digits.count else { + fatalError("Invalid index") + } - for i in 0 ..< current.count { - temp += String(current[i]) - _dfs(&res, board, chars, temp, index + 1) - temp = String(temp.characters.dropLast()) + 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] } -} \ No newline at end of file +} 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/NQueens.swift b/DFS/NQueens.swift new file mode 100644 index 00000000..cec0e770 --- /dev/null +++ b/DFS/NQueens.swift @@ -0,0 +1,81 @@ +/** + * Question Link: https://leetcode.com/problems/n-queens/ + * Primary idea: Classic Depth-first Search, fill out row by row, and check column and + * diagnol for each time + * + * Time Complexity: O(n^n), Space Complexity: O(n^2) + * + */ + +class NQueens { + func solveNQueens(_ n: Int) -> [[String]] { + guard n > 0 else { + return [[String]]() + } + + var boards = [[String]]() + var board = Array(repeating: "", count: n) + + dfs(&boards, &board, n, 0) + + return boards + } + + private func dfs(_ boards: inout [[String]], _ board: inout [String], _ n: Int, _ row: Int) { + if row == n { + boards.append(Array(board)) + return + } + + + for col in 0.. Bool { + var c = -1 + + for i in 0.. Character { + return str[str.index(str.startIndex, offsetBy: index)] + } + + private func setRow(_ col: Int, _ n: Int) -> String { + var row = "" + + for i in 0.. Int { + guard n > 0 else { + return 0 + } + var count = 0 + var usedCols = Array(repeating: 0, count: n) + + dfs(&usedCols, &count, n, 0) + + return count + } + + private func dfs(_ usedCols: inout [Int], _ count: inout Int, _ n: Int, _ row: Int) { + if row == n { + count += 1 + return + } + + for col in 0.. Bool { + var c = -1 + + for i in 0.. 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/NumberofIslands.swift b/DFS/NumberofIslands.swift index ae380908..6b070fd0 100644 --- a/DFS/NumberofIslands.swift +++ b/DFS/NumberofIslands.swift @@ -2,7 +2,7 @@ * Question Link: https://leetcode.com/problems/number-of-islands/ * Primary idea: Classic Depth-first Search, go up, down, left, right four directions * - * Time Complexity: O((n^2)!), Space Complexity: O(1) + * Time Complexity: O(mn), Space Complexity: O(1) * */ @@ -17,8 +17,8 @@ class NumberofIslands { let n = grid[0].count var count = 0 - for i in 0 ..< m { - for j in 0 ..< n { + for i in 0.. [[String]] { + var paths = [[String]](), path = [String]() + + dfs(&paths, &path, Array(s), 0) + + return paths + } + + fileprivate func dfs(_ paths: inout [[String]], _ path: inout [String], _ s: [Character], _ index: Int) { + if index == s.count { + paths.append(Array(path)) + return + } + + for i in index.. 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]] { + func permute(_ nums: [Int]) -> [[Int]] { var res = [[Int]]() var path = [Int]() - var visited = [Bool](count: nums.count, repeatedValue: false) + var isVisited = [Bool](repeating: false, count: nums.count) - let nums = nums.sort({$0 < $1}) - - _dfs(&res, &path, nums, &visited) + dfs(&res, &path, &isVisited, nums) return res } - private func _dfs(inout res: [[Int]], inout _ path: [Int], _ nums: [Int], inout _ visited: [Bool]) { - // termination case - if path.count == nums.count { - res.append(Array(path)) + private func dfs(_ res: inout [[Int]], _ path: inout [Int], _ isVisited: inout [Bool], _ nums: [Int]) { + guard path.count != nums.count else { + res.append(path) return } - for i in 0 ..< nums.count { - guard !visited[i] else { - continue - } - - path.append(nums[i]) - visited[i] = true - _dfs(&res, &path, nums, &visited) - visited[i] = false + for (i, num) in nums.enumerated() where !isVisited[i] { + path.append(num) + isVisited[i] = true + dfs(&res, &path, &isVisited, nums) + isVisited[i] = false path.removeLast() } } diff --git a/DFS/PermutationsII.swift b/DFS/PermutationsII.swift index 49cb705b..f2747052 100644 --- a/DFS/PermutationsII.swift +++ b/DFS/PermutationsII.swift @@ -2,40 +2,35 @@ * Question Link: https://leetcode.com/problems/permutations-ii/ * Primary idea: Classic Depth-first Search, adopt last occurrence to avoid dupliates * - * Time Complexity: O(n!), Space Complexity: O(n) + * Time Complexity: O(n^n), Space Complexity: O(n) * */ class PermutationsII { - func permuteUnique(nums: [Int]) -> [[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.sort({$0 < $1}) - - _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 ..< nums.count { - if visited[i] || (i > 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/RemoveInvalidParentheses.swift b/DFS/RemoveInvalidParentheses.swift new file mode 100644 index 00000000..0e383359 --- /dev/null +++ b/DFS/RemoveInvalidParentheses.swift @@ -0,0 +1,52 @@ +/** + * Question Link: https://leetcode.com/problems/remove-invalid-parentheses/ + * Primary idea: Remove ) when the string is invalid, add to result when string is valid, + * and do the same thing for the reversed one + * + * Time Complexity: O(n^n), Space Complexity: O(n) + * + */ + +class RemoveInvalidParentheses { + func removeInvalidParentheses(_ s: String) -> [String] { + var paths = [String]() + + dfs(&paths, Array(s), 0, 0, ("(", ")")) + + return paths + } + + fileprivate func dfs(_ paths: inout [String], _ s: [Character], _ lastValid: Int, _ lastRight: Int, _ parens: (Character, Character)) { + var counter = 0, s = s + + for i in lastValid.. [String] { + var path = Array(repeating: Character(" "), count: n), paths = [String]() + + dfs(0, n - 1, &path, &paths) + + return paths + } + + private func dfs(_ left: Int, _ right: Int, _ path: inout [Character], _ paths: inout [String]) { + if left > right { + paths.append(String(path)) + return + } + + for (key, value) in mirrorDigits { + if left == right && (key == 6 || key == 9) { + continue + } + if left != right && left == 0 && key == 0 { + continue + } + + path[left] = Character(String(key)) + path[right] = Character(String(value)) + + dfs(left + 1, right - 1, &path, &paths) + } + } +} diff --git a/DFS/Subsets.swift b/DFS/Subsets.swift index 031485a3..0a0dbd13 100644 --- a/DFS/Subsets.swift +++ b/DFS/Subsets.swift @@ -2,31 +2,26 @@ * Question Link: https://leetcode.com/problems/subsets/ * Primary idea: Classic Depth-first Search * - * Time Complexity: O(n!), Space Complexity: O(n) + * Time Complexity: O(n * 2^n), Space Complexity: O(n * 2^n) * */ class Subsets { - func subsets(nums: [Int]) -> [[Int]] { - var res = [[Int]]() - var path = [Int]() + func subsets(_ nums: [Int]) -> [[Int]] { + var res = [[Int]](), path = [Int]() - let nums = nums.sort({$0 < $1}) - - _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 ..< nums.count { + for i in idx.. [[Int]] { - var res = [[Int]]() - var path = [Int]() + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + var res = [[Int]](), path = [Int]() - let nums = nums.sort({$0 < $1}) - - _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 ..< nums.count { - if i > 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 new file mode 100644 index 00000000..c7a8cd29 --- /dev/null +++ b/DFS/SudokuSolver.swift @@ -0,0 +1,64 @@ +/** + * Question Link: https://leetcode.com/problems/sudoku-solver/ + * Primary idea: Iterate through the whole matrix, try to fill out empty space with all + * possible cases and check the vaildity + * + * Time Complexity: O((9!) ^ 9), Space Complexity: O(1) + */ + + +class SudokuSolver { + private let length = 9 + + func solveSudoku(_ board: inout [[Character]]) { + dfs(&board) + } + + private func dfs(_ board: inout [[Character]]) -> Bool { + let candidates = "123456789" + + for i in 0.. Bool { + + for n in 0.. 0 && rooms[0].count > 0 else { + return + } + + let m = rooms.count + let n = rooms[0].count + + for i in 0..= 0 && i < m && j >= 0 && j < n else { + return + } + + if distance == 0 || distance < rooms[i][j] { + rooms[i][j] = distance + dfs(&rooms, i + 1, j, m, n, distance + 1) + dfs(&rooms, i - 1, j, m, n, distance + 1) + dfs(&rooms, i, j + 1, m, n, distance + 1) + dfs(&rooms, i, j - 1, m, n, distance + 1) + } + } +} \ No newline at end of file diff --git a/DFS/WordBreakII.swift b/DFS/WordBreakII.swift new file mode 100644 index 00000000..21231241 --- /dev/null +++ b/DFS/WordBreakII.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/word-break-ii/ + * Primary idea: DFS. Termination case is index hits the end of the string + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class WordBreakII { + func wordBreak(_ s: String, _ wordDict: [String]) -> [String] { + var res = [String](), path = [String]() + + dfs(&res, &path, Array(s), Set(wordDict), 0) + + return res + } + + private func dfs(_ res: inout [String], _ path: inout [String], _ s: [Character], _ dict: Set, _ idx: Int) { + if idx >= s.count { + res.append(path.joined(separator: " ")) + return + } + + for i in idx.. Bool { + return trie.search(word:word) + } +} + +class TrieNode { + var children: [Character: TrieNode] + var isEnd: Bool + + init() { + self.children = [Character: TrieNode]() + self.isEnd = false + } +} + +class Trie { + var root: TrieNode + + init() { + root = TrieNode() + } + + func add(word: String) { + var node = root + + for char in word { + if node.children[char] == nil { + node.children[char] = TrieNode() + } + + node = node.children[char]! + } + + node.isEnd = true + } + + func search(word:String) -> Bool { + return dfsSearch(word: word, index: 0, node: root) + } + + fileprivate func dfsSearch(word: String, index: Int, node: TrieNode) -> Bool { + if index == word.count { + return node.isEnd + } + + let char = Array(word)[index] + + if char != "." { + guard let nextNode = node.children[char] else { + return false + } + + return dfsSearch(word: word, index: index + 1, node: nextNode) + } else{ + for key in node.children.keys { + if dfsSearch(word: word, index: index + 1, node: node.children[key]!) { + return true + } + } + + return false + } + } +} \ No newline at end of file diff --git a/DFS/WordPatternII.swift b/DFS/WordPatternII.swift new file mode 100644 index 00000000..b9a18a31 --- /dev/null +++ b/DFS/WordPatternII.swift @@ -0,0 +1,47 @@ +/** + * Question Link: https://leetcode.com/problems/word-pattern-ii/ + * Primary idea: Depth first search, split the string to find possible word to pattern character until find the end + * + * Time Complexity: O(n^n), Space Complexity: O(n) + * + */ + +class WordPatternII { + func wordPatternMatch(_ pattern: String, _ str: String) -> Bool { + return helper(pattern, str, [Character: String]()) + } + + fileprivate func helper(_ pattern: String, _ str: String, _ patternToWord: [Character: String]) -> Bool { + guard let firstChar = pattern.first, str.count > 0 else { + return pattern.isEmpty && str.isEmpty + } + + let newPattern = String(pattern.suffix(pattern.count - 1)) + + if let existingWord = patternToWord[firstChar] { + if str.hasPrefix(existingWord) { + return helper(newPattern, String(str.suffix(str.count - existingWord.count)), patternToWord) + } else { + return false + } + } + + for i in 0.. [String] { + 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 + } + + guard !isVisited[i][j] else { + return + } + + guard let child = currentNode.children[board[i][j]] else { + return + } + + isVisited[i][j] = true + + let str = currentStr + "\(board[i][j])" + + if child.isEnd { + res.insert(str) + } + + 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 + } + + class Trie { + var root: TrieNode + + init(_ words: [String]) { + root = TrieNode() + + words.forEach { insert($0) } + } + + 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.isEnd = true + } + } + + class TrieNode { + var isEnd: Bool + var children: [Character: TrieNode] + + init() { + isEnd = false + children = [Character: TrieNode]() + } + } +} diff --git a/DFS/WordSquares.swift b/DFS/WordSquares.swift new file mode 100644 index 00000000..df5ff951 --- /dev/null +++ b/DFS/WordSquares.swift @@ -0,0 +1,65 @@ +/** + * 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^2), Space Complexity: O(n^2) + * + */ + + class WordSquares { + func wordSquares(_ words: [String]) -> [[String]] { + guard let first = words.first else { + return [[String]]() + } + + let prefixesToWords = buildMaps(words), rowNum = first.count + var paths = [[String]](), path = [String]() + + dfs(&paths, path, prefixesToWords, rowNum, 0) + + return paths + } + + private func dfs(_ paths: inout [[String]], _ path: [String], _ prefixesToWords: [String: [String]], _ rowNum: Int, _ currentRow: Int) { + if currentRow == rowNum { + paths.append(path) + return + } + + var prefix = "" + for i in 0.. [String: [String]] { + var res = [String: [String]]() + + for word in words { + for prefix in prefixes(word) { + res[prefix, default: []].append(word) + } + } + + 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/DFS/combinationSumIII.swift b/DFS/combinationSumIII.swift deleted file mode 100644 index 2be98ac2..00000000 --- a/DFS/combinationSumIII.swift +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Question Link: https://leetcode.com/problems/combination-sum-iii/ - * Primary idea: Classic Depth-first Search - * - * Time Complexity: O(n!), Space Complexity: O(nCk) - * - */ - -class combinationSumIII { - func combinationSum3(k: Int, _ n: Int) -> [[Int]] { - let candidates = _init() - var res = [[Int]]() - var path = [Int]() - - _dfs(&res, &path, candidates, n, 0, k) - - return res - } - - private func _init() -> [Int] { - var res = [Int]() - for num in 1 ... 9 { - res.append(num) - } - return res - } - - private func _dfs(inout res: [[Int]], inout _ path: [Int], _ candidates: [Int], _ target: Int, _ index: Int, _ size: Int) { - if target == 0 && path.count == size{ - res.append(Array(path)) - return - } - - for i in index ..< candidates.count { - guard candidates[i] <= target else { - break - } - - path.append(candidates[i]) - _dfs(&res, &path, candidates, target - candidates[i], i + 1, size) - path.removeLast() - } - } -} \ 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/BestTimeBuySellStockCooldown.swift b/DP/BestTimeBuySellStockCooldown.swift new file mode 100644 index 00000000..3c73e229 --- /dev/null +++ b/DP/BestTimeBuySellStockCooldown.swift @@ -0,0 +1,32 @@ +/** + * Question Link: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ + * Primary idea: Dynamic programming, dp array means the max value sold at today + * Time Complexity: O(n^2), Space Complexity: O(n) + * + */ + + class BestTimeBuySellStockCooldown { + func maxProfit(_ prices: [Int]) -> Int { + guard prices.count > 1 else { + return 0 + } + + var res = 0 + var dp = Array(repeating: 0, count: prices.count) + + for i in 1..= 2 { + dp[i] = max(dp[i], prices[i] - prices[j] + dp[j - 2]) + } else { + dp[i] = max(dp[i], prices[i] - prices[j]) + } + } + + dp[i] = max(dp[i], dp[i - 1]) + res = max(res, dp[i]) + } + + return res + } +} \ No newline at end of file diff --git a/DP/BestTimeBuySellStockII.swift b/DP/BestTimeBuySellStockII.swift new file mode 100644 index 00000000..afc0f68e --- /dev/null +++ b/DP/BestTimeBuySellStockII.swift @@ -0,0 +1,22 @@ +/** + * Question Link: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ + * Primary idea: Add all substractions if sell stock could earn money + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class BestTimeBuySellStockII { + func maxProfit(_ prices: [Int]) -> Int { + var max = 0 + + guard prices.count > 1 else { + return max + } + + for i in 1.. prices[i - 1] { + max += prices[i] - prices[i - 1] + } + + return max + } +} \ No newline at end of file diff --git a/DP/BestTimeBuySellStockIII.swift b/DP/BestTimeBuySellStockIII.swift new file mode 100644 index 00000000..3a2b32e6 --- /dev/null +++ b/DP/BestTimeBuySellStockIII.swift @@ -0,0 +1,38 @@ +/** + * Question Link: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ + * Primary idea: Dynamic Programming, find point where sum of [0, i] and [i, count - 1] + * is largest + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class BestTimeBuySellStockIII { + func maxProfit(_ prices: [Int]) -> Int { + guard prices.count > 0 else { + return 0 + } + + var maxProfit = 0 + var finalMaxProfit = 0 + var maxProfitLeft = [Int]() + var low = prices.first! + var high = prices.last! + + for price in prices { + maxProfit = max(price - low, maxProfit) + low = min(price, low) + maxProfitLeft.append(maxProfit) + } + + maxProfit = 0 + + for i in (0..= n, dynamic programming is not efficient, + * we can take advantage of the method in Best Time Buy Sell Stock II + * + * Time Complexity: O(n^2), Space Complexity: O(n) + * + */ + + class BestTimeBuySellStockIV { + func maxProfit(_ k: Int, _ prices: [Int]) -> Int { + let n = prices.count + + guard k > 0 && n > 1 else { + return 0 + } + guard k < n else { + return makeMaxProfit(prices) + } + + // 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 sum = 0 + + for i in 1.. 0 ? diff : 0 + } + + return sum + } +} diff --git a/DP/BurstBalloons.swift b/DP/BurstBalloons.swift new file mode 100644 index 00000000..6e304fe3 --- /dev/null +++ b/DP/BurstBalloons.swift @@ -0,0 +1,41 @@ +/** + * Question Link: https://leetcode.com/problems/burst-balloons/ + * Primary idea: Dynamic Programming, dp[i][j] represents the max coins from ballon i to j, + * transition function: dp[i][j] = max(dp[i][j], nums[i - 1]*nums[k]*nums[j + 1] + dp[i][k - 1] + dp[k + 1][j]) + * + * Note: for loops should start by length, not by start + * + * Time Complexity: O(n^3), Space Complexity: O(n) + * + */ + +class BurstBalloons { + func maxCoins(_ nums: [Int]) -> Int { + guard nums.count > 0 else { + return 0 + } + + let n = nums.count, nums = renderNums(nums) + var dp = Array(repeating: Array(repeating: 0, count: nums.count), count: nums.count) + + for len in 1...n { + for left in 1...n - len + 1 { + let right = left + len - 1 + for k in left...right { + dp[left][right] = max(dp[left][right], nums[left - 1] * nums[k] * nums[right + 1] + dp[left][k - 1] + dp[k + 1][right]) + } + } + } + + return dp[1][n] + } + + private func renderNums(_ nums: [Int]) -> [Int] { + var nums = nums + + nums.append(1) + nums.insert(1, at: 0) + + return nums + } +} \ No newline at end of file diff --git a/DP/CanIWin.swift b/DP/CanIWin.swift new file mode 100644 index 00000000..97dc7783 --- /dev/null +++ b/DP/CanIWin.swift @@ -0,0 +1,41 @@ +/** + * Question Link: https://leetcode.com/problems/can-i-win/ + * Primary idea: Dynamic Programming, use current and bit manipulation to track cases + * Time Complexity: O(2^n), Space Complexity: O(n) + * + */ + + class CanIWin { + func canIWin(_ maxChoosableInteger: Int, _ desiredTotal: Int) -> Bool { + if maxChoosableInteger >= desiredTotal { + return true + } + if (maxChoosableInteger + 1) * maxChoosableInteger / 2 < desiredTotal { + return false + } + + var memo = [Int: Bool]() + return helper(maxChoosableInteger, desiredTotal, 0, &memo) + } + + fileprivate func helper(_ maxChoosableInteger: Int, _ desiredTotal: Int, _ used: Int, _ memo: inout [Int: Bool]) -> Bool { + if let canWin = memo[used] { + return canWin + } + + 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 7c7bfdbf..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/CoinChangeII.swift b/DP/CoinChangeII.swift new file mode 100644 index 00000000..877c07bc --- /dev/null +++ b/DP/CoinChangeII.swift @@ -0,0 +1,27 @@ +/** + * Question Link: https://leetcode.com/problems/coin-change-ii/ + * Primary idea: Dynamic Programming, dp[i] += dp[i - coin] + * Note: Loopo of coins should be outside to avoid duplicates + * Time Complexity: O(n^2), Space Complexity: O(n) + */ + + class CoinChangeII { + func change(_ amount: Int, _ coins: [Int]) -> Int { + guard amount > 0 else { + return 1 + } + + var dp = [Int](repeating: 0, count: amount + 1) + dp[0] = 1 + + for coin in coins { + for i in 1...amount { + if i >= coin { + dp[i] += dp[i - coin] + } + } + } + + return dp[amount] + } +} \ No newline at end of file diff --git a/DP/CombinationSumIV.swift b/DP/CombinationSumIV.swift new file mode 100644 index 00000000..1c83e8ba --- /dev/null +++ b/DP/CombinationSumIV.swift @@ -0,0 +1,31 @@ +/** + * Question Link: https://leetcode.com/problems/combination-sum-iv/ + * Primary idea: DFS cannot solve without time limited error, so use DP + * + * Note: Integer overflow + * Space Complexity: O(n) + * + */ + +class CombinationSumIV { + func combinationSum4(_ nums: [Int], _ target: Int) -> Int { + var dp = [Int](repeating: 0, count: target + 1) + dp[0] = 1 + let nums = nums.sorted() + + for i in 1...target { + for num in nums { + if i < num { + break + } + + if dp[i - num] > Int.max - dp[i] { + continue + } + dp[i] += dp[i - num] + } + } + + return dp.last! + } +} \ No newline at end of file diff --git a/DP/DecodeWays.swift b/DP/DecodeWays.swift new file mode 100644 index 00000000..343b187d --- /dev/null +++ b/DP/DecodeWays.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/decode-ways/ + * Primary idea: Dynamic Programming, dp[i] = dp[i - 1] + dp[i - 2], + * determine if current one or two characters are number at first + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class DecodeWays { + func numDecodings(_ s: String) -> Int { + let s = Array(s) + var dp = Array(repeating: 0, count: s.count + 1) + dp[0] = 1 + + for i in 1...s.count { + if s[i - 1] != "0" { + dp[i] += dp[i - 1] + } + + if i > 1 && isValid(s, i - 2, i - 1) { + dp[i] += dp[i - 2] + } + } + + return dp[s.count] + } + + private func isValid(_ s: [Character], _ start: Int, _ end: Int) -> Bool { + guard let num = Int(String(s[start...end])) else { + fatalError() + } + + return num >= 10 && num <= 26 + } +} \ No newline at end of file diff --git a/DP/DifferentWaysAddParentheses.swift b/DP/DifferentWaysAddParentheses.swift new file mode 100644 index 00000000..b196e27c --- /dev/null +++ b/DP/DifferentWaysAddParentheses.swift @@ -0,0 +1,50 @@ +/** + * Question Link: https://leetcode.com/problems/different-ways-to-add-parentheses/ + * Primary idea: Divide and Conquer, calculate left and right separately and union results + * + * Note: Keep a memo dictionary to avoid duplicate calculations + * Time Complexity: O(n^n), Space Complexity: O(n) + * + */ + +class DifferentWaysAddParentheses { + var memo = [String: [Int]]() + + func diffWaysToCompute(_ input: String) -> [Int] { + if let nums = memo[input] { + return nums + } + + var res = [Int]() + let chars = Array(input.characters) + + for (i, char) in chars.enumerated() { + if char == "+" || char == "*" || char == "-" { + let leftResults = diffWaysToCompute(String(chars[0.. Int { + let m = dungeon.count, n = dungeon[0].count + + guard m > 0, n > 0 else { + return 1 + } + + var minHP = Array(repeating: Array(repeating: 1, count: n), count: m) + + for i in (0.. Int { - let aChars = [Character](word1.characters) - let bChars = [Character](word2.characters) - let aLen = aChars.count - let bLen = bChars.count + let word1Chars = Array(word1), word2Chars = Array(word2), m = word1.count, n = word2.count + var distances = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1) - var dp = Array(count: aLen + 1, repeatedValue:(Array(count: bLen + 1, repeatedValue: 0))) - - for i in 0 ... aLen { - for j in 0 ... bLen { + for i in 0...m { + for j in 0...n { if i == 0 { - dp[i][j] = j + distances[i][j] = j } else if j == 0 { - dp[i][j] = i - } else if aChars[i - 1] == bChars[j - 1] { - dp[i][j] = dp[i - 1][j - 1] + distances[i][j] = i + } else if word1Chars[i - 1] == word2Chars[j - 1] { + distances[i][j] = distances[i - 1][j - 1] } else { - dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1 + distances[i][j] = min(distances[i - 1][j - 1], distances[i - 1][j], distances[i][j - 1]) + 1 } + } } - return dp[aLen][bLen] + return distances[m][n] } } \ No newline at end of file diff --git a/DP/FlipGameII.swift b/DP/FlipGameII.swift new file mode 100644 index 00000000..afb69225 --- /dev/null +++ b/DP/FlipGameII.swift @@ -0,0 +1,39 @@ +/** + * Question Link: https://leetcode.com/problems/flip-game-ii/ + * Primary idea: Classic DP, using a map to memorize previous step + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class FlipGameII { + func canWin(_ s: String) -> Bool { + var winMap = [String: Bool]() + + return helper(s, &winMap) + } + + func helper(_ s: String, _ winMap: inout [String: Bool]) -> Bool { + guard s.count >= 2 else { + return false + } + + if let sWin = winMap[s] { + return sWin + } + + let sChars = Array(s) + + for i in 0.. Bool { + guard stones.count > 1 else { + return true + } + + var indexSteps = [Int: Set](), maxStep = Array(repeating: 0, count: stones.count) + var k = 0 + + insert(&indexSteps, 0, 0) + + for i in 1.. 0 + } + + private func insert(_ dict: inout [Int: Set], _ num: Int, _ index: Int) { + if dict[index] != nil { + dict[index]!.insert(num) + } else { + var set = Set() + set.insert(num) + dict[index] = set + } + } +} diff --git a/DP/GenerateParentheses.swift b/DP/GenerateParentheses.swift new file mode 100644 index 00000000..8e0b65c8 --- /dev/null +++ b/DP/GenerateParentheses.swift @@ -0,0 +1,34 @@ +/** + * Question Link: https://leetcode.com/problems/generate-parentheses/ + * Primary idea: Insert left and right parentheses and ensure they are valid + * Time Complexity: O(2^n), Space Complexity: O(n) + * + */ + +class GenerateParentheses { + func generateParenthesis(_ n: Int) -> [String] { + guard n > 0 else { + return [String]() + } + + var paths = [String](), path = "" + + dfs(&paths, path, n, n) + + return paths + } + + private func dfs(_ paths: inout [String], _ path: String, _ leftRemaining: Int, _ rightRemaining: Int) { + if rightRemaining == 0 { + paths.append(path) + return + } + + if leftRemaining > 0 { + dfs(&paths, path + "(", leftRemaining - 1, rightRemaining) + } + if rightRemaining > leftRemaining { + dfs(&paths, path + ")", leftRemaining, rightRemaining - 1) + } + } +} diff --git a/DP/GuessNumberHigherOrLowerII.swift b/DP/GuessNumberHigherOrLowerII.swift new file mode 100644 index 00000000..2e2a8a27 --- /dev/null +++ b/DP/GuessNumberHigherOrLowerII.swift @@ -0,0 +1,33 @@ +/** + * Question Link: https://leetcode.com/problems/guess-number-higher-or-lower-ii/ + * Primary idea: Typical DP problem + * For each guess: + * 1) Get the worse case of every choice to guarantee the result + * 2) Get the least cost among the guaranteed results + * Time Complexity: O(nlogn), Space Complexity: O(n^2) + */ + +class GuessNumberHigherOrLowerII { + func getMoneyAmount(_ n: Int) -> Int { + var table = [[Int]](repeatElement([Int](repeatElement(0, count: n + 1)), count: n + 1)) + return DP(&table, 1, n) + } + + private func DP(_ table: inout [[Int]], _ s: Int, _ e: Int) -> Int { + if s >= e { + return 0 + } + if table[s][e] != 0 { + return table[s][e] + } + var guarantee = Int.max + for i in s.. Int { - var cur = 0 - var pre = 0 - var res = 0 + var curt = 0, prev = 0 for num in nums { - res = max(cur, pre + num) - pre = cur - cur = res + (curt, prev) = (max(curt, prev + num), curt) } - return res + return curt } } \ No newline at end of file diff --git a/DP/HouseRobberII.swift b/DP/HouseRobberII.swift index dcea0aa5..0df956cc 100644 --- a/DP/HouseRobberII.swift +++ b/DP/HouseRobberII.swift @@ -6,28 +6,25 @@ */ class HouseRobberII { - func rob(nums: [Int]) -> Int { - guard nums.count != 0 else { - return 0 - } + func rob(_ nums: [Int]) -> Int { guard nums.count != 1 else { return nums[0] } - - return max(_helper(nums, 0, nums.count - 2), _helper(nums, 1, nums.count - 1)) + + return max(helper(nums, 0, nums.count - 2), helper(nums, 1, nums.count - 1)) } - private func _helper(nums:[Int], _ start: Int, _ end: Int) -> Int { - var pre = 0 - var cur = 0 - var res = 0 + fileprivate func helper(_ nums: [Int], _ start: Int, _ end: Int) -> Int { + if start > end { + return 0 + } + + var prev = 0, current = 0 - for i in start ... end { - res = max(pre + nums[i], cur) - pre = cur - cur = res + for i in start...end { + (current, prev) = (max(prev + nums[i], current), current) } - return res + return current } } \ No newline at end of file diff --git a/DP/JumpGame.swift b/DP/JumpGame.swift new file mode 100644 index 00000000..5bdd19d5 --- /dev/null +++ b/DP/JumpGame.swift @@ -0,0 +1,25 @@ +/** + * Question Link: https://leetcode.com/problems/jump-game/ + * Primary idea: check each position with the previous farest step can reach. If i > last farest step, means cannot reach + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class JumpGame { + func canJump(_ nums: [Int]) -> Bool { + var maximumIndex = nums[0] + + for (currentIndex, value) in nums.enumerated(){ + + if currentIndex > maximumIndex{ + return false + } + + 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 9072ec85..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.count { - for j in 0 ..< i { - if nums[i] > 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 new file mode 100644 index 00000000..69d0fde2 --- /dev/null +++ b/DP/LongestPalindromicSubstring.swift @@ -0,0 +1,38 @@ +/** + * Question Link: https://leetcode.com/problems/longest-palindromic-substring/ + * 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.count > 1 else { + return s + } + + let sChars = Array(s) + var maxLen = 0, start = 0 + + for i in 0..= 0 && r < chars.count && chars[l] == chars[r] { + l -= 1 + r += 1 + } + + if maxLen < r - l - 1 { + start = l + 1 + maxLen = r - l - 1 + } + } +} diff --git a/DP/MaximalSquare.swift b/DP/MaximalSquare.swift index 631a8f1a..adbd52f8 100644 --- a/DP/MaximalSquare.swift +++ b/DP/MaximalSquare.swift @@ -16,8 +16,8 @@ class MaximalSquare { var max_global = 0 var maxSquareSide = Array(count: m, repeatedValue: (Array(count: n, repeatedValue: 0))) - for i in 0 ..< m { - for j in 0 ..< n { + for i in 0.. 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] { + let sums = createSums(nums), n = nums.count + let leftIndices = createLeftIndices(sums, n, k), rightIndices = createRightIndices(sums, n, k) + var total = 0, res = [-1, -1, -1] + + for i in k...n - 2 * k { + let l = leftIndices[i - 1], r = rightIndices[i + k] + let currentTotal = (sums[i+k] - sums[i]) + (sums[l + k] - sums[l]) + (sums[r + k] - sums[r]) + + if currentTotal > total { + total = currentTotal + res = [l, i, r] + } + } + + return res + } + + fileprivate func createSums(_ nums: [Int]) -> [Int] { + var sums = [0], currentSum = 0 + + for num in nums { + currentSum += num + + sums.append(currentSum) + } + + return sums + } + + // DP for starting index of left + fileprivate func createLeftIndices(_ sums: [Int], _ n: Int, _ k: Int) -> [Int] { + var leftIndices = Array(repeating: 0, count: n), maxSum = sums[k] - sums[0] + + for i in k.. maxSum { + leftIndices[i] = i + 1 - k + maxSum = sums[i + 1] - sums[i + 1 - k] + } else { + leftIndices[i] = leftIndices[i - 1] + } + } + + return leftIndices + } + + // DP for starting index of right + fileprivate func createRightIndices(_ sums: [Int], _ n: Int, _ k: Int) -> [Int] { + var rightIndices = Array(repeating: 0, count: n), maxSum = sums[n] - sums[n - k] + rightIndices[n - k] = n - k + + for i in (0...n - k - 1).reversed() { + if sums[i + k] - sums[i] >= maxSum { + rightIndices[i] = i + maxSum = sums[i + k] - sums[i] + } else { + rightIndices[i] = rightIndices[i + 1] + } + } + + return rightIndices + } +} \ No newline at end of file diff --git a/DP/MinCostClimbingStairs.swift b/DP/MinCostClimbingStairs.swift new file mode 100644 index 00000000..6d46c480 --- /dev/null +++ b/DP/MinCostClimbingStairs.swift @@ -0,0 +1,25 @@ +/** + * Question Link: https://leetcode.com/problems/min-cost-climbing-stairs/description/ + * Primary idea: Dynamic Programming, dp[i] represents the current element will be + * added to previous smallest sum, so the Bellman equation is + * dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + + class MinCostClimbingStairs { + func minCostClimbingStairs(_ cost: [Int]) -> Int { + var dp = [Int](repeating: Int.max, count: cost.count + 1) + (dp[0], dp[1]) = (cost[0], cost[1]) + + for i in 2...cost.count { + if i == cost.count { + dp[i] = min(dp[i - 1], dp[i - 2]) + } else { + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] + } + } + + return dp[cost.count] + } +} \ No newline at end of file diff --git a/DP/MinimumPathSum.swift b/DP/MinimumPathSum.swift new file mode 100644 index 00000000..39b3c028 --- /dev/null +++ b/DP/MinimumPathSum.swift @@ -0,0 +1,28 @@ +/** + * Question Link: https://leetcode.com/problems/minimum-path-sum/ + * Primary idea: Classic Two Dimensionel Dynamic Programming + * Time Complexity: O(mn), Space Complexity: O(mn) + */ + +class MinimumPathSum { + func minPathSum(_ grid: [[Int]]) -> Int { + let m = grid.count, n = grid[0].count + var dp = grid + + for i in 0.. String { + let m = T.count, n = S.count, sChars = Array(S), tChars = Array(T) + var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1) + var start = 0, len = n + 1 + + for i in 0...n { + dp[0][i] = i + 1 + } + + for i in 1...m { + for j in 1...n { + if tChars[i - 1] == sChars[j - 1] { + dp[i][j] = dp[i - 1][j - 1] + } else { + dp[i][j] = dp[i][j - 1] + } + } + } + + for i in 1...n { + if dp[m][i] != 0 { + if i - dp[m][i] + 1 < len { + len = i - dp[m][i] + 1 + start = dp[m][i] - 1 + } + } + } + + return len == n + 1 ? "" : String(sChars[start.. 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/PaintFence.swift b/DP/PaintFence.swift new file mode 100644 index 00000000..7ee62f4c --- /dev/null +++ b/DP/PaintFence.swift @@ -0,0 +1,27 @@ +/** + * Question Link: https://leetcode.com/problems/paint-fence/ + * Primary idea: Dynamic Programming, the current ways are either same as of different than previous one + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class PaintFence { + func numWays(_ n: Int, _ k: Int) -> Int { + if n == 0 || k == 0 { + return 0 + } + if n == 1 { + return k + } + + var lastSame = k + var lastDiff = k * (k - 1) + + for i in 2.. 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.. Int { + var palinCount = 0, dp = Array(repeating: Array(repeating: false, count: s.count), count: s.count) + var s = Array(s) + + // init case with distance of 0 and 1 + for i in 0.. 1 else { + return palinCount + } + + for i in 0.. 2 else { + return palinCount + } + + for distance in 2...s.count - 1 { + for i in 0.. i { break } diff --git a/DP/RegularExpressionMatching.swift b/DP/RegularExpressionMatching.swift new file mode 100644 index 00000000..9de0c1ec --- /dev/null +++ b/DP/RegularExpressionMatching.swift @@ -0,0 +1,34 @@ +/** + * Question Link: https://leetcode.com/problems/regular-expression-matching/ + * Primary idea: Classic Two Dimensionel Dynamic Programming + * Time Complexity: O(mn), Space Complexity: O(mn) + */ + +class RegularExpressionMatching { + func isMatch(_ s: String, _ p: String) -> Bool { + 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 j in 0...n { + dp[0][j] = j == 0 || (j > 1 && dp[0][j - 2] && p[j - 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 { + 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[m][n] + } +} \ No newline at end of file diff --git a/DP/Triangle.swift b/DP/Triangle.swift new file mode 100644 index 00000000..cbe508ab --- /dev/null +++ b/DP/Triangle.swift @@ -0,0 +1,24 @@ +/** + * Question Link: https://leetcode.com/problems/triangle/ + * Primary idea: Dynamic Programming, start from bottom to top + * Time Complexity: O(2^n), Space Complexity: O(m) + * + */ + + class Triangle { + func minimumTotal(_ triangle: [[Int]]) -> Int { + guard triangle.count > 0 else { + return 0 + } + + var dp = triangle.last! + + for i in stride(from: triangle.count - 2, through: 0, by: -1) { + for j in 0...i { + dp[j] = min(dp[j], dp[j + 1]) + triangle[i][j] + } + } + + return dp[0] + } +} diff --git a/DP/UniquePathsII.swift b/DP/UniquePathsII.swift index f156b21a..8724b14c 100644 --- a/DP/UniquePathsII.swift +++ b/DP/UniquePathsII.swift @@ -5,28 +5,29 @@ */ class UniquePathsII { - func uniquePathsWithObstacles(obstacleGrid: [[Int]]) -> Int { - let m = obstacleGrid.count - let n = obstacleGrid[0].count - - var pathNums = Array(count: m, repeatedValue: Array(count: n, repeatedValue: 0)) - return _helper(&pathNums, m - 1, n - 1, obstacleGrid) - } - - func _helper(inout _ pathNums: [[Int]], _ m: Int, _ n: Int, _ obstacleGrid: [[Int]]) -> Int { - // termination - if m < 0 || n < 0 || obstacleGrid[m][n] == 1 { - return 0 - } - if m == 0 && n == 0 { - return 1 - } - if pathNums[m][n] != 0 { - return pathNums[m][n] - } + func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int { + let m = obstacleGrid.count, n = obstacleGrid[0].count + + var dp = Array(repeating: Array(repeating: 0, count: n), count: m) - pathNums[m][n] = _helper(&pathNums, m - 1, n, obstacleGrid) + _helper(&pathNums, m, n - 1, obstacleGrid) + for i in 0.. Int { + guard nums.count >= 2 else { + return nums.count + } + + var up = 1, down = 1 + + for i in 1.. nums[i - 1] { + up = down + 1 + } else if nums[i] < nums[i - 1] { + down = up + 1 + } + } + + return max(up, down) + } +} \ No newline at end of file diff --git a/DP/WildcardMatching.swift b/DP/WildcardMatching.swift new file mode 100644 index 00000000..b2951836 --- /dev/null +++ b/DP/WildcardMatching.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/wildcard-matching/ + * Primary idea: Classic Two Dimensionel Dynamic Programming + * Time Complexity: O(mn), Space Complexity: O(mn) + */ + + class WildcardMatching { + func isMatch(_ s: String, _ p: String) -> Bool { + 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 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 + } + } else { + dp[i][j] = dp[i][j - 1] || dp[i - 1][j] + } + } + } + + 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.. Bool { + guard n > 0 else { + return true + } + + var roots = [Int](0.. Int { + var node = node + + while node != roots[node] { + node = roots[node] + } + + return node + } +} \ No newline at end of file diff --git a/Graph/NumberConnectedComponentsUndirectedGraph.swift b/Graph/NumberConnectedComponentsUndirectedGraph.swift new file mode 100644 index 00000000..8d40b5d2 --- /dev/null +++ b/Graph/NumberConnectedComponentsUndirectedGraph.swift @@ -0,0 +1,40 @@ +/** + * Question Link: https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/ + * Primary idea: Classic Union Find, decrease count when there is a link between two nodes + * + * Time Complexity: O(nlogn), Space Complexity: O(n) + * + */ + +class NumberConnectedComponentsUndirectedGraph { + func countComponents(_ n: Int, _ edges: [[Int]]) -> Int { + guard n > 0 else { + return 0 + } + + var count = n + var roots = [Int](0...n - 1) + + for edge in edges { + let root0 = findRoot(edge[0], roots) + let root1 = findRoot(edge[1], roots) + + if root0 != root1 { + count -= 1 + roots[root1] = root0 + } + } + + return count + } + + private func findRoot(_ node: Int, _ roots: [Int]) -> Int { + var node = node + + while (node != roots[node]) { + node = roots[node] + } + + return node + } +} \ No newline at end of file diff --git a/Graph/NumberIslandsII.swift b/Graph/NumberIslandsII.swift new file mode 100644 index 00000000..a76a563b --- /dev/null +++ b/Graph/NumberIslandsII.swift @@ -0,0 +1,48 @@ +/** + * Question Link: https://leetcode.com/problems/number-of-islands-ii/ + * Primary idea: Classic Union Find, check four directions and update count every time + * + * Time Complexity: O(klogmn), Space Complexity: O(mn) + * + */ + +class NumberIslandsII { + func numOfIslandsII(_ m: Int, _ n: Int, _ positions: [(Int, Int)]) -> [Int] { + var res = [Int](), count = 0, roots = Array(repeating: -1, count: m * n) + + for position in positions { + var pos = position.0 * n + position.1 + roots[pos] = pos + count += 1 + + for moveDir in [(0, 1), (0, -1), (1, 0), (-1, 0)] { + let i = position.0 + moveDir.0, j = position.1 + moveDir.1 + let movePos = i * n + j + + guard i >= 0 && i < m && j >= 0 && j < n && roots[movePos] != -1 else { + continue + } + + let movePosRoot = findRoot(movePos, roots) + + if movePosRoot != pos { + count -= 1 + roots[pos] = movePosRoot + pos = movePosRoot + } + } + + res.append(count) + } + + return res + } + + fileprivate func findRoot(_ node: Int, _ roots: [Int]) -> Int { + var node = node + while node != roots[node] { + node = roots[node] + } + return node + } +} \ No newline at end of file diff --git a/Leetcode_Contributors.png b/Leetcode_Contributors.png new file mode 100644 index 00000000..29835bf8 Binary files /dev/null and b/Leetcode_Contributors.png differ diff --git a/License.md b/License.md new file mode 100644 index 00000000..9cb24e56 --- /dev/null +++ b/License.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Yi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LinkedList/LFUCache.swift b/LinkedList/LFUCache.swift new file mode 100644 index 00000000..88cdd4b8 --- /dev/null +++ b/LinkedList/LFUCache.swift @@ -0,0 +1,138 @@ +/** + * Question Link: https://leetcode.com/problems/lfu-cache/ + * Primary idea: Use linked list and hash map to build the cache. + * Time Complexity Per Action: O(1), Space Complexity: O(K) + * + */ + +class LFUCache { + + private let capacity: Int + private var nodeMap = [Int: CacheNode]() + private var listMap = [Int: CacheList]() + private var size = 0 + private var leastFrequency = 1 + + init(_ capacity: Int) { + self.capacity = capacity + } + + func get(_ key: Int) -> 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/MergeKSortedLists.swift b/LinkedList/MergeKSortedLists.swift index 5c3af52a..5addaf16 100644 --- a/LinkedList/MergeKSortedLists.swift +++ b/LinkedList/MergeKSortedLists.swift @@ -57,11 +57,7 @@ class MergeKSortedLists { node = node.next! } - if l1 != nil { - node.next = l1 - } else { - node.next = l2 - } + node.next = l1 ?? l2 return dummy.next } diff --git a/LinkedList/MergeTwoSortedLists.swift b/LinkedList/MergeTwoSortedLists.swift index eabee845..030b7c87 100644 --- a/LinkedList/MergeTwoSortedLists.swift +++ b/LinkedList/MergeTwoSortedLists.swift @@ -33,11 +33,7 @@ class MergeTwoSortedLists { node = node.next! } - if l1 != nil { - node.next = l1 - } else if l2 != nil { - node.next = l2 - } + node.next = l1 ?? l2 return dummy.next } diff --git a/LinkedList/OddEvenLinkedList.swift b/LinkedList/OddEvenLinkedList.swift new file mode 100644 index 00000000..8009e41c --- /dev/null +++ b/LinkedList/OddEvenLinkedList.swift @@ -0,0 +1,45 @@ +/** + * Question Link: https://leetcode.com/problems/odd-even-linked-list/ + * Primary idea: Prev-post two pointers; change the prev and move both at a time + * Time Complexity: O(n), Space Complexity: O(1) + * + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * } + * } + */ + +class OddEvenLinkedList { + func oddEvenList(_ head: ListNode?) -> ListNode? { + guard head != nil, head!.next != nil else { + return head + } + + var prev = head, post = head!.next, isEndEven = true + let evenStart = post + + while post!.next != nil { + prev!.next = post!.next + + prev = post + post = post!.next + + isEndEven = !isEndEven + } + + if isEndEven { + prev!.next = evenStart + } else { + prev!.next = nil + post!.next = evenStart + } + + + return head + } +} 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/LinkedList/PartitionList.swift b/LinkedList/PartitionList.swift index 9625a6b5..8355c930 100644 --- a/LinkedList/PartitionList.swift +++ b/LinkedList/PartitionList.swift @@ -1,8 +1,6 @@ /** * Question Link: https://leetcode.com/problems/partition-list/ - * Primary idea: Tail Insert and merge two lists - * - * Note: Swift provides "!==" to determine two objects refer to the same reference + * Primary idea: Tail Insert and merge two lists, use dummy to avoid edge case * * Time Complexity: O(n), Space Complexity: O(1) * Definition for singly-linked list. @@ -17,27 +15,26 @@ */ class PartitionList { - func partition(head: ListNode?, _ x: Int) -> ListNode? { - let prevDummy = ListNode(0) - var prev = prevDummy - let postDummy = ListNode(0) - var post = postDummy + func partition(_ head: ListNode?, _ x: Int) -> ListNode? { + let prevDummy = ListNode(0), postDummy = ListNode(0) + var prev = prevDummy, post = postDummy var node = head while node != nil { + let next = node!.next + node!.next = nil + if node!.val < x { prev.next = node - prev = node! + prev = prev.next! } else { post.next = node - post = node! + post = post.next! } - - node = node!.next + node = next } - post.next = nil prev.next = postDummy.next return prevDummy.next diff --git a/LinkedList/RemoveDuplicatesFromSortedList.swift b/LinkedList/RemoveDuplicatesFromSortedList.swift index 6270297d..88066904 100644 --- a/LinkedList/RemoveDuplicatesFromSortedList.swift +++ b/LinkedList/RemoveDuplicatesFromSortedList.swift @@ -1,6 +1,6 @@ /** * Question Link: https://leetcode.com/problems/remove-duplicates-from-sorted-list/ - * Primary idea: Two pointers, iterate the list and only reserve right nodes + * Primary idea: Iterate the list, jump over duplicates by replacing next with next.next * Time Complexity: O(n), Space Complexity: O(1) * * Definition for singly-linked list. @@ -15,22 +15,20 @@ */ class RemoveDuplicatesFromSortedList { - func deleteDuplicates(head: ListNode?) -> ListNode? { + func deleteDuplicates(_ head: ListNode?) -> ListNode? { guard let head = head else { return nil } - var prev = head - var curr = head.next + var curt = head - while curr != nil { - if prev.val != curr!.val { - prev.next = curr - prev = curr! - } - curr = curr!.next + while curt.next != nil { + if curt.next!.val == curt.val { + curt.next = curt.next!.next + } else { + curt = curt.next! + } } - prev.next = nil return head } diff --git a/LinkedList/RemoveDuplicatesFromSortedListII.swift b/LinkedList/RemoveDuplicatesFromSortedListII.swift index ea2fe37b..57fe5158 100644 --- a/LinkedList/RemoveDuplicatesFromSortedListII.swift +++ b/LinkedList/RemoveDuplicatesFromSortedListII.swift @@ -1,7 +1,6 @@ /** * Question Link: https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/ - * Primary idea: Three pointers, iterate the list and only reserve right nodes, - * specially handle the last one afterwards + * Primary idea: Iterate the list, jump over duplicates by replacing next with next.next * * Note: Swift provides "===" to compare two objects refer to the same reference * @@ -19,40 +18,26 @@ */ class RemoveDuplicatesfromSortedListII { - func deleteDuplicates(head: ListNode?) -> ListNode? { + func deleteDuplicates(_ head: ListNode?) -> ListNode? { if head == nil || head!.next == nil { return head } let dummy = ListNode(0) dummy.next = head - var node = dummy - var prev = head - var post = head!.next - while post != nil { - if post!.val != prev!.val && prev!.next === post{ - node.next = prev - node = prev! - prev = post! - post = post!.next - } else { - if post!.val != prev!.val { - prev = post! - post = post!.next - } else { - post = post!.next + while node.next != nil && node.next!.next != nil { + if node.next!.val == node.next!.next!.val { + let val = node.next!.val + while node.next != nil && node.next!.val == val { + node.next = node.next!.next } + } else { + node = node.next! } } - if prev!.next != nil { - node.next = nil - } else { - node.next = prev - } - return dummy.next } } \ No newline at end of file diff --git a/LinkedList/RemoveLinkedListElements.swift b/LinkedList/RemoveLinkedListElements.swift index a306e595..26ea7eda 100644 --- a/LinkedList/RemoveLinkedListElements.swift +++ b/LinkedList/RemoveLinkedListElements.swift @@ -1,6 +1,6 @@ /** * Question Link: https://leetcode.com/problems/remove-linked-list-elements/ - * Primary idea: Two pointers, iterate the list and only reserve right nodes + * Primary idea: Iterate the list, jump over vals by replacing next with next.next * Time Complexity: O(n), Space Complexity: O(1) * * Definition for singly-linked list. @@ -15,24 +15,18 @@ */ class RemoveLinkedListElements { - func removeElements(head: ListNode?, _ val: Int) -> ListNode? { + func removeElements(_ head: ListNode?, _ val: Int) -> ListNode? { let dummy = ListNode(0) dummy.next = head + var node = dummy - var prev = dummy - var curr = head - - while curr != nil { - if curr!.val == val { - curr = curr!.next + while node.next != nil { + if node.next!.val == val { + node.next = node.next!.next } else { - prev.next = curr - prev = curr! - curr = curr!.next + node = node.next! } } - // remember to handle the last one - prev.next = nil return dummy.next } diff --git a/LinkedList/RemoveNthFromEnd.swift b/LinkedList/RemoveNthFromEnd.swift index 17ecade3..80c604b5 100644 --- a/LinkedList/RemoveNthFromEnd.swift +++ b/LinkedList/RemoveNthFromEnd.swift @@ -15,27 +15,19 @@ */ class RemoveNthFromEnd { - func removeNthFromEnd(head: ListNode?, _ n: Int) -> ListNode? { - guard let head = head else { - return nil - } - + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { let dummy = ListNode(0) dummy.next = head var prev: ListNode? = dummy var post: ListNode? = dummy // move post - for _ in 0 ..< n { - if post == nil { - break - } - + for _ in 0.. ListNode?{ + + private func _reverse(inout head: ListNode?) -> ListNode? { var prev = head var temp: ListNode? - + while prev != nil { let post = prev!.next - + prev!.next = temp - + temp = prev prev = post } - + return temp } + + private func _merge(inout prev: ListNode?, inout _ post: ListNode?) { + while prev != nil && post != nil{ + let preNext = prev!.next + let posNext = post!.next + + prev!.next = post + post!.next = preNext + + prev = preNext + post = posNext + } + } } \ No newline at end of file diff --git a/LinkedList/ReverseLinkedList.swift b/LinkedList/ReverseLinkedList.swift index 9ad7737d..34c5ef6a 100644 --- a/LinkedList/ReverseLinkedList.swift +++ b/LinkedList/ReverseLinkedList.swift @@ -30,4 +30,17 @@ class ReverseLinkedList { return temp } + + func reverseList(_ head: ListNode?) -> ListNode? { + guard let h = head, let next = h.next else { + return head + } + + let node = reverseList(next) + + next.next = h + h.next = nil + + return node + } } \ No newline at end of file diff --git a/LinkedList/ReverseNodesInKGroup.swift b/LinkedList/ReverseNodesInKGroup.swift new file mode 100644 index 00000000..05058443 --- /dev/null +++ b/LinkedList/ReverseNodesInKGroup.swift @@ -0,0 +1,49 @@ +/** + * Question Link: https://leetcode.com/problems/reverse-nodes-in-k-group/ + * Primary idea: Reverse one group during one loop. Construct a reversed group from back to forth. + * Time Complexity: O(n), Space Complexity: O(1) + * + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * } + * } + */ + +class ReverseNodesInKGroup { + func reverseKGroup(_ head: ListNode?, _ k: Int) -> ListNode? { + let dummy = ListNode(0) + dummy.next = head + + var prev = dummy // prev = A + while prev.next != nil { // A->B->C->D->E => A->D->C->B->E + var groupTail : ListNode? = prev + for _ in 1...k { + groupTail = groupTail?.next + } + guard let _ = groupTail else { // groupTail = D + break + } + + let nextGroupHead = groupTail!.next // nextGroupHead = E + var last = nextGroupHead // last = E + var current : ListNode? = prev.next // current = B + while current != nil && current !== nextGroupHead { + let next = current!.next // next = C + current!.next = last // B -> E + last = current // last = B + current = next // current = C + } + + groupTail = prev.next + prev.next = last + prev = groupTail! + } + + return dummy.next + } +} diff --git a/LinkedList/SwapNodesInPairs.swift b/LinkedList/SwapNodesInPairs.swift index 2d25cacb..7a5c8aa7 100644 --- a/LinkedList/SwapNodesInPairs.swift +++ b/LinkedList/SwapNodesInPairs.swift @@ -16,29 +16,22 @@ class SwapNodesInPairs { func swapPairs(head: ListNode?) -> ListNode? { - if head == nil || head!.next == nil { - return head - } - let dummy = ListNode(0) dummy.next = head - var prev = dummy - var fir = dummy.next - var sec = fir!.next - while sec != nil { - let next = sec!.next + var prev: ListNode? = dummy + var current = dummy.next + + while current != nil && current!.next != nil { + let next = current!.next + let post = current!.next!.next - prev.next = sec - sec!.next = fir - fir!.next = next + prev!.next = next + next!.next = current + current!.next = post - if next == nil { - break - } - prev = fir! - fir = next - sec = next!.next + prev = current + current = post } return dummy.next diff --git a/Math/AddBinary.swift b/Math/AddBinary.swift index 126a4320..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 @@ -9,34 +9,26 @@ */ class AddBinary { - func addBinary(a: String, _ b: String) -> String { - var res = "" - var aChars = [Character](a.characters) - var bChars = [Character](b.characters) - - var i = aChars.count - 1 - var j = bChars.count - 1 - var carry = 0 - var num = 0 - + func addBinary(_ a: String, _ b: String) -> String { + 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 { - num = carry - + var sum = carry + if i >= 0 { - num += Int(String(aChars[i]))! + sum += Int(String(a[i]))! i -= 1 } if j >= 0 { - num += Int(String(bChars[j]))! + sum += Int(String(b[j]))! j -= 1 } - - carry = num / 2 - num = num % 2 - - res = String(num) + res + + res = "\(sum % 2)" + res + carry = sum / 2 } - + return res } } \ No newline at end of file diff --git a/Math/AddDigits.swift b/Math/AddDigits.swift new file mode 100644 index 00000000..c12657af --- /dev/null +++ b/Math/AddDigits.swift @@ -0,0 +1,22 @@ +/** + * Question Link: https://leetcode.com/problems/add-digits/ + * Primary idea: Transform number into array of string and sum the values recursively until the result is achieved + * + * Time Complexity: O(1), Space Complexity: O(1) + * + */ + +class AddDigits { + func add (num: Int) -> Int { + let numString = String(num) + guard numString.characters.count > 1 else { + return num + } + + let array = Array(numString.characters).map { String($0) } + + let sum = array.reduce(0) { $0.0 + Int($0.1)! } + + return add(num: sum) + } +} diff --git a/Math/AddTwoNumbers.swift b/Math/AddTwoNumbers.swift index fcee9d8a..3930ce6c 100644 --- a/Math/AddTwoNumbers.swift +++ b/Math/AddTwoNumbers.swift @@ -16,33 +16,18 @@ class AddTwoNumbers { func addTwoNumbers(l1: ListNode?, _ l2: ListNode?) -> ListNode? { - var carry = 0 - var sum = 0 - let dummy = ListNode(0) - var node = dummy - var l1 = l1 - var l2 = l2 + guard let l1 = l1 else {return l2} + guard let l2 = l2 else {return l1} - while l1 != nil || l2 != nil || carry != 0 { - sum = carry - - if l1 != nil { - sum += l1!.val - l1 = l1!.next - } - if l2 != nil { - sum += l2!.val - l2 = l2!.next - } - - carry = sum / 10 - sum = sum % 10 - - node.next = ListNode(sum) - node = node.next! + 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/ContainerMostWater.swift b/Math/ContainerMostWater.swift new file mode 100644 index 00000000..f86bf74c --- /dev/null +++ b/Math/ContainerMostWater.swift @@ -0,0 +1,28 @@ +/** + * Question Link: https://leetcode.com/problems/container-with-most-water/ + * Primary idea: First given largest width, then go to height-increase direction while width decreases + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class ContainerMostWater { + func maxArea(_ height: [Int]) -> Int { + var maxRes = 0 + var left = 0 + var right = height.count - 1 + + while left < right { + let minHeight = min(height[left], height[right]) + + maxRes = max(maxRes, (right - left) * minHeight) + + if minHeight == height[left] { + left += 1 + } else { + right -= 1 + } + } + + return maxRes + } +} \ No newline at end of file diff --git a/Math/CountPrimes.swift b/Math/CountPrimes.swift new file mode 100644 index 00000000..72bda921 --- /dev/null +++ b/Math/CountPrimes.swift @@ -0,0 +1,38 @@ +/** + * 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 number starting from its square + * + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class CountPrimes { + func countPrimes(_ n: Int) -> Int { + guard n > 2 else { + return 0 + } + + // init isPrime bool array + var isPrime = Array(repeating: true, count: n) + isPrime[0] = false + isPrime[1] = false + + // count prime number + var count = 0 + for num in 2.. [Int] { + guard num > 0 else { + return [0] + } + + var bits = [Int](count: num + 1, repeatedValue: 0) + var x = 1 + for i in 1...num { + if i == x { + x = x << 1 + bits[i] = 1 + } else { + bits[i] = bits[i - (x >> 1)] + 1 + } + } + + return bits + } +} diff --git a/Math/DivideTwoIntegers.swift b/Math/DivideTwoIntegers.swift new file mode 100644 index 00000000..a9fef816 --- /dev/null +++ b/Math/DivideTwoIntegers.swift @@ -0,0 +1,42 @@ +/** + * Question Link: https://leetcode.com/problems/divide-two-integers/ + * Primary idea: Use left shift and subtraction to get the number of every digit + * Time Complexity: O(logn), Space Complexity: O(1) + * + */ + + class DivideTwoIntegers { + func divide(_ dividend: Int, _ divisor: Int) -> Int { + let isPositive = (dividend < 0) == (divisor < 0) + var dividend = abs(dividend), divisor = abs(divisor), count = 0 + + while dividend >= divisor { + var shift = 0 + + while dividend >= (divisor << shift) { + shift += 1 + } + + dividend -= divisor << (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 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.. Int { + var z = x ^ y, count = 0 + + while z > 0 { + count += z & 1 + z = z >> 1 + } + + return count + } +} 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/IntegerBreak.swift b/Math/IntegerBreak.swift index c1005196..700d3604 100644 --- a/Math/IntegerBreak.swift +++ b/Math/IntegerBreak.swift @@ -1,6 +1,6 @@ /** * Question Link: https://leetcode.com/problems/integer-break/ - * Primary idea: Final Result must be split as 2^m * 3^n. Lets say p = p1 + p2 + ... + pn, + * Primary idea: Final Result must be split as 2^m * 3^n. Lets say p = p1 + p2 +...+ pn, * if p1 could be split as 2(p1 - 2), than it would be greater than p1 if p1 > 4. * same thing for 3(p1 - 3). Thus we spilt the original number to multiple 3s and 2s to * get the final result diff --git a/Math/IntegerEnglishWords.swift b/Math/IntegerEnglishWords.swift new file mode 100644 index 00000000..8fbc3811 --- /dev/null +++ b/Math/IntegerEnglishWords.swift @@ -0,0 +1,48 @@ +/** + * Question Link: https://leetcode.com/problems/integer-to-english-words/ + * Primary idea: Divide and mod 1000, 100, and 10 to get string for each digit + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class IntegerEnglishWords { + func numberToWords(_ num: Int) -> String { + let l1 = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"] + let l2 = ["Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"] + let l3 = "Hundred" + let l4 = ["Thousand", "Million", "Billion"] + var res = "", num = num, digit = -1 + + if num == 0 { + return "Zero" + } + + while num > 0 { + var temp = "", token = num % 1000 + + if token > 99 { + temp += "\(l1[token / 100]) \(l3) " + token %= 100 + } + + if token > 19 { + temp += "\(l2[token / 10 - 2]) " + token %= 10 + } + + if token > 0 { + temp += "\(l1[token]) " + } + + if digit != -1 && temp != "" { + res = temp + l4[digit] + " " + res + } else { + res = temp + res + } + digit += 1 + num /= 1000 + } + + return String(res.characters.dropLast()) + } +} \ No newline at end of file diff --git a/Math/IntegerToRoman.swift b/Math/IntegerToRoman.swift new file mode 100644 index 00000000..dd292533 --- /dev/null +++ b/Math/IntegerToRoman.swift @@ -0,0 +1,47 @@ +/** + * Question Link: https://leetcode.com/problems/integer-to-roman/ + * Primary idea: Add symbols from big to small, minus relative number as well + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class IntegerToRoman { + func intToRoman(_ num: Int) -> String { + guard num > 0 else { + return "" + } + + let nums = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1] + let symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"] + var res = "" + var digit = 0 + var number = num + + while number > 0 { + let current = number / nums[digit] + + for _ in 0.. String { + guard num > 0 else { return "" } + + let thousands = ["", "M", "MM", "MMM"] + let hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"] + let tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"] + let ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"] + + return thousands[num / 1000] + hundreds[num % 1000 / 100] + tens[num % 100 / 10] + ones[num % 10] + } +} diff --git a/Math/KthSmallestLexicographicalOrder.swift b/Math/KthSmallestLexicographicalOrder.swift new file mode 100644 index 00000000..c258da22 --- /dev/null +++ b/Math/KthSmallestLexicographicalOrder.swift @@ -0,0 +1,34 @@ +/** + * Question Link: https://leetcode.com/problems/k-th-smallest-in-lexicographical-order/ + * Primary idea: Image it is a ten-deminsion tree, we only need to calculate the number + * of children for each node, and keep minus until we get the right 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), Space Complexity: O(1) + * + */ + +class KthSmallestLexicographicalOrder { + func findKthNumber(_ n: Int, _ k: Int) -> Int { + var curt = 1, k = k - 1 + + while k > 0 { + var step = 0, first = curt, last = curt + 1 + while first <= n { + step += min(n + 1, last) - first + first *= 10 + last *= 10 + } + if step <= k { + curt += 1 + k -= step + } else { + curt *= 10 + k -= 1 + } + } + + return curt + } +} \ No newline at end of file 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/MinimumMovesEqualArrayElements.swift b/Math/MinimumMovesEqualArrayElements.swift new file mode 100644 index 00000000..418cd788 --- /dev/null +++ b/Math/MinimumMovesEqualArrayElements.swift @@ -0,0 +1,15 @@ +/** + * Question Link: https://leetcode.com/problems/minimum-moves-to-equal-array-elements/ + * Primary idea: Adding 1 to n - 1 elements is the same as subtracting 1 from one element, + * the best way is to make all the elements equal to the min element. + * + * Time Complexity: O(n), Space Complexity: O(1) + */ + +class MinimumMovesEqualArrayElements { + func minMoves(_ nums: [Int]) -> Int { + let minNum = nums.min()! + + return nums.reduce(0) { total, num in total + num - minNum } + } +} \ No newline at end of file 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/NumberComplement.swift b/Math/NumberComplement.swift new file mode 100644 index 00000000..10385b9f --- /dev/null +++ b/Math/NumberComplement.swift @@ -0,0 +1,22 @@ +/** + * Question Link: https://leetcode.com/problems/number-complement/ + * Primary idea: Caculate digit by digit, and use offset to left shift + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class NumberComplement { + func findComplement(_ num: Int) -> Int { + var res = 0, num = num, offset = 0 + + while num > 0 { + res = res + ((num & 1) ^ 1) << offset + + num = num >> 1 + offset += 1 + } + + return res + } +} \ No newline at end of file diff --git a/Math/PalindromeNumber.swift b/Math/PalindromeNumber.swift new file mode 100644 index 00000000..0e077956 --- /dev/null +++ b/Math/PalindromeNumber.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/palindrome-number/ + * Primary idea: Negative numbers are not palindromes. + * + * Time Complexity: O(1), Space Complexity: O(1) + */ + + class PalindromeNumber { + func isPalindrome(x: Int) -> Bool { + guard x >= 0 else { + return false + } + + var x = x + var div = 1 + + while (x / div >= 10) { + div = div * 10 + } + + while (x > 0) { + var left = x / div + var right = x % 10 + + if (left != right) { + return false + } + + x = (x % div) / 10 + div = div / 100 + } + + return true + } +} \ 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/RectangleArea.swift b/Math/RectangleArea.swift new file mode 100644 index 00000000..7136018f --- /dev/null +++ b/Math/RectangleArea.swift @@ -0,0 +1,23 @@ +/** + * Question Link: https://leetcode.com/problems/rectangle-area/ + * Primary idea: Add areas of two and minus the overlap one + * Time Complexity: O(1), Space Complexity: O(1) + * + */ + + class RectangleArea { + func computeArea(_ A: Int, _ B: Int, _ C: Int, _ D: Int, _ E: Int, _ F: Int, _ G: Int, _ H: Int) -> Int { + let areaOne = (C - A) * (D - B) + let areaTwo = (G - E) * (H - F) + + return areaOne + areaTwo - _overlap(A, C, E, G) * _overlap(B, D, F, H) + } + + private func _overlap(_ A: Int, _ B: Int, _ C: Int, _ D: Int) -> Int { + if B <= C || A >= D { + return 0 + } + + return min(B, D) - max(A, C) + } +} \ No newline at end of file diff --git a/Math/ReverseInteger.swift b/Math/ReverseInteger.swift new file mode 100644 index 00000000..740f80bc --- /dev/null +++ b/Math/ReverseInteger.swift @@ -0,0 +1,22 @@ +/** + * Question Link: https://leetcode.com/problems/reverse-integer/ + * Primary idea: Using % 10 to reversely iterate through digits of the number, + * and use * 10 to update the result accordingly + * Note: Handle integer overflow at proper time + * Time Complexity: O(n), Space Complexity: O(1) + */ + + class ReverseInteger { + func reverse(_ x: Int) -> Int { + var res = 0 + var x = x + while x != 0 { + if res > Int(Int32.max) / 10 || res < Int(Int32.min) / 10 { + return 0 + } + res = res * 10 + x % 10 + x = x / 10 + } + return res + } +} diff --git a/Math/RomanToInteger.swift b/Math/RomanToInteger.swift index 845edabe..b18b5047 100644 --- a/Math/RomanToInteger.swift +++ b/Math/RomanToInteger.swift @@ -11,7 +11,7 @@ class RomanToInteger { let chars = [Character](s.characters.reverse()) var res = 0 - for i in 0 ..< chars.count { + for i in 0.. Int { + var ans = 0 + var sum = 0 + + for i in 0..<64 { + sum = 0 + let tmp = (1 << i) + for j in 0.. [[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 { + let m = board.count, n = board[0].count + + for i in 0.. Bool { + let m = board.count, n = board[0].count + + // check row + for x in 0.. Int { + var a = a + var b = b + + while b != 0 { + (a, b) = (a ^ b, (a & b) << 1) + } + return a + } +} \ No newline at end of file diff --git a/Math/SuperPow.swift b/Math/SuperPow.swift new file mode 100644 index 00000000..f9772363 --- /dev/null +++ b/Math/SuperPow.swift @@ -0,0 +1,32 @@ +/** + * Question Link: https://leetcode.com/problems/super-pow/ + * Primary idea: a * b % k = (a % k) * (b % k) % k + * a ^ b % k = [a ^ (b / 10 * 10) % k] * [a ^ (b % 10) % k] % k + * f(a, b) = f(a, b / 10 * 10) * f(a, b % 10) % k + * = f(f(a, b / 10), 10) * f(a, b % 10) % k + * + * Time Complexity: O(n), Space Complexity: O(1) + */ + +class SuperPow { + let base = 1337 + + func superPow(a: Int, _ b: [Int]) -> Int { + return _superPowHelper(a, b, b.count - 1) + } + + private func _pow(a: Int, _ b: Int) -> Int { + var ret = 1 + for _ in 0.. Int { + guard idx >= 0 else { + return 1 + } + return _pow(_superPowHelper(a, b, idx - 1), 10) * _pow(a, b[idx]) % base + } +} diff --git a/Math/SuperUglyNumber.swift b/Math/SuperUglyNumber.swift index 47b79f97..b3c55bf4 100644 --- a/Math/SuperUglyNumber.swift +++ b/Math/SuperUglyNumber.swift @@ -11,7 +11,7 @@ class SuperUglyNumber { var uglyNums = [Int](count: n, repeatedValue: 1) var dict = _init(primes) - for i in 1 ..< n { + for i in 1.. Int { + guard height.count > 0 else { + return 0 + } + + var res = 0 + var left = _initMaxHeights(height, true) + var right = _initMaxHeights(height, false) + + for i in 0.. [Int] { + var res = [Int](count: height.count, repeatedValue: 0) + var currentMax = 0 + + if isLeft { + 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 921f5141..5a1a1102 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,145 @@ # LeetCode by Swift -[LeetCode Online Judge] (https://leetcode.com/) is a website containing many **algorithm questions**. Most of them are real interview questions of **Google, Facebook, LinkedIn, Apple**, etc. This repo shows my solutions by Swift and the code style is strictly follow [RayWenderlich Swift Style Guide](https://github.com/raywenderlich/swift-style-guide). Please feel free to reference and **STAR** to support this repo, thank you! +[LeetCode Online Judge](https://leetcode.com/) is a website containing many **algorithm questions**. Most of them are real interview questions of **Google, Facebook, LinkedIn, Apple**, etc. This repo shows my solutions by Swift with the code style strictly follows the [RayWenderlich Swift Style Guide](https://github.com/raywenderlich/swift-style-guide). Please feel free to reference and **STAR** to support this repo, thank you! -![Leetcode](https://scontent.xx.fbcdn.net/v/t1.0-9/391890_300545119987425_1275084672_n.jpg?oh=a1ba903f5e420f9de3b8824a70aa2183&oe=57C30872) +![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) +* [Breadth-first search](#breadth-first-search) * [Math](#math) * [Search](#search) * [Sort](#sort) +* [Graph](#graph) +* [Design](#design) ## Companies * [Google](#google) * [Facebook](#facebook) +* [Snapchat](#snapchat) +* [Uber](#uber) +* [Airbnb](#airbnb) * [LinkedIn](#linkedin) +* [Amazon](#amazon) * [Microsoft](#microsoft) + ## Array | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | -[Summary Ranges](https://leetcode.com/problems/summary-ranges/)| [Swift](./Array/SummaryRanges.swift)| Medium| O(n)| O(n)| +[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)| +[Island Perimeter](https://leetcode.com/problems/island-perimeter/)| [Swift](./Array/IslandPerimeter.swift)| Easy| O(nm)| O(1)| +[Majority Element](https://leetcode.com/problems/majority-element/)| [Swift](./Array/MajorityElement.swift)| Easy| O(n)| O(1)| +[Majority Element II](https://leetcode.com/problems/majority-element-ii/)| [Swift](./Array/MajorityElementII.swift)| Medium| O(n)| O(1)| +[First Missing Positive](https://leetcode.com/problems/first-missing-positive/)| [Swift](./Array/FirstMissingPositive.swift)| Hard| O(n)| O(n)| [Intersection of Two Arrays](https://leetcode.com/problems/intersection-of-two-arrays/)| [Swift](./Array/IntersectionTwoArrays.swift)| Easy| O(n)| O(n)| -[Intersection of Two Arrays II](https://leetcode.com/problems/intersection-of-two-arrays-ii/)| [Swift](./Array/IntersectionTwoArraysII.swift)| Easy| O(nlogn)| O(n)| +[Intersection of Two Arrays II](https://leetcode.com/problems/intersection-of-two-arrays-ii/)| [Swift](./Array/IntersectionTwoArraysII.swift)| Easy| O(n)| O(n)| [Contains Duplicate](https://leetcode.com/problems/contains-duplicate/)| [Swift](./Array/ContainsDuplicate.swift)| Easy| O(n)| O(n)| [Contains Duplicate II](https://leetcode.com/problems/contains-duplicate-ii/)| [Swift](./Array/ContainsDuplicateII.swift)| Easy| O(n)| O(n)| [Remove Duplicates from Sorted Array](https://leetcode.com/problems/remove-duplicates-from-sorted-array/)| [Swift](./Array/RemoveDuplicatesFromSortedArray.swift)| Easy| O(n)| O(1)| [Remove Duplicates from Sorted Array II](https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/)| [Swift](./Array/RemoveDuplicatesFromSortedArrayII.swift)| Medium| O(n)| O(1)| [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)| -[3Sum](https://leetcode.com/problems/3sum/)| [Swift](./Array/ThreeSum.swift)| Medium| O(n^2)| O(n)| -[4Sum](https://leetcode.com/problems/4sum/)| [Swift](./Array/FourSum.swift)| Medium| O(n^3)| O(n)| -[Maximum Size Subarray Sum Equals k](https://leetcode.com/problems/maximum-size-subarray-sum-equals-k/)| [Swift](./Array/MaximumSizeSubarraySumEqualsK.swift)| Easy| O(n)| O(n)| -[Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/)| [Swift](./Array/ProductExceptSelf.swift)| Medium| 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/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)| +[Smallest Range](https://leetcode.com/problems/smallest-range/)| [Swift](./Array/SmallestRange.swift)| Hard | O(nm)| O(nm)| +[Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/)| [Swift](./Array/ProductExceptSelf.swift)| Medium| O(n)| O(1)| [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/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)| +[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 | | ----- | -------- | ---------- | ---- | ----- | -[Count and Say](https://leetcode.com/problems/count-and-say/)| [Swift](./String/CountAndSay.swift)| Easy| O(n^2)| O(n)| +[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(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)| [Reverse String](https://leetcode.com/problems/reverse-string/)| [Swift](./String/ReverseString.swift)| Easy| O(n)| O(n)| +[Reverse String II](https://leetcode.com/problems/reverse-string-ii/)| [Swift](./String/ReverseStringII.swift)| Easy| O(n)| O(n)| [Reverse Vowels of a String](https://leetcode.com/problems/reverse-vowels-of-a-string/)| [Swift](./String/ReverseVowelsOfAString.swift)| Easy| O(n)| O(n)| -[Length of Last Word](https://leetcode.com/problems/length-of-last-word/)| [Swift](./String/LengthLastWord.swift)| Easy| O(n)| O(n)| +[Reverse Words in a String](https://leetcode.com/problems/reverse-words-in-a-string/)| [Swift](./String/ReverseWordsString.swift)| Medium| O(n)| O(1)| +[Reverse Words in a String II](https://leetcode.com/problems/reverse-words-in-a-string-ii/)| [Swift](./String/ReverseWordsStringII.swift)| Medium| O(n)| O(1)| +[Reverse Words in a String III](https://leetcode.com/problems/reverse-words-in-a-string-iii/)| [Swift](./String/ReverseWordsStringIII.swift)| Easy| O(n)| O(1)| +[Length of Last Word](https://leetcode.com/problems/length-of-last-word/)| [Swift](./String/AddStrings.swift)| Easy| O(n)| O(n)| +[String Compression](https://leetcode.com/problems/string-compression/)| [Swift](./String/StringCompression.swift)| Easy| O(n)| O(1)| +[Add Strings](https://leetcode.com/problems/add-strings/)| [Swift](./String/LengthLastWord.swift)| Easy| O(n)| O(1)| +[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) [Longest Common Prefix](https://leetcode.com/problems/longest-common-prefix/)| [Swift](./String/LongestCommonPrefix.swift)| Easy| O(nm)| O(m)| [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)| +[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 | @@ -73,143 +151,348 @@ [Remove Duplicates from Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list/)| [Swift](./LinkedList/RemoveDuplicatesFromSortedList.swift)| Easy| O(n)| O(1)| [Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/)| [Swift](./LinkedList/RemoveDuplicatesFromSortedListII.swift)| Medium| O(n)| O(1)| [Remove Nth Node From End of List](https://leetcode.com/problems/remove-nth-node-from-end-of-list/)| [Swift](./LinkedList/RemoveNthFromEnd.swift)| Easy| O(n)| O(1)| +[Odd Even Linked List](https://leetcode.com/problems/odd-even-linked-list/)| [Swift](./LinkedList/OddEvenLinkedList.swift)| Medium| O(n)| O(1)| [Rotate List](https://leetcode.com/problems/rotate-list/)| [Swift](./LinkedList/RotateList.swift)| Medium| O(n)| O(1)| [Reorder List](https://leetcode.com/problems/reorder-list/)| [Swift](./LinkedList/ReorderList.swift)| Medium| O(n)| O(1)| [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(1)| -[Symmetric Tree](https://leetcode.com/problems/symmetric-tree/)| [Swift](./Tree/SymmetricTree.swift)| Easy| O(n)| O(1)| -[Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/)| [Swift](./Tree/InvertBinaryTree)| Easy| O(n)| O(1)| +[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.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)| -[Balanced Binary Tree](https://leetcode.com/problems/balanced-binary-tree/)| [Swift](./Tree/BalancedBinaryTree.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(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)| +[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)| -[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(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(n)| O(1)| -[Path Sum](https://leetcode.com/problems/path-sum/)| [Swift](./Tree/PathSum.swift)| Easy| O(n)| O(1)| -[Path Sum II](https://leetcode.com/problems/path-sum-ii/)| [Swift](./Tree/PathSumII.swift)| Medium| O(n)| O(1)| +[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(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)| +[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)| +[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 | | ----- | -------- | ---------- | ---- | ----- | [Nested List Weight Sum](https://leetcode.com/problems/nested-list-weight-sum/)| [Swift](./DP/NestedListWeightSum.swift)| Easy| O(n)| O(1)| [Climbing Stairs](https://leetcode.com/problems/climbing-stairs/)| [Swift](./DP/ClimbingStairs.swift)| Easy| O(n)| O(1)| +[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) | 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)| [Best Time to Buy and Sell Stock](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/)| [Swift](./DP/BestTimeBuySellStock.swift)| Easy| O(n)| O(1)| +[Best Time to Buy and Sell Stock II](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/)| [Swift](./DP/BestTimeBuySellStockII.swift)| Medium| O(n)| O(1)| +[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)| [Coin Change](https://leetcode.com/problems/coin-change/)| [Swift](./DP/CoinChange.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)| +[Coin Change II](https://leetcode.com/problems/coin-change-ii/)| [Swift](./DP/CoinChangeII.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)| [House Robber](https://leetcode.com/problems/house-robber/)| [Swift](./DP/HouseRobber.swift)| Easy| O(n)| O(1)| [House Robber II](https://leetcode.com/problems/house-robber-ii/)| [Swift](./DP/HouseRobberII.swift)| Medium| O(n)| O(1)| +[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)| +[Triangle](https://leetcode.com/problems/triangle/)| [Swift](./DP/Triangle.swift)| Medium| O(2^n)| O(m)| +[Wiggle Subsequence](https://leetcode.com/problems/wiggle-subsequence/)| [Swift](./DP/WiggleSubsequence.swift)| Medium| O(n)| O(1)| +[Wildcard Matching](https://leetcode.com/problems/wildcard-matching/)| [Swift](./DP/WildcardMatching.swift)| Hard| O(mn)| O(mn)| +[Regular Expression Matching](https://leetcode.com/problems/regular-expression-matching/)| [Swift](./DP/RegularExpressionMatching.swift)| Hard| O(mn)| O(mn)| +[Minimum Window Subsequence](https://leetcode.com/problems/minimum-window-subsequence/)| [Swift](./DP/MinimumWindowSubsequence.swift)| Hard| O(mn)| O(mn)| +[Guess Number Higher or Lower II](https://leetcode.com/problems/guess-number-higher-or-lower-ii/)| [Swift](./DP/GuessNumberHigherOrLowerII.swift)| Medium| O(nlogn)| O(n^2)| +[Burst Ballons](https://leetcode.com/problems/burst-balloons/)| [Swift](./DP/BurstBalloons.swift)| Hard| O(n^3)| O(n)| +[Frog Jump](https://leetcode.com/problems/frog-jump/)| [Swift](./DP/FrogJump.swift)| Hard| O(n^2)| O(n)| +[Jump Game](https://leetcode.com/problems/jump-game/)| [Swift](./DP/JumpGame.swift)| Medium| O(n)| O(1)| +[Dungeon Game](https://leetcode.com/problems/dungeon-game/)| [Swift](./DP/DungeonGame.swift)| Hard| O(nm)| O(nm)| + ## Depth-first search | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | -[Permutations](https://leetcode.com/problems/permutations/)| [Swift](./DFS/Permutations.swift)| Medium| O(n!)| O(n)| -[Permutations II](https://leetcode.com/problems/permutations-ii/)| [Swift](./DFS/PermutationsII.swift)| Medium| O(n!)| O(n)| -[Subsets](https://leetcode.com/problems/subsets/)| [Swift](./DFS/Subsets.swift)| Medium| O(n!)| O(n)| -[Subsets II](https://leetcode.com/problems/subsets-ii/)| [Swift](./DFS/SubsetsII.swift)| Medium| O(n!)| O(n)| -[Combinations](https://leetcode.com/problems/combinations/)| [Swift](./DFS/Combinations.swift)| Medium| O(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!)| O(2^n - 2)| -[Combination Sum III](https://leetcode.com/problems/combination-sum-iii/)| [Swift](./DFS/CombinationSumIII.swift)| Medium| O(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)| -[Number of Islands](https://leetcode.com/problems/number-of-islands/)| [Swift](./DFS/NumberofIslands.swift)| Medium| O((n^2)!)| O(1)| -[Word Search](https://leetcode.com/problems/word-search/)| [Swift](./DFS/WordSearch.swift)| Medium| O((n^2)!)| O(n^2)| +[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(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)| +[Palindrome Partitioning](https://leetcode.com/problems/palindrome-partitioning/)| [Swift](./DFS/PalindromePartitioning.swift)| Medium| O(n^n)| O(n)| +[Is Graph Bipartite](https://leetcode.com/problems/is-graph-bipartite/)| [Swift](./DFS/IsGraphBipartite.swift)| Medium| O(n)| O(n)| +[Number of Islands](https://leetcode.com/problems/number-of-islands/)| [Swift](./DFS/NumberofIslands.swift)| Medium| O((mn)^2)| O(1)| +[Walls and Gates](https://leetcode.com/problems/walls-and-gates/)| [Swift](./DFS/WallsGates.swift)| Medium| O(n^n)| O(2^n)| +[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^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)| +[Expression Add Operators](https://leetcode.com/problems/expression-add-operators/)| [Swift](./DFS/ExpressionAddOperators.swift)| Hard| O(n^n)| O(n)| +## 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)| ## Math | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | [Add Binary](https://leetcode.com/problems/add-binary/)| [Swift](./Math/AddBinary.swift)| Easy| O(n)| O(n)| [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)| [Ugly Number II](https://leetcode.com/problems/ugly-number-ii/)| [Swift](./Math/UglyNumberII.swift)| Medium| O(n)| O(n)| [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)| +[Super Power](https://leetcode.com/problems/super-pow/)| [Swift](./Math/SuperPow.swift)| Medium| O(n)| O(1)| +[Sum of Two Integers](https://leetcode.com/problems/sum-of-two-integers/)| [Swift](./Math/SumTwoIntegers.swift)| Easy| O(n)| O(1)| +[Reverse Integer](https://leetcode.com/problems/reverse-integer/)| [Swift](./Math/ReverseInteger.swift)| Easy| O(n)| O(1)| [Excel Sheet Column Number](https://leetcode.com/problems/excel-sheet-column-number/)| [Swift](./Math/ExcelSheetColumnNumber.swift)| Easy| O(n)| O(1)| +[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)| +[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 | | ----- | -------- | ---------- | ---- | ----- | [Closest Binary Search Tree Value](https://leetcode.com/problems/closest-binary-search-tree-value/)| [Swift](./Search/ClosestBinarySearchTreeValue.swift)| Easy| O(logn)| O(1)| +[Closest Binary Search Tree Value II](https://leetcode.com/problems/closest-binary-search-tree-value-ii/)| [Swift](./Search/ClosestBinarySearchTreeValueII.swift)| Hard| O(n)| O(n)| [Search in Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/)| [Swift](./Search/SearchInRotatedSortedArray.swift)| Hard| O(logn)| O(1)| [Search in Rotated Sorted Array II](https://leetcode.com/problems/search-in-rotated-sorted-array-ii/)| [Swift](./Search/SearchInRotatedSortedArrayII.swift)| Medium| O(logn)| O(1)| [Find Minimum in Rotated Sorted Array](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/)| [Swift](./Search/FindMinimumRotatedSortedArray.swift)| Medium| O(logn)| O(1)| [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 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 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/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)| ## Sort | Title | Solution | Difficulty | Time | Space | | ----- | -------- | ---------- | ---- | ----- | +[Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array/)| [Swift](./Sort/MergeSortedArray.swift)| Easy| O(n)| O(1)| [Sort Colors](https://leetcode.com/problems/sort-colors/)| [Swift](./Sort/SortColors.swift)| Medium| O(n)| O(1)| [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)| -[Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/)| [Swift](./Array/TopKFrequentElements.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](./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](./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)| + +## 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](./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| ★★★★| [Perfect Squares](https://leetcode.com/problems/perfect-squares/)| [Swift](./DP/PerfectSquares.swift)| Medium| ★★★★| [Merge Intervals](https://leetcode.com/problems/merge-intervals/)| [Swift](./Sort/MergeIntervals.swift)| Hard| ★★★| [Valid Parentheses](https://leetcode.com/problems/valid-parentheses/)| [Swift](./Stack/ValidParentheses.swift)| Easy| ★★★| +[Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/)| [Swift](./Math/TrappingRainWater.swift)| Hard| ★★| [Merge k Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/)| [Swift](./LinkedList/MergeKSortedLists.swift)| Hard| ★★| +[Longest Consecutive Sequence](https://leetcode.com/problems/longest-consecutive-sequence/)| [Swift](./Array/LongestConsecutiveSequence.swift)| Hard| ★★| [Find Peak Element](https://leetcode.com/problems/find-peak-element/)| [Swift](./Search/FindPeakElement.swift)| Medium| ★★| +[Power of Two](https://leetcode.com/problems/power-of-two/)| [Swift](./Math/PowerTwo.swift)| Easy| ★★| [Spiral Matrix](https://leetcode.com/problems/spiral-matrix/)| [Swift](./Array/SpiralMatrix.swift)| Medium| ★★| [Sliding Window Maximum ](https://leetcode.com/problems/sliding-window-maximum/)| [Swift](./Array/SlidingWindowMaximum.swift)| Hard| ★★| -[Power of Two](https://leetcode.com/problems/power-of-two/)| [Swift](./Math/PowerTwo.swift)| Easy| ★★| +[Pow(x, n)](https://leetcode.com/problems/isomorphic-strings/)| [Swift](./Math/Pow.swift)| Medium| ★★| +[Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)| [Swift](./DFS/LetterCombinationsPhoneNumber.swift)| Medium| ★★| +[Heaters](https://leetcode.com/problems/heaters/)| [Swift](./Array/Heaters.swift)| Easy| ★| ## Facebook | Title | Solution | Difficulty | Frequency | | ----- | -------- | ---------- | --------- | [3Sum](https://leetcode.com/problems/3sum/)| [Swift](./Array/ThreeSum.swift)| Medium| ★★★★★★| +[Valid Palindrome](https://leetcode.com/problems/valid-palindrome/)| [Swift](./String/ValidPalindrome.swift)| Easy| ★★★★★★| +[Valid Palindrome II](https://leetcode.com/problems/valid-palindrome-ii/)| [Swift](./String/ValidPalindromeII.swift)| Easy| ★★★★★★| [Move Zeroes](https://leetcode.com/problems/move-zeroes/)| [Swift](./Array/MoveZeroes.swift)| Easy| ★★★★★★| +[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| ★★★★★| +[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| ★★★| [Merge Intervals](https://leetcode.com/problems/merge-intervals/)| [Swift](./Sort/MergeIntervals.swift)| Hard| ★★★| +[Number of Islands](https://leetcode.com/problems/number-of-islands/)| [Swift](./DFS/NumberofIslands.swift)| Medium| ★★★| +[Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/)| [Swift](./LinkedList/ReverseLinkedList.swift)| Easy| ★★★| +[Expression Add Operators](https://leetcode.com/problems/expression-add-operators/)| [Swift](./DFS/ExpressionAddOperators.swift)| Hard| ★★★| [Subsets](https://leetcode.com/problems/subsets/)| [Swift](./DFS/Subsets.swift)| Medium| ★★★| +[Sort Colors](https://leetcode.com/problems/sort-colors/)| [Swift](./Sort/SortColors.swift)| Medium| ★★| + +## Snapchat +| Title | Solution | Difficulty | Frequency | +| ----- | -------- | ---------- | --------- | +[Game of Life](https://leetcode.com/problems/game-of-life/) | [Swift](./Array/GameLife.swift)| Medium| ★★★★★★| +[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](./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](./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| ★| + +## Uber +| Title | Solution | Difficulty | Frequency | +| ----- | -------- | ---------- | --------- | +[Valid Sudoku](https://leetcode.com/problems/valid-sudoku/)| [Swift](./Array/ValidSudoku.swift)| Easy| ★★★★| +[Spiral Matrix](https://leetcode.com/problems/spiral-matrix/)| [Swift](./Array/SpiralMatrix.swift)| Medium| ★★★★| +[Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)| [Swift](./DFS/LetterCombinationsPhoneNumber.swift)| Medium| ★★★★| +[Group Anagrams](https://leetcode.com/problems/anagrams/)| [Swift](./String/GroupAnagrams.swift)| Medium| ★★★★| +[Word Pattern](https://leetcode.com/problems/word-pattern/)| [Swift](./String/WordPattern.swift)| Easy| ★★★| +[Roman to Integer](https://leetcode.com/problems/roman-to-integer/)| [Swift](./Math/RomanToInteger.swift)| Easy| ★★★| +[Combination Sum](https://leetcode.com/problems/combination-sum/)| [Swift](./DFS/CombinationSum.swift)| Medium| ★★| + +## Airbnb +| Title | Solution | Difficulty | Frequency | +| ----- | -------- | ---------- | --------- | +[Two Sum](https://leetcode.com/problems/two-sum/)| [Swift](./Array/TwoSum.swift)| Easy| ★★★★★| +[Text Justification](https://leetcode.com/problems/text-justification/)| [Swift](./String/TextJustification.swift)| Hard| ★★★★| +[House Robber](https://leetcode.com/problems/house-robber/)| [Swift](./DP/HouseRobber.swift)| Easy| ★★| +[Single Number](https://leetcode.com/problems/single-number/)| [Swift](./Math/SingleNumber.swift)| Medium| ★★| +[Word Search II](https://leetcode.com/problems/word-search-ii/)| [Swift](./DFS/WordSearchII.swift)| Hard| ★★| +[Add Two Numbers](https://leetcode.com/problems/add-two-numbers/)| [Swift](./Math/AddTwoNumbers.swift)| Medium| ★★| ## LinkedIn | Title | Solution | Difficulty | Frequency | @@ -219,12 +502,32 @@ [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| ★★★| [Maximum Product Subarray](https://leetcode.com/problems/maximum-product-subarray/)| [Swift](./DP/MaximumProductSubarray.swift)| Medium| ★★★| [Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/)| [Swift](./Array/ProductExceptSelf.swift)| Medium| ★★★| +[Symmetric Tree](https://leetcode.com/problems/symmetric-tree/)| [Swift](./Tree/SymmetricTree.swift)| Easy| ★★| + +## Amazon +| Title | Solution | Difficulty | Frequency | +| ----- | -------- | ---------- | --------- | +[Two Sum](https://leetcode.com/problems/two-sum/)| [Swift](./Array/TwoSum.swift)| Easy| ★★★★★★| +[Min Cost Climbing Stairs](https://leetcode.com/problems/min-cost-climbing-stairs/)| [Swift](./DP/MinCostClimbingStairs.swift)| Easy| ★★★★| +[Number of Islands](https://leetcode.com/problems/number-of-islands/)| [Swift](./DFS/NumberofIslands.swift)| Medium| ★★| +[Add Two Numbers](https://leetcode.com/problems/add-two-numbers/)| [Swift](./Math/AddTwoNumbers.swift)| Medium| ★★| +[Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/)| [Swift](./LinkedList/ReverseLinkedList.swift)| Easy| ★★| +[Valid Parentheses](https://leetcode.com/problems/valid-parentheses/)| [Swift](./Stack/ValidParentheses.swift)| Easy| ★★| +[Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/)| [Swift](./DP/LongestPalindromicSubstring.swift)| Medium| ★★| +[Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/)| [Swift](./Math/TrappingRainWater.swift)| Hard| ★★| +[Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/)| [Swift](./String/LongestSubstringWithoutRepeatingCharacters.swift)| Medium| ★★| +[Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)| [Swift](./DFS/LetterCombinationsPhoneNumber.swift)| Medium| ★★| +[Valid Anagram](https://leetcode.com/problems/valid-anagram/)| [Swift](./String/ValidAnagram.swift)| Easy| ★★| +[Rotate Image](https://leetcode.com/problems/rotate-image/)| [Swift](./Array/RotateImage.swift)| Medium| ★★| +[Best Time to Buy and Sell Stock](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/)| [Swift](./DP/BestTimeBuySellStock.swift)| Easy| ★★| +[3Sum](https://leetcode.com/problems/3sum/)| [Swift](./Array/ThreeSum.swift)| Medium| ★★| +[Sliding Window Maximum ](https://leetcode.com/problems/sliding-window-maximum/)| [Swift](./Array/SlidingWindowMaximum.swift)| Hard| ★★| ## Microsoft | Title | Solution | Difficulty | Frequency | @@ -236,3 +539,371 @@ [Excel Sheet Column Number](https://leetcode.com/problems/excel-sheet-column-number/)| [Swift](./Math/ExcelSheetColumnNumber.swift)| Easy| ★★★★| [Validate Binary Search Tree](https://leetcode.com/problems/validate-binary-search-tree/)| [Swift](./Tree/ValidateBinarySearchTree.swift)| Medium| ★★★| [Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/)| [Swift](./LinkedList/MergeTwoSortedLists.swift)| Easy| ★★★| + + + +## 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 +| | 374 | [Guess Number Higher or Lower](https://leetcode.com/problems/guess-number-higher-or-lower/) | Easy +| | 373 | [Find K Pairs with Smallest Sums](https://leetcode.com/problems/find-k-pairs-with-smallest-sums/) | Medium +| [Swift](./Math/SuperPow.swift) | 372 | [Super Pow](https://leetcode.com/problems/super-pow/) | Medium +| [Swift](./Math/SumTwoIntegers.swift) | 371 | [Sum of Two Integers](https://leetcode.com/problems/sum-of-two-integers/) | Easy +| | 370 | [Range Addition](https://leetcode.com/problems/range-addition/) ♥ | Medium +| | 369 | [Plus One Linked List](https://leetcode.com/problems/plus-one-linked-list/) ♥ | Medium +| | 368 | [Largest Divisible Subset](https://leetcode.com/problems/largest-divisible-subset/) | Medium +| | 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 +| [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 +| [Swift](./Sort/SortTransformedArray.swift) | 360 | [Sort Transformed Array](https://leetcode.com/problems/sort-transformed-array/) ♥ | Medium +| | 359 | [Logger Rate Limiter](https://leetcode.com/problems/logger-rate-limiter/) ♥ | Easy +| | 358 | [Rearrange String k Distance Apart](https://leetcode.com/problems/rearrange-string-k-distance-apart/) ♥ | Hard +| | 357 | [Count Numbers with Unique Digits](https://leetcode.com/problems/count-numbers-with-unique-digits/) | Medium +| | 356 | [Line Reflection](https://leetcode.com/problems/line-reflection/) ♥ | Medium +| | 355 | [Design Twitter](https://leetcode.com/problems/design-twitter/) | Medium +| | 354 | [Russian Doll Envelopes](https://leetcode.com/problems/russian-doll-envelopes/) | Hard +| | 353 | [Design Snake Game](https://leetcode.com/problems/design-snake-game/) ♥ | Medium +| | 352 | [Data Stream as Disjoint Intervals](https://leetcode.com/problems/data-stream-as-disjoint-intervals/) | Hard +| | 351 | [Android Unlock Patterns](https://leetcode.com/problems/android-unlock-patterns/) ♥ | Medium +| [Swift](./Array/IntersectionTwoArraysII.swift) | 350 | [Intersection of Two Arrays II](https://leetcode.com/problems/intersection-of-two-arrays-ii/) | Easy +| [Swift](./Array/IntersectionTwoArrays.swift) | 349 | [Intersection of Two Arrays](https://leetcode.com/problems/intersection-of-two-arrays/) | Easy +| | 348 | [Design Tic-Tac-Toe](https://leetcode.com/problems/design-tic-tac-toe/) ♥ | Medium +| [Swift](./Sort/TopKFrequentElements.swift) | 347 | [Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/) | Medium +| | 346 | [Moving Average from Data Stream](https://leetcode.com/problems/moving-average-from-data-stream/) ♥ | Easy +| [Swift](./String/ReverseVowelsOfAString.swift) | 345 | [Reverse Vowels of a String](https://leetcode.com/problems/reverse-vowels-of-a-string/) | Easy +| [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 +| [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 +| [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 +| | 330 | [Patching Array](https://leetcode.com/problems/patching-array/) | Hard +| | 329 | [Longest Increasing Path in a Matrix](https://leetcode.com/problems/longest-increasing-path-in-a-matrix/) | Hard +| [Swift](./LinkedList/OddEvenLinkedList.swift) | 328 | [Odd Even Linked List](https://leetcode.com/problems/odd-even-linked-list/) | Medium +| | 327 | [Count of Range Sum](https://leetcode.com/problems/count-of-range-sum/) | Hard +| [Swift](./Math/PowerThree.swift) | 326 | [Power of Three](https://leetcode.com/problems/power-of-three/) | Easy +| [Swift](./Array/MaximumSizeSubarraySumEqualsK.swift) | 325 | [Maximum Size Subarray Sum Equals k](https://leetcode.com/problems/maximum-size-subarray-sum-equals-k/) ♥ | Medium +| [Swift](./Sort/WiggleSortII.swift) | 324 | [Wiggle Sort II](https://leetcode.com/problems/wiggle-sort-ii/) | Medium +| [Swift](./Sort/NumberConnectedComponentsUndirectedGraph.swift) | 323 | [Number of Connected Components in an Undirected Graph](https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/) ♥ | Medium +| [Swift](./DP/CoinChange.swift) | 322 | [Coin Change](https://leetcode.com/problems/coin-change/) | Medium +| [Swift](./Array/CreateMaximumNumber.swift) | 321 | [Create Maximum Number](https://leetcode.com/problems/create-maximum-number/) | Hard +| [Swift](./DFS/GeneralizedAbbreviation.swift) | 320 | [Generalized Abbreviation](https://leetcode.com/problems/generalized-abbreviation/) ♥ | Medium +| | 319 | [Bulb Switcher](https://leetcode.com/problems/bulb-switcher/) | Medium +| | 318 | [Maximum Product of Word Lengths](https://leetcode.com/problems/maximum-product-of-word-lengths/) | Medium +| [Swift](./BFS/ShortestDistanceAllBuildings.swift) | 317 | [Shortest Distance from All Buildings](https://leetcode.com/problems/shortest-distance-from-all-buildings/) ♥ | Hard +| | 316 | [Remove Duplicate Letters](https://leetcode.com/problems/remove-duplicate-letters/) | Hard +| | 315 | [Count of Smaller Numbers After Self](https://leetcode.com/problems/count-of-smaller-numbers-after-self/) | Hard +| [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 +| [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](./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 +| [Swift](./DFS/RemoveInvalidParentheses.swift) | 301 | [Remove Invalid Parentheses](https://leetcode.com/problems/remove-invalid-parentheses/) | Hard +| [Swift](./DP/LongestIncreasingSubsequence.swift) | 300 | [Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/) | Medium +| | 299 | [Bulls and Cows](https://leetcode.com/problems/bulls-and-cows/) | Easy | +| | 298 | [Binary Tree Longest Consecutive Sequence](https://leetcode.com/problems/binary-tree-longest-consecutive-sequence/) ♥ | Medium | +| | 297 | [Serialize and Deserialize Binary Tree](https://leetcode.com/problems/serialize-and-deserialize-binary-tree/) | Hard | +| [Swift](./Tree/UniqueBinarySearchTrees.swift) | 296 | [Best Meeting Point](https://leetcode.com/problems/best-meeting-point/) ♥ | Hard | +| | 295 | [Find Median from Data Stream](https://leetcode.com/problems/find-median-from-data-stream/) | Hard | +| [Swift](./DP/FlipGameII.swift) | 294 | [Flip Game II](https://leetcode.com/problems/flip-game-ii/) ♥ | Medium | +| [Swift](./String/FlipGame.swift) | 293 | [Flip Game](https://leetcode.com/problems/flip-game/) ♥ | Easy | +| | 292 | [Nim Game](https://leetcode.com/problems/nim-game/) | Easy | +| | 291 | [Word Pattern II](https://leetcode.com/problems/word-pattern-ii/) ♥ | Hard | +| [Swift](./String/WordPattern.swift) | 290 | [Word Pattern](https://leetcode.com/problems/word-pattern/) | Easy | +| [Swift](./Array/GameLife.swift) | 289 | [Game of Life](https://leetcode.com/problems/game-of-life/) | Medium | +| | 288 | [Unique Word Abbreviation](https://leetcode.com/problems/unique-word-abbreviation/) ♥ | Easy | +| | 287 | [Find the Duplicate Number](https://leetcode.com/problems/find-the-duplicate-number/) | Hard | +| [Swift](./DFS/NumberofIslands.swift) | 286 | [Walls and Gates](https://leetcode.com/problems/walls-and-gates/) ♥ | Medium | +| | 285 | [Inorder Successor in BST](https://leetcode.com/problems/inorder-successor-in-bst/) ♥ | Medium | +| | 284 | [Peeking Iterator](https://leetcode.com/problems/peeking-iterator/) | Medium | +| [Swift](./Array/MoveZeroes.swift) | 283 | [Move Zeroes](https://leetcode.com/problems/move-zeroes/) | Easy | +| [Swift](./DFS/ExpressionAddOperators.swift) | 282 | [Expression Add Operators](https://leetcode.com/problems/expression-add-operators/) | Hard | +| | 281 | [Zigzag Iterator](https://leetcode.com/problems/zigzag-iterator/) ♥ | Medium | +| [Swift](./Sort/WiggleSort.swift) | 280 | [Wiggle Sort](https://leetcode.com/problems/wiggle-sort/) ♥ | Medium | +| [Swift](./DP/PerfectSquares.swift) | 279 | [Perfect Squares](https://leetcode.com/problems/perfect-squares/) | Medium | +| | 278 | [First Bad Version](https://leetcode.com/problems/first-bad-version/) | Easy | +| | 277 | [Find the Celebrity](https://leetcode.com/problems/find-the-celebrity/) ♥ | Medium | +| [Swift](./DP/PaintFence.swift) | 276 | [Paint Fence](https://leetcode.com/problems/paint-fence/) ♥ | Easy | +| | 275 | [H-Index II](https://leetcode.com/problems/h-index-ii/) | Medium | +| | 274 | [H-Index](https://leetcode.com/problems/h-index/) | Medium | +| [Swift](./Math/IntegerEnglishWords.swift) | 273 | [Integer to English Words](https://leetcode.com/problems/integer-to-english-words/) | Hard | +| [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](./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 | +| [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/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 | +| [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 | +| [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 | +| [Swift](./Search/Search2DMatrixII.swift) | 240 | [Search a 2D Matrix II](https://leetcode.com/problems/search-a-2d-matrix-ii/) | Medium | +| [Swift](./Array/SlidingWindowMaximum.swift) | 239 | [Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) | Hard | +| [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 | +| [Swift](./Tree/LowestCommonAncestorBinarySearchTree.swift) | 235 | [Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/) | Easy | +| [Swift](./LinkedList/PalindromeLinkedList.swift) | 234 | [Palindrome Linked List](https://leetcode.com/problems/palindrome-linked-list/) | Easy | +| | 233 | [Number of Digit One](https://leetcode.com/problems/number-of-digit-one/) | Hard | +| [Swift](./Queue/ImplementQueueUsingStacks.swift) | 232 | [Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks/) | Easy | +| [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 | +| [Swift](./Array/SummaryRanges.swift) | 228 | [Summary Ranges](https://leetcode.com/problems/summary-ranges/) | Medium | +| | 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 | +| [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 | +| | 220 | [Contains Duplicate III](https://leetcode.com/problems/contains-duplicate-iii/) | Medium | +| [Swift](./Array/ContainsDuplicateII.swift) | 219 | [Contains Duplicate II](https://leetcode.com/problems/contains-duplicate-ii/) | Easy | +| | 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 | +| [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 | +| [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 | +| [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 | +| [Swift](./LinkedList/RemoveLinkedListElements.swift) | 203 | [Remove Linked List Elements](https://leetcode.com/problems/remove-linked-list-elements/) | Easy | +| [Swift](./Math/HappyNumber.swift) | 202 | [Happy Number](https://leetcode.com/problems/happy-number/) | Easy | +| | 201 | [Bitwise AND of Numbers Range](https://leetcode.com/problems/bitwise-and-of-numbers-range/) | Medium | +| [Swift](./DFS/NumberofIslands.swift) | 200 | [Number of Islands](https://leetcode.com/problems/number-of-islands/) | Medium | +| [Swift](./Tree/BinaryTreeRightSideView.swift) | 199 | [Binary Tree Right Side View](https://leetcode.com/problems/binary-tree-right-side-view/) | Medium | +| [Swift](./DP/HouseRobber.swift) | 198 | [House Robber](https://leetcode.com/problems/house-robber/) | Easy | +| | 191 | [Number of 1 Bits](https://oj.leetcode.com/problems/number-of-1-bits/) | Easy | +| | 190 | [Reverse Bits](https://oj.leetcode.com/problems/reverse-bits/) | Easy | +| [Swift](./Array/RotateArray.swift) | 189 | [Rotate Array](https://oj.leetcode.com/problems/rotate-array/) | Easy | +| [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 | +| [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 | +| [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 | +| [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 | +| [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 | +| [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 | +| [Swift](./String/LongestSubstringMostTwoDistinctCharacters.swift) | 159 | [Longest Substring with At Most Two Distinct Characters](https://oj.leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/) ♥ | Hard | +| | 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 | +| [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 | +| [Swift](./String/ReverseWordsString.swift) | 151 | [Reverse Words in a String](https://oj.leetcode.com/problems/reverse-words-in-a-string/) | Medium | +| [Swift](./Stack/EvaluateReversePolishNotation.swift) | 150 | [Evaluate Reverse Polish Notation](https://oj.leetcode.com/problems/evaluate-reverse-polish-notation/) | Medium | +| | 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 | +| [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 | +| | 142 | [Linked List Cycle II](https://oj.leetcode.com/problems/linked-list-cycle-ii/) | Medium | +| | 141 | [Linked List Cycle](https://oj.leetcode.com/problems/linked-list-cycle/) | Easy | +| | 140 | [Word Break II](https://oj.leetcode.com/problems/word-break-ii/) | Hard | +| | 139 | [Word Break](https://oj.leetcode.com/problems/word-break/) | Medium | +| | 138 | [Copy List with Random Pointer](https://oj.leetcode.com/problems/copy-list-with-random-pointer/) | Hard | +| [Swift](./Math/SingleNumberII.swift) | 137 | [Single Number II](https://oj.leetcode.com/problems/single-number-ii/) | Medium | +| [Swift](./Math/SingleNumber.swift) | 136 | [Single Number](https://oj.leetcode.com/problems/single-number/) | Medium | +| | 135 | [Candy](https://oj.leetcode.com/problems/candy/) | Hard | +| [Swift](./Array/GasStation.swift) | 134 | [Gas Station](https://oj.leetcode.com/problems/gas-station/) | Medium | +| | 133 | [Clone Graph](https://oj.leetcode.com/problems/clone-graph/) | Medium | +| | 132 | [Palindrome Partitioning II](https://oj.leetcode.com/problems/palindrome-partitioning-ii/) | Hard | +| [Swift](./DFS/PalindromePartitioning.swift) | 131 | [Palindrome Partitioning](https://oj.leetcode.com/problems/palindrome-partitioning/) | Medium | +| | 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 | +| [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 | +| [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 | +| [Swift](./DP/Triangle.swift) | 120 | [Triangle](https://oj.leetcode.com/problems/triangle/) | Medium | +| | 119 | [Pascal's Triangle II](https://oj.leetcode.com/problems/pascals-triangle-ii/) | Easy | +| | 118 | [Pascal's Triangle](https://oj.leetcode.com/problems/pascals-triangle/) | Easy | +| | 117 | [Populating Next Right Pointers in Each Node II](https://oj.leetcode.com/problems/populating-next-right-pointers-in-each-node-ii/) | Hard | +| | 116 | [Populating Next Right Pointers in Each Node](https://oj.leetcode.com/problems/populating-next-right-pointers-in-each-node/) | Medium | +| | 115 | [Distinct Subsequences](https://oj.leetcode.com/problems/distinct-subsequences/) | Hard | +| [Swift](./Tree/FlattenBinaryTreeLinkedList.swift) | 114 | [Flatten Binary Tree to Linked List](https://oj.leetcode.com/problems/flatten-binary-tree-to-linked-list/) | Medium | +| [Swift](./Tree/PathSumII.swift) | 113 | [Path Sum II](https://oj.leetcode.com/problems/path-sum-ii/) | Medium | +| [Swift](./Tree/PathSum.swift) | 112 | [Path Sum](https://oj.leetcode.com/problems/path-sum/) | Easy | +| [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 | +| [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 | +| [Swift](./Tree/MaximumDepthOfBinaryTree.swift) | 104 | [Maximum Depth of Binary Tree](https://oj.leetcode.com/problems/maximum-depth-of-binary-tree/) | Easy | +| [Swift](./Tree/BinaryTreeZigzagLevelOrderTraversal.swift) | 103 | [Binary Tree Zigzag Level Order Traversal](https://oj.leetcode.com/problems/binary-tree-zigzag-level-order-traversal/) | Medium | +| [Swift](./Tree/BinaryTreeLevelOrderTraversal.swift) | 102 | [Binary Tree Level Order Traversal](https://oj.leetcode.com/problems/binary-tree-level-order-traversal/) | Easy | +| [Swift](./Tree/SymmetricTree.swift) | 101 | [Symmetric Tree](https://oj.leetcode.com/problems/symmetric-tree/) | Easy | +| [Swift](./Tree/SameTree.swift) | 100 | [Same Tree](https://oj.leetcode.com/problems/same-tree/) | Easy | +| [Swift](./Tree/RecoverBinarySearchTree.swift) | 99 | [Recover Binary Search Tree](https://oj.leetcode.com/problems/recover-binary-search-tree/) | Hard | +| [Swift](./Tree/ValidateBinarySearchTree.swift) | 98 | [Validate Binary Search Tree](https://oj.leetcode.com/problems/validate-binary-search-tree/) | Medium | +| | 97 | [Interleaving String](https://oj.leetcode.com/problems/interleaving-string/) | Hard | +| [Swift](./Tree/UniqueBinarySearchTrees.swift) | 96 | [Unique Binary Search Trees](https://oj.leetcode.com/problems/unique-binary-search-trees/) | Medium | +| | 95 | [Unique Binary Search Trees II](https://oj.leetcode.com/problems/unique-binary-search-trees-ii/) | Medium | +| [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 | +| [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 | +| [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 | +| | 85 | [Maximal Rectangle](https://oj.leetcode.com/problems/maximal-rectangle/) | Hard | +| | 84 | [Largest Rectangle in Histogram](https://oj.leetcode.com/problems/largest-rectangle-in-histogram/) | Hard | +| [Swift](./LinkedList/RemoveDuplicatesFromSortedList.swift) | 83 | [Remove Duplicates from Sorted List](https://oj.leetcode.com/problems/remove-duplicates-from-sorted-list/) | Easy | +| [Swift](./LinkedList/RemoveDuplicatesFromSortedListII.swift) | 82 | [Remove Duplicates from Sorted List II](https://oj.leetcode.com/problems/remove-duplicates-from-sorted-list-ii/) | Medium | +| [Swift](./Search/SearchInRotatedSortedArrayII.swift) | 81 | [Search in Rotated Sorted Array II](https://oj.leetcode.com/problems/search-in-rotated-sorted-array-ii/) | Medium | +| [Swift](./Array/RemoveDuplicatesFromSortedArrayII.swift) | 80 | [Remove Duplicates from Sorted Array II](https://oj.leetcode.com/problems/remove-duplicates-from-sorted-array-ii/) | Medium | +| [Swift](./DFS/WordSearch.swift) | 79 | [Word Search](https://oj.leetcode.com/problems/word-search/) | Medium | +| [Swfit](./DFS/Subsets.swift) | 78 | [Subsets](https://oj.leetcode.com/problems/subsets/) | Medium | +| [Swift](./DFS/combinations.swift) | 77 | [Combinations](https://oj.leetcode.com/problems/combinations/) | Medium | +| [Swift](./Array/MinimumWindowSubstring.swift) | 76 | [Minimum Window Substring](https://oj.leetcode.com/problems/minimum-window-substring/) | Hard | +| [Swift](./Sort/SortColors.swift) | 75 | [Sort Colors](https://oj.leetcode.com/problems/sort-colors/) | Medium | +| [Swift](./Search/Search2DMatrix.swift) | 74 | [Search a 2D Matrix](https://oj.leetcode.com/problems/search-a-2d-matrix/) | Medium | +| [Swift](./Array/SetMatrixZero.swift) | 73 | [Set Matrix Zeroes](https://oj.leetcode.com/problems/set-matrix-zeroes/) | Medium | +| [Swift](./DP/EditDistance.swift) | 72 | [Edit Distance](https://oj.leetcode.com/problems/edit-distance/) | Hard | +| [Swift](./Stack/SimplifyPath.swift) | 71 | [Simplify Path](https://oj.leetcode.com/problems/simplify-path/) | Medium | +| [Swift](./DP/ClimbingStairs.swift) | 70 | [Climbing Stairs](https://oj.leetcode.com/problems/climbing-stairs/) | Easy | +| [Swift](./Search/Sqrtx.swift) | 69 | [Sqrt(x)](https://oj.leetcode.com/problems/sqrtx/) | Medium | +| [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 | +| [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 | +| [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 | +| [Swift](./Sort/MergeIntervals.swift) | 56 | [Merge Intervals](https://oj.leetcode.com/problems/merge-intervals/) | Hard | +| [Swift](./DP/JumpGame.swift) | 55 | [Jump Game](https://oj.leetcode.com/problems/jump-game/) | Medium | +| [Swift](./Array/SpiralMatrix.swift) | 54 | [Spiral Matrix](https://oj.leetcode.com/problems/spiral-matrix/) | Medium | +| [Swift](./DP/MaximumSubarray.swift) | 53 | [Maximum Subarray](https://oj.leetcode.com/problems/maximum-subarray/) | Medium | +| [Swift](./DFS/NQueensII.swift) | 52 | [N-Queens II](https://oj.leetcode.com/problems/n-queens-ii/) | Hard | +| [Swift](./DFS/NQueens.swift) | 51 | [N-Queens](https://oj.leetcode.com/problems/n-queens/) | Hard | +| [Swift](./Math/Pow.swift) | 50 | ["Pow(x, n)"](https://oj.leetcode.com/problems/powx-n/) | Medium | +| [Swift](./String/GroupAnagrams.swift) | 49 | [Group Anagrams](https://oj.leetcode.com/problems/anagrams/) | Medium | +| [Swift](./Array/RotateImage.swift) | 48 | [Rotate Image](https://oj.leetcode.com/problems/rotate-image/) | Medium | +| [Swift](./DFS/PermutationsII.swift) | 47 | [Permutations II](https://oj.leetcode.com/problems/permutations-ii/) | Medium | +| [Swift](./DFS/Permutations.swift) | 46 | [Permutations](https://oj.leetcode.com/problems/permutations/) | Medium | +| | 45 | [Jump Game II](https://oj.leetcode.com/problems/jump-game-ii/) | Hard | +| [Swift](./DP/WildcardMatching.swift) | 44 | [Wildcard Matching](https://oj.leetcode.com/problems/wildcard-matching/) | Hard | +| [Swift](./String/MultiplyStrings.swift) | 43 | [Multiply Strings](https://oj.leetcode.com/problems/multiply-strings/) | Medium | +| [Swift](./Math/TrappingRainWater.swift) | 42 | [Trapping Rain Water](https://oj.leetcode.com/problems/trapping-rain-water/) | Hard | +| [Swift](./Array/FirstMissingPositive.swift) | 41 | [First Missing Positive](https://oj.leetcode.com/problems/first-missing-positive/) | Hard | +| [Swift](./DFS/combinationSumII.swiftc) | 40 | [Combination Sum II](https://oj.leetcode.com/problems/combination-sum-ii/) | Medium | +| [Swift](./DFS/CombinationSum.swift) | 39 | [Combination Sum](https://oj.leetcode.com/problems/combination-sum/) | Medium | +| [Swift](./String/CountAndSay.swift) | 38 | [Count and Say](https://oj.leetcode.com/problems/count-and-say/) | Easy | +| [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://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 | +| | 30 | [Substring with Concatenation of All Words](https://oj.leetcode.com/problems/substring-with-concatenation-of-all-words/) | Hard | +| [Swift](./Math/DivideTwoIntegers.swift) | 29 | [Divide Two Integers](https://oj.leetcode.com/problems/divide-two-integers/) | Medium | +| [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 | +| [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 | +| [Swift](./LinkedList/MergeTwoSortedLists.swift) | 21 | [Merge Two Sorted Lists](https://oj.leetcode.com/problems/merge-two-sorted-lists/) | Easy | +| [Swift](./Stack/ValidParentheses.swift) | 20 | [Valid Parentheses](https://oj.leetcode.com/problems/valid-parentheses/) | Easy | +| [Swift](./LinkedList/RemoveNthFromEnd.swift) | 19 | [Remove Nth Node From End of List](https://oj.leetcode.com/problems/remove-nth-node-from-end-of-list/) | Easy | +| [Swift](./Array/FourSum.swift) | 18 | [4Sum](https://oj.leetcode.com/problems/4sum/) | Medium | +| [Swift](./DFS/LetterCombinationsPhoneNumber.swift) | 17 | [Letter Combinations of a Phone Number](https://oj.leetcode.com/problems/letter-combinations-of-a-phone-number/) | Medium | +| [Swift](./Array/ThreeSum.swift) | 16 | [3Sum Closest](https://oj.leetcode.com/problems/3sum-closest/) | Medium | +| [Swift](./Array/ThreeSum.swift) | 15 | [3Sum](https://oj.leetcode.com/problems/3sum/) | Medium | +| [Swift](./String/LongestCommonPrefix.swift) | 14 | [Longest Common Prefix](https://oj.leetcode.com/problems/longest-common-prefix/) | Easy | +| [Swift](./Math/RomanToInteger.swift) | 13 | [Roman to Integer](https://oj.leetcode.com/problems/roman-to-integer/) | Easy | +| [Swift](./Math/IntegerToRoman.swift) | 12 | [Integer to Roman](https://oj.leetcode.com/problems/integer-to-roman/) | Medium | +| [Swift](./Math/ContainerMostWater.swift) | 11 | [Container With Most Water](https://oj.leetcode.com/problems/container-with-most-water/) | Medium | +| [Swift](./DP/RegularExpressionMatching.swift) | 10 | [Regular Expression Matching](https://oj.leetcode.com/problems/regular-expression-matching/) | Hard | +| [Swift](./Math/PalindromeNumber.swift) | 9 | [Palindrome Number](https://oj.leetcode.com/problems/palindrome-number/) | Easy | +| [Swift](./Math/Atoi.swift) | 8 | [String to Integer (atoi)](https://oj.leetcode.com/problems/string-to-integer-atoi/) | Easy | +| [Swift](./Math/ReverseInteger.swift) | 7 | [Reverse Integer](https://oj.leetcode.com/problems/reverse-integer/) | Easy | +| [Swift](./String/ZigZagConversion.swift) | 6 | [ZigZag Conversion](https://oj.leetcode.com/problems/zigzag-conversion/) | Easy | +| [Swift](./DP/LongestPalindromicSubstring.swift) | 5 | [Longest Palindromic Substring](https://oj.leetcode.com/problems/longest-palindromic-substring/) | Medium | +| [Swift](./Search/MedianOfTwoSortedArrays.swift) | 4 | [Median of Two Sorted Arrays](https://leetcode.com/problems/median-of-two-sorted-arrays/) | Hard | +| [Swift](./String/LongestSubstringWithoutRepeatingCharacters.swift) | 3 | [Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/) | Medium | +| [Swift](./Math/AddTwoNumbers.swift) | 2 | [Add Two Numbers](https://leetcode.com/problems/add-two-numbers/) | Medium | +| [Swift](./Array/TwoSum.swift) | 1 | [Two Sum](https://leetcode.com/problems/two-sum/) | Easy | diff --git a/Search/ClosestBinarySearchTreeValueII.swift b/Search/ClosestBinarySearchTreeValueII.swift new file mode 100644 index 00000000..00e3bdff --- /dev/null +++ b/Search/ClosestBinarySearchTreeValueII.swift @@ -0,0 +1,45 @@ +/** + * Question Link: Question Link: https://leetcode.com/problems/closest-binary-search-tree-value-ii/ + * Primary idea: Inorder traverse, compare current node val with the first one of the + * array, as it is far from closest one as usual. + * 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 ClosestBinarySearchTreeValueII { + func closestKValues(_ root: TreeNode?, _ target: Double, _ k: Int) -> [Int] { + var res = [Int]() + + inorder(root, target, k, &res) + + return res + } + + fileprivate func inorder(_ node: TreeNode?, _ target: Double, _ k: Int, _ res: inout [Int]) { + guard let node = node else { + return + } + + inorder(node.left, target, k, &res) + if res.count < k { + res.append(node.val) + } else if abs(Double(res.first!) - target) > abs(Double(node.val) - target) { + res.removeFirst() + res.append(node.val) + } else { + return + } + inorder(node.right, target, k, &res) + } +} diff --git a/Search/FindMinimumRotatedSortedArray.swift b/Search/FindMinimumRotatedSortedArray.swift index 1ac36637..aa81a8a5 100644 --- a/Search/FindMinimumRotatedSortedArray.swift +++ b/Search/FindMinimumRotatedSortedArray.swift @@ -6,23 +6,22 @@ */ class FindMinimumRotatedSortedArray { - func findMin(nums: [Int]) -> Int { + func findMin(_ nums: [Int]) -> Int { + var minVal = Int.max var left = 0 var right = nums.count - 1 - var mid = 0 - var minVal = Int.max - while left + 1 < right { - mid = (right - left) / 2 + left - if nums[mid] > nums[left] { - minVal = min(nums[left], minVal) + while left <= right { + let mid = (right - left) / 2 + left + if nums[mid] >= nums[left] { + minVal = min(minVal, nums[left]) left = mid + 1 } else { - minVal = min(nums[mid], minVal) + minVal = min(minVal, nums[mid]) right = mid - 1 } } - return min(minVal, nums[left], nums[right]) + return minVal } } \ No newline at end of file diff --git a/Search/FindPeakElement.swift b/Search/FindPeakElement.swift index 2500e904..6eb1df82 100644 --- a/Search/FindPeakElement.swift +++ b/Search/FindPeakElement.swift @@ -6,30 +6,19 @@ */ class FindPeakElement { - func findPeakElement(nums: [Int]) -> Int { - guard nums.count > 1 else { - return 0 - } - - var left = 1 - var right = nums.count - 2 - var mid = 0 + func findPeakElement(_ nums: [Int]) -> Int { + var left = 0, right = nums.count - 1, mid = 0 - while left <= right { + while left < right { mid = (right - left) / 2 + left - if nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1] { - return mid - } else if nums[mid] < nums[mid + 1] { - left = mid + 1 + + if nums[mid] > nums[mid + 1] { + right = mid } else { - right = mid - 1 + left = mid + 1 } } - if nums[left] >= nums[right] { - return left - } else { - return right - } + return left } } \ No newline at end of file diff --git a/Search/MedianOfTwoSortedArrays.swift b/Search/MedianOfTwoSortedArrays.swift new file mode 100644 index 00000000..b1d17ced --- /dev/null +++ b/Search/MedianOfTwoSortedArrays.swift @@ -0,0 +1,65 @@ +/** + * Question Link: https://leetcode.com/problems/median-of-two-sorted-arrays/ + * + * Primary idea: For arrays of m and n numbers, nums1 and nums2, where m <= n. + * To find an index of mid1 in nums1, to separate the arrays into left and right parts: + * nums1[0, 1, ..., mid1 - 1] | nums1[mid1, mid1 + 1, ..., m] + * nums2[0, 1, ..., mid2 - 1] | nums2[mid2, mid2 + 1, ..., n] + * + * Make sure: + * count of left = count of right + * max of left <= min of right + * + * Time Complexity: O(log(n + m)), Space Complexity: O(1) + * + */ + + class Solution { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + let m = nums1.count + let n = nums2.count + + if m > n { + return findMedianSortedArrays(nums2, nums1) + } + + var halfLength: Int = (m + n + 1) >> 1 + var b = 0, e = m + var maxOfLeft = 0 + var minOfRight = 0 + + while b <= e { + let mid1 = (b + e) >> 1 + let mid2 = halfLength - mid1 + + if mid1 > 0 && mid2 < n && nums1[mid1 - 1] > nums2[mid2] { + e = mid1 - 1 + } else if mid2 > 0 && mid1 < m && nums1[mid1] < nums2[mid2 - 1] { + b = mid1 + 1 + } else { + if mid1 == 0 { + maxOfLeft = nums2[mid2 - 1] + } else if mid2 == 0 { + maxOfLeft = nums1[mid1 - 1] + } else { + maxOfLeft = max(nums1[mid1 - 1], nums2[mid2 - 1]) + } + + if (m + n) % 2 == 1 { + return Double(maxOfLeft) + } + + if mid1 == m { + minOfRight = nums2[mid2] + } else if mid2 == n { + minOfRight = nums1[mid1] + } else { + minOfRight = min(nums1[mid1], nums2[mid2]) + } + + break + } + } + return Double(maxOfLeft + minOfRight) / 2.0 + } +} diff --git a/Search/MedianTwoSortedArrays.swift b/Search/MedianTwoSortedArrays.swift new file mode 100644 index 00000000..7531a782 --- /dev/null +++ b/Search/MedianTwoSortedArrays.swift @@ -0,0 +1,40 @@ +/** + * Question Link: https://leetcode.com/problems/median-of-two-sorted-arrays/ + * Primary idea: Search kth value of both arrays and then compare, each time + * remove half of them until index is 0 + * + * Time Complexity: O(log(m + n)), Space Complexity: O(1) + */ + +class MedianTwoSortedArrays { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + let m = nums1.count + let n = nums2.count + + return (findKth(nums1, nums2, (m + n + 1) / 2) + findKth(nums1, nums2, (m + n + 2) / 2)) / 2 + } + + private func findKth(_ nums1: [Int], _ nums2: [Int], _ index: Int) -> Double { + let m = nums1.count + let n = nums2.count + + guard m <= n else { + return findKth(nums2, nums1, index) + } + guard m != 0 else { + return Double(nums2[index - 1]) + } + guard index != 1 else { + return Double(min(nums1[0], nums2[0])) + } + + let i = min(index / 2, m) + let j = min(index / 2, n) + + if nums1[i - 1] < nums2[j - 1] { + return findKth(Array(nums1[i.. Double { + var left = Double(0), right = Double(stations.last! - stations.first!) + + while right - left >= pow(10, -6) { + let mid = left + (right - left) / 2 + var count = 0 + + for i in 0.. K { + left = mid + } else { + right = mid + } + } + + return right + } +} \ No newline at end of file diff --git a/Search/PeakIndexMountainArray.swift b/Search/PeakIndexMountainArray.swift new file mode 100644 index 00000000..ef02ebc4 --- /dev/null +++ b/Search/PeakIndexMountainArray.swift @@ -0,0 +1,25 @@ +/** + * Question Link: https://leetcode.com/problems/peak-index-in-a-mountain-array/ + * Primary idea: Classic Binary Search + * + * Time Complexity: O(logn), Space Complexity: O(1) + */ + + +class PeakIndexMountainArray { + func peakIndexInMountainArray(_ A: [Int]) -> Int { + var left = 0, right = A.count - 1, mid = 0 + + while left < right { + mid = (right - left) / 2 + left + + if A[mid] > A[mid + 1] { + right = mid + } else { + left = mid + 1 + } + } + + return left + } +} \ 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/Search2DMatrix.swift b/Search/Search2DMatrix.swift new file mode 100644 index 00000000..0a08e2f4 --- /dev/null +++ b/Search/Search2DMatrix.swift @@ -0,0 +1,51 @@ +/** + * Question Link: https://leetcode.com/problems/search-a-2d-matrix/ + * Primary idea: Search col and then binary search row + * + * Time Complexity: O(log(m + n)), Space Complexity: O(1) + */ + +class Search2DMatrix { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + if matrix.count == 0 || matrix[0].count == 0 { + return false + } + + let rowNum = searchRow(matrix, target) + return searchCol(matrix[rowNum], target) + } + + private func searchRow(_ matrix: [[Int]], _ target: Int) -> Int { + var left = 0, right = matrix.count - 1 + + while left + 1 < right { + let mid = (right - left) / 2 + left + if matrix[mid][0] == target { + return mid + } else if matrix[mid][0] < target { + left = mid + } else { + right = mid + } + } + + return matrix[right][0] <= target ? right : left + } + + private func searchCol(_ nums: [Int], _ target: Int) -> Bool { + var left = 0, right = nums.count - 1 + + while left <= right { + let mid = (right - left) / 2 + left + if nums[mid] == target { + return true + } else if nums[mid] < target { + left = mid + 1 + } else { + right = mid - 1 + } + } + + return false + } +} diff --git a/Search/Search2DMatrixII.swift b/Search/Search2DMatrixII.swift new file mode 100644 index 00000000..6844aa8a --- /dev/null +++ b/Search/Search2DMatrixII.swift @@ -0,0 +1,28 @@ +/** + * Question Link: https://leetcode.com/problems/search-a-2d-matrix-ii/ + * Primary idea: Start from last element at first row, then move downwards or backwards + * + * Time Complexity: O(m + n), Space Complexity: O(1) + */ + +class Search2DMatrixII { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + guard matrix.count > 0 else { + return false + } + + var row = 0, col = matrix[0].count - 1 + + while row < matrix.count && col >= 0 { + if matrix[row][col] == target { + return true + } else if matrix[row][col] < target { + row += 1 + } else { + col -= 1 + } + } + + return false + } +} \ 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/SlidingWindow/MinimumWindowSubstring.swift b/SlidingWindow/MinimumWindowSubstring.swift new file mode 100644 index 00000000..6320cecc --- /dev/null +++ b/SlidingWindow/MinimumWindowSubstring.swift @@ -0,0 +1,51 @@ +/** + * Question Link: https://leetcode.com/problems/minimum-window-substring/ + * Primary idea: Use dictionary and int to verify if contain all characters, and update + * startIndex and miniWindow as well + * + * Time Complexity: O(n^2), Space Complexity: O(n) + * + */ + +class MinimumWindowSubstring { + + func minWindow(_ s: String, _ t: String) -> String { + let sChars = Array(s), tChars = Array(t) + var tFreq = Dictionary(tChars.map { ($0, 1) }, uniquingKeysWith: +) + var left = 0, shortestLen = Int.max, start = 0, matters = 0 + + for (i, sChar) in sChars.enumerated() { + guard let sCharFreq = tFreq[sChar] else { + continue + } + + tFreq[sChar] = sCharFreq - 1 + + if sCharFreq > 0 { + matters += 1 + } + + while matters == tChars.count { + guard let leftFreq = tFreq[sChars[left]] else { + left += 1 + continue + } + + if leftFreq == 0 { + matters -= 1 + + if i - left + 1 < shortestLen { + shortestLen = i - left + 1 + start = left + } + + } + + tFreq[sChars[left]] = leftFreq + 1 + left += 1 + } + } + + return shortestLen == Int.max ? "" : String(sChars[start.. [Int] { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { var maxIdx = [Int]() var res = [Int]() - for i in 0 ..< nums.count { + for i in 0.. 0 && nums[maxIdx.last!] < nums[i] { maxIdx.removeLast() } maxIdx.append(i) - if i + 1 >= k { + if i >= k - 1 { + if maxIdx.first! + k == i { + maxIdx.removeFirst() + } + res.append(nums[maxIdx.first!]) } - if i - maxIdx.first! + 1 == k { - maxIdx.removeFirst() - } } 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/ArrayPartitionI.swift b/Sort/ArrayPartitionI.swift new file mode 100644 index 00000000..890c841f --- /dev/null +++ b/Sort/ArrayPartitionI.swift @@ -0,0 +1,21 @@ +class ArrayPartitionI { + func arrayPairSum(_ nums: [Int]) -> Int { + var arr = nums + arr = arr.sorted() + var res = 0 + for i in 0.. Int { + return nums.sorted(by: <).enumerated() + .flatMap { $0 % 2 == 0 ? $1 : nil } + .reduce(0) { $0 + $1 } + } +} diff --git a/Sort/EmployeeFreeTime.swift b/Sort/EmployeeFreeTime.swift new file mode 100644 index 00000000..753b4aa6 --- /dev/null +++ b/Sort/EmployeeFreeTime.swift @@ -0,0 +1,77 @@ +/** + * Question Link: https://leetcode.com/problems/employee-free-time/ + * Primary idea: Combine and merge sorted arrays. Then iterate through it to get offset between every two elements. + * Time Complexity: O(n), Space Complexity: O(n) + * + * Definition for an Interval. + * public class Interval { + * public var start: Int + * public var end: Int + * public init(_ start: Int, _ end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class EmployeeFreeTime { + func employeeFreeTime(_ schedule: [[Interval]]) -> [Interval] { + let intervals = mergeIntervals(combineIntervals(schedule)) + var res = [Interval]() + + for i in 1.. [Interval] { + var res = [Interval]() + + for interval in intervals { + if let last = res.last, last.end >= interval.start { + res.removeLast() + res.append(Interval(last.start, max(last.end, interval.end))) + } else { + res.append(interval) + } + } + + return res + } + + private func combineIntervals(_ schedule: [[Interval]]) -> [Interval] { + var res = schedule[0] + + for i in 1.. [Interval] { + var res = [Interval](), i = 0, j = 0 + + while i < l.count || j < r.count { + if i == l.count { + res.append(r[j]) + j += 1 + } else if j == r.count { + res.append(l[i]) + i += 1 + } else { + if l[i].start <= r[j].start { + res.append(l[i]) + i += 1 + } else { + res.append(r[j]) + j += 1 + } + } + } + + return res + } +} diff --git a/Sort/InsertInterval.swift b/Sort/InsertInterval.swift new file mode 100644 index 00000000..8b3e85e7 --- /dev/null +++ b/Sort/InsertInterval.swift @@ -0,0 +1,49 @@ +/** + * Question Link: https://leetcode.com/problems/insert-interval/ + * Primary idea: First, check if nuewInterval's start is larger than one interval's end. + * If so, save the index, otherwise save intervals + * Second, keep updating a new interval if nuewInterval's end is larger then one interval's start + * If cannot find more, append the new interval to the result array + * Final Step, append the rest intervals to the result array + * + * Time Complexity: O(n), Space Complexity: O(1), + * + * Definition for an interval. + * public class Interval { + * public var start: Int + * public var end: Int + * public init(_ start: Int, _ end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class InsertInterval { + func insert(_ intervals: [Interval], _ newInterval: Interval) -> [Interval] { + 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) + } + } + + res.insert(newInterval, at: insertIdx) + + return res + } + + extension Interval { + func isOverlap(with interval: Interval) -> Bool { + return start <= interval.end && end >= interval.start + } + } +} 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/MeetingRooms.swift b/Sort/MeetingRooms.swift index 7b750c2a..b8bca988 100644 --- a/Sort/MeetingRooms.swift +++ b/Sort/MeetingRooms.swift @@ -28,7 +28,7 @@ class MeetingRooms { } } - for i in 0 ..< intervals.count - 1 { + for i in 0.. intervals[i + 1].start { return false } diff --git a/Sort/MeetingRoomsII.swift b/Sort/MeetingRoomsII.swift new file mode 100644 index 00000000..5513c40e --- /dev/null +++ b/Sort/MeetingRoomsII.swift @@ -0,0 +1,37 @@ +/** + * Question Link: https://leetcode.com/problems/meeting-rooms-ii/ + * Primary idea: Sort start and end separately, then count conflicts + * Time Complexity: O(nlogn), Space Complexity: O(n) + * + * Definition for an interval. + * public class Interval { + * public var start: Int + * public var end: Int + * public init(_ start: Int, _ end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class MeetingRoomsII { + 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 + + var i = 0, j = 0, meetingRoomsNum = 0 + + while i < intervalsCount && j < intervalsCount { + if startingTimes[i] < endingTimes[j] { + meetingRoomsNum += 1 + } else { + j += 1 + } + + i += 1 + } + + return meetingRoomsNum + } +} diff --git a/Sort/MergeIntervals.swift b/Sort/MergeIntervals.swift index 8a014945..dc558a34 100644 --- a/Sort/MergeIntervals.swift +++ b/Sort/MergeIntervals.swift @@ -15,32 +15,21 @@ */ class MergeIntervals { - func merge(intervals: [Interval]) -> [Interval] { - guard intervals.count > 1 else { - return intervals - } - - var intervals = intervals.sort() { - if $0.start != $1.start { - return $0.start < $1.start - } else { - return $0.end < $1.end - } - } + func merge(_ intervals: [[Int]]) -> [[Int]] { + let intervals = intervals.sorted { return $0[0] < $1[0] } + var res = [intervals[0]] + + for interval in intervals[1.. last.end { - res.append(current) + if lastEnd < interval[0] { + res.append(interval) } else { - last.end = max(last.end, current.end) + res[res.count - 1][1] = max(res[res.count - 1][1], interval[1]) } } - + return res } } \ No newline at end of file diff --git a/Sort/MergeSortedArray.swift b/Sort/MergeSortedArray.swift new file mode 100644 index 00000000..dc40e13e --- /dev/null +++ b/Sort/MergeSortedArray.swift @@ -0,0 +1,21 @@ +/** + * Question Link: https://leetcode.com/problems/merge-sorted-array/ + * Primary idea: Merge from tail to head to avoid override + * Time Complexity: O(n), Space Complexity: O(1) + */ + + class MergeSortedArray { + func merge(_ nums1: inout [Int], _ m: Int, _ nums2: [Int], _ n: Int) { + var i = m - 1, j = n - 1 + + while i >= 0 || j >= 0 { + if j < 0 || (i >= 0 && nums1[i] > nums2[j]) { + nums1[i + j + 1] = nums1[i] + i -= 1 + } else { + nums1[i + j + 1] = nums2[j] + j -= 1 + } + } + } +} \ No newline at end of file diff --git a/Sort/RangeModule.swift b/Sort/RangeModule.swift new file mode 100644 index 00000000..4b5ef42c --- /dev/null +++ b/Sort/RangeModule.swift @@ -0,0 +1,100 @@ +/** + * Question Link: https://leetcode.com/problems/range-module/ + * Primary idea: Sort ranges by left, query using binary search, remove to handle 4 cases separately. + * Time Complexity: add - O(nlogn), query - O(logn), remove - O(n), Space Complexity: O(n) + * + */ + +class RangeModule { + + var sortedRanges: [(Int, Int)] + + init() { + sortedRanges = [(Int, Int)]() + } + + func addRange(_ left: Int, _ right: Int) { + sortRanges(for: (left, right)) + } + + func queryRange(_ left: Int, _ right: Int) -> Bool { + // binary search + var l = 0, r = sortedRanges.count - 1 + + while l <= r { + let m = (r - l) / 2 + l + + if sortedRanges[m].0 <= left && sortedRanges[m].1 >= right { + return true + } else { + if sortedRanges[m].0 > right { + r = m - 1 + } else if sortedRanges[m].1 < left { + l = m + 1 + } else { + return false + } + } + } + + return false + } + + func removeRange(_ left: Int, _ right: Int) { + var idx = 0 + + while idx < sortedRanges.count { + let range = sortedRanges[idx] + + if isOverlap(range, (left, right)) { + if range.0 >= left && range.1 <= right { + sortedRanges.remove(at: idx) + } else if range.0 < left && range.1 > right { + sortedRanges.remove(at: idx) + sortedRanges.insert((right, range.1), at: idx) + sortedRanges.insert((range.0, left), at: idx) + idx += 2 + } else if range.1 <= right { + sortedRanges[idx].1 = left + idx += 1 + } else { + sortedRanges[idx].0 = right + idx += 1 + } + } else { + idx += 1 + } + } + } + + + private func sortRanges(for newRange: (Int, Int)) { + sortedRanges.append(newRange) + + sortedRanges.sort { + $0.0 < $1.0 + } + + var res = [(Int, Int)]() + + for range in sortedRanges { + guard let last = res.last else { + res.append(range) + continue + } + + if range.0 > last.1 { + res.append(range) + } else { + res.removeLast() + res.append((last.0, max(last.1, range.1))) + } + } + + sortedRanges = res + } + + private func isOverlap(_ l: (Int, Int), _ r: (Int, Int)) -> Bool { + return !(l.0 >= r.1 || r.0 >= l.1) + } +} diff --git a/Sort/SortColors.swift b/Sort/SortColors.swift index b3e549a1..cf2ec298 100644 --- a/Sort/SortColors.swift +++ b/Sort/SortColors.swift @@ -5,32 +5,26 @@ */ class SortColors { - func sortColors(inout nums: [Int]) { - guard nums.count > 1 else { - return - } - - var red = 0 - var blue = nums.count - 1 - var i = 0 + func sortColors(_ nums: inout [Int]) { + 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 } } - private func _swap(inout nums: [Int], _ p: Int, _ q: Int) { - let temp = nums[p] - nums[p] = nums[q] - nums[q] = temp + 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/SortTransformedArray.swift b/Sort/SortTransformedArray.swift new file mode 100644 index 00000000..9b001ff2 --- /dev/null +++ b/Sort/SortTransformedArray.swift @@ -0,0 +1,46 @@ +/** + * Question Link: https://leetcode.com/problems/sort-transformed-array/ + * Primary idea: Compare head and tail and put larger/smaller one to end/start of array, + * according to a is less than 0 or not + * Time Complexity: O(n), Space Complexity: O(1) + */ + +class SortTransformedArray { + func sortTransformedArray(_ nums: [Int], _ a: Int, _ b: Int, _ c: Int) -> [Int] { + var res = [Int](repeating: 0, count: nums.count) + var left = 0 + var right = res.count - 1 + var index = a >= 0 ? nums.count - 1 : 0 + + while left <= right { + let leftVal = getRes(a, b, c, nums[left]) + let rightVal = getRes(a, b, c, nums[right]) + + if a >= 0 { + if leftVal > rightVal { + res[index] = leftVal + left += 1 + } else { + res[index] = rightVal + right -= 1 + } + index -= 1 + } else if a < 0 { + if leftVal < rightVal { + res[index] = leftVal + left += 1 + } else { + res[index] = rightVal + right -= 1 + } + index += 1 + } + } + + return res + } + + private func getRes(_ a: Int, _ b: Int, _ c: Int, _ x: Int) -> Int { + return a * x * x + b * x + c + } +} \ No newline at end of file diff --git a/Sort/TopKFrequentElements.swift b/Sort/TopKFrequentElements.swift index b69793f6..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 ..< k]) + + return Array(sortedNums[0..= 2 else { return } - for i in 1.stride(to: nums.count, by: 2) { - let largestIndex = _getLargest(&nums, i - 1, i, i + 1) - if i != largestIndex { - _swap(&nums, largestIndex, i) - } + for i in stride(from: 1, to: nums.count, by: 2) { + let idx = getLargest(nums, i - 1, i , i + 1) + (nums[i], nums[idx]) = (nums[idx], nums[i]) } } - private func _swap(inout nums: [Int], _ p: Int, _ q: Int) { - let temp = nums[p] - nums[p] = nums[q] - nums[q] = temp - } + private func getLargest(_ nums: [Int], _ x: Int, _ y: Int, _ z: Int) -> Int { + let len = nums.count - private func _getLargest(inout nums: [Int], _ x: Int, _ y: Int, _ z: Int) -> Int { - let xVal = _isValid(x, nums.count) ? nums[x] : Int.min - let yVal = _isValid(y, nums.count) ? nums[y] : Int.min - let zVal = _isValid(z, nums.count) ? nums[z] : Int.min + let xVal = x >= 0 && x < len ? nums[x] : Int.min + let yVal = y >= 0 && y < len ? nums[y] : Int.min + let zVal = z >= 0 && z < len ? nums[z] : Int.min let maxVal = max(xVal, yVal, zVal) if maxVal == xVal { @@ -38,8 +32,4 @@ class WiggleSort { return z } } - - private func _isValid(index: Int, _ len: Int) -> Bool { - return index >= 0 && index < len - } } \ No newline at end of file diff --git a/Sort/WiggleSortII.swift b/Sort/WiggleSortII.swift index ea75f8a5..9f5b661d 100644 --- a/Sort/WiggleSortII.swift +++ b/Sort/WiggleSortII.swift @@ -5,13 +5,13 @@ */ class WiggleSortII { - func wiggleSort(inout nums: [Int]) { - let temp = nums.sort({$0 < $1}) + func wiggleSort(_ nums: inout [Int]) { + let temp = nums.sorted() var m = temp.count var n = (m + 1) / 2 - for i in 0 ..< nums.count { + for i in 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 { + func evalRPN(_ tokens: [String]) -> Int { var stack = [Int]() - for str in tokens { - if _isNumber(str) { - stack.append(Int(str)!) + for token in tokens { + if let num = Int(token) { + stack.append(num) } else { - guard stack.count > 1 else { - return 0 + guard let postNum = stack.popLast(), let prevNum = stack.popLast() else { + fatalError("Invalid Input") } - let post = stack.removeLast() - let prev = stack.removeLast() - - let res = _operate(prev, post, str) - stack.append(res) + stack.append(operate(token, prevNum, postNum)) } } - if stack.count == 0 { - return 0 + if let last = stack.last { + return last } else { - return stack.first! + fatalError("Invalid Input") } } - private func _isNumber(str: String) -> Bool { - return Int(str) != nil - } - - private func _operate(prev: Int, _ post: Int, _ token: String) -> 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 new file mode 100644 index 00000000..0c74b1f8 --- /dev/null +++ b/Stack/LongestValidParentheses.swift @@ -0,0 +1,31 @@ +/** + * Question Link: https://leetcode.com/problems/longest-valid-parentheses/ + * Primary idea: Push index to a stack and pop encountering ")" + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class LongestValidParentheses { + func longestValidParentheses(_ s: String) -> Int { + var stack = [Int](), longest = 0, start = 0 + + for (i, char) in s.enumerated() { + if char == "(" { + stack.append(i) + } else { + if !stack.isEmpty { + stack.removeLast() + + if let last = stack.last { + longest = max(longest, i - last) + } else { + longest = max(longest, i - start + 1) + } + } else { + start = i + 1 + } + } + } + + return longest + } +} 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/RemoveKDigits.swift b/Stack/RemoveKDigits.swift new file mode 100644 index 00000000..b2c02217 --- /dev/null +++ b/Stack/RemoveKDigits.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/remove-k-digits/ + * Primary idea: Keep a stack to ensure its numbers are ascending so that the number + * is the smallest, truncate it to right size and handle edge cases + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class RemoveKDigits { + func removeKdigits(_ num: String, _ k: Int) -> String { + var res = [Character](), k = k + let numChars = String(num).characters, size = numChars.count - k + + for char in numChars { + while k > 0 && res.count > 0 && charToInt(res.last!) > charToInt(char) { + res.removeLast() + k -= 1 + } + res.append(char) + } + + res = Array(res[0.. Int { + return Int(String(c))! + } +} \ No newline at end of file diff --git a/Stack/SimplifyPath.swift b/Stack/SimplifyPath.swift index 7ee8de8f..2c51d4ec 100644 --- a/Stack/SimplifyPath.swift +++ b/Stack/SimplifyPath.swift @@ -5,32 +5,19 @@ */ class SimplifyPath { - func simplifyPath(path: String) -> String { - var res = "" - var stack = [String]() - let paths = path.characters.split {$0 == "/"}.map(String.init) - - for path in paths { - var path = path.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) - - guard path != "." else { - continue + func simplifyPath(_ path: String) -> String { + 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)) } - - if path == ".." { - if (stack.count > 0) { - stack.removeLast() - } - } else if path.characters.count > 0 { - stack.append(path) - } - } - - for str in stack { - res += "/" - res += str } - - return res.isEmpty ? "/" : res + return "/" + String(directories.joined(separator: "/")) } -} \ No newline at end of file +} diff --git a/Stack/TernaryExpressionParser.swift b/Stack/TernaryExpressionParser.swift new file mode 100644 index 00000000..c363eb45 --- /dev/null +++ b/Stack/TernaryExpressionParser.swift @@ -0,0 +1,31 @@ +/** + * Question Link: https://leetcode.com/problems/ternary-expression-parser/ + * Primary idea: Use a stack and go from right to left, pop when peek is "?" + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class TernaryExpressionParser { + func parseTernary(_ expression: String) -> String { + var stack = [Character]() + + for char in expression.characters.reversed() { + if !stack.isEmpty && stack.last! == "?" { + stack.removeLast() + let first = stack.removeLast() + stack.removeLast() + let second = stack.removeLast() + + if char == "T" { + stack.append(first) + } else { + stack.append(second) + } + } else { + stack.append(char) + } + } + + + return stack.isEmpty ? "" : String(stack.last!) + } +} \ No newline at end of file diff --git a/Stack/ValidParentheses.swift b/Stack/ValidParentheses.swift index 7a42a7fd..5ac0bf14 100644 --- a/Stack/ValidParentheses.swift +++ b/Stack/ValidParentheses.swift @@ -5,27 +5,30 @@ */ class ValidParentheses { - func isValid(s: String) -> Bool { + func isValid(_ s: String) -> Bool { var stack = [Character]() - - for char in s.characters { - if char == "(" || char == "[" || char == "{" { + + for char in s { + 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.count == 0 + + return stack.isEmpty } } \ No newline at end of file diff --git a/String/AddStrings.swift b/String/AddStrings.swift new file mode 100644 index 00000000..dd120206 --- /dev/null +++ b/String/AddStrings.swift @@ -0,0 +1,38 @@ +/** + * Question Link: https://leetcode.com/problems/add-strings/ + * Primary idea: reverse two strings and add them using sum && carry idea + * + * Note: do not forget to reverse afterwards + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class AddStrings { + func addStrings(_ num1: String, _ num2: String) -> String { + let num1Chars = Array(num1.characters.reversed()) + let num2Chars = Array(num2.characters.reversed()) + var i = 0, j = 0, sum = 0, carry = 0 + var res = "" + + while i < num1Chars.count || j < num2Chars.count || carry != 0 { + sum = carry + + if i < num1Chars.count { + sum += Int(String(num1Chars[i]))! + i += 1 + } + if j < num2Chars.count { + sum += Int(String(num2Chars[j]))! + j += 1 + } + + carry = sum / 10 + sum = sum % 10 + + res.append(String(sum)) + } + + return String(res.characters.reversed()) + } +} \ No newline at end of file diff --git a/String/CountAndSay.swift b/String/CountAndSay.swift index 20e56a6a..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 ..< n{ - temp = "" - count = 1 - chars = [Character](res.characters) - current = chars[0] - - for i in 1 ..< chars.count { - if chars[i] == current { - count += 1 - } else { - temp.append(Character("\(count)")) - temp.append(current) - count = 1 - current = chars[i] - } + + let previousStr = countAndSay(n - 1) + var currentChar = previousStr.first!, currentCount = 0, res = "" + + for (i, char) in previousStr.enumerated() { + if char == currentChar { + currentCount += 1 + } else { + res += "\(currentCount)\(currentChar)" + + currentCount = 1 + currentChar = char } - temp.append(Character("\(count)")) - temp.append(current) - - res = temp } - + + res += "\(currentCount)\(currentChar)" + return res } -} \ No newline at end of file +} diff --git a/String/DetectCapital.swift b/String/DetectCapital.swift new file mode 100644 index 00000000..a8a7ee35 --- /dev/null +++ b/String/DetectCapital.swift @@ -0,0 +1,31 @@ +/** + * Question Link: https://leetcode.com/problems/detect-capital/ + * Primary idea: Counts uppercased characters then compare to the standards. + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class DetectCapital { + func detectCapitalUse(_ word: String) -> Bool { + var capitalNum = 0, isFirstUpperCased = false + + for char in word { + if char.isUpperCased() { + capitalNum += 1 + } + } + + if let firstChar = word.first { + isFirstUpperCased = firstChar.isUpperCased() + } + + return capitalNum == 0 || (capitalNum == 1 && isFirstUpperCased) || capitalNum == word.count + } +} + +fileprivate extension Character { + 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/FindDuplicateFileInSystem.swift b/String/FindDuplicateFileInSystem.swift new file mode 100644 index 00000000..619276a9 --- /dev/null +++ b/String/FindDuplicateFileInSystem.swift @@ -0,0 +1,36 @@ +/** + * Question Link: https://leetcode.com/problems/find-duplicate-file-in-system/ + * Primary idea: Iterate the paths array and categories paths with the same content + * + * Time Complexity: O(nm), n stands for number of paths, m stands for file number in a path + * Space Complexity: O(n) + */ + +class FindDuplicateFileInSystem { + func findDuplicate(_ paths: [String]) -> [[String]] { + var contentToFiles = [String: [String]]() + + for path in paths { + let params = path.split(separator: " ") + + guard let dir = params.first else { + continue + } + + for i in 1..= 2 } + } +} diff --git a/String/FirstUniqueCharacterInString.swift b/String/FirstUniqueCharacterInString.swift new file mode 100644 index 00000000..6af2a127 --- /dev/null +++ b/String/FirstUniqueCharacterInString.swift @@ -0,0 +1,31 @@ +/** + * Question Link: https://leetcode.com/problems/first-unique-character-in-a-string/ + * Primary idea: Keep track of existence of each character in the string + * + * Note: The maximum space of the dictionary is 26, so space complexity is O(1) + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class FirstUniqueCharacterInString { + func firstUniqChar(_ s: String) -> Int { + var dict = [Character: Bool]() + + for char in s.characters { + if let isDup = dict[char] { + dict[char] = true + } else { + dict[char] = false + } + } + + for (i, char) in Array(s.characters).enumerated() { + if !dict[char]! { + return i + } + } + + return -1 + } +} \ No newline at end of file diff --git a/String/FizzBuzz.swift b/String/FizzBuzz.swift new file mode 100644 index 00000000..952f584d --- /dev/null +++ b/String/FizzBuzz.swift @@ -0,0 +1,31 @@ +/** + * Question Link: https://leetcode.com/problems/fizz-buzz/ + * Primary idea: Iterate the array and handle multiples of 3 or 5 separately. + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + +class FizzBuzz { + func fizzBuzz(_ n: Int) -> [String] { + var res = [String]() + + if n < 0 { + return res + } + + for i in 1...n { + if i % 3 == 0 && i % 5 == 0 { + res.append("FizzBuzz") + } else if i % 3 == 0 { + res.append("Fizz") + } else if i % 5 == 0 { + res.append("Buzz") + } else { + res.append("\(i)") + } + } + + return res + } +} \ No newline at end of file diff --git a/String/FlipGame.swift b/String/FlipGame.swift index 8b5f3eda..f279b4ab 100644 --- a/String/FlipGame.swift +++ b/String/FlipGame.swift @@ -5,20 +5,17 @@ */ class FlipGame { - func generatePossibleNextMoves(s: String) -> [String] { + func generatePossibleNextMoves(_ s: String) -> [String] { var res = [String]() - var sChars = [Character](s.characters) + let sChars = Array(s) guard sChars.count > 1 else { return res } - for i in 0 ..< sChars.count - 1 { + for i in 0.. [[String]] { - var res = [[String]]() - var map = [String: [String]]() - - for str in strs { - let sortedStr = _sortStr(str) - - var anagrams = [String]() - if map[sortedStr] != nil { - anagrams = map[sortedStr] as [String]! - } - anagrams.append(str) - map[sortedStr] = anagrams - } - - _convertMapToLists(map, &res) - return res + func groupAnagrams(_ strs: [String]) -> [[String]] { + return Array(Dictionary(strs.map { (String($0.sorted()), [$0]) }, uniquingKeysWith: +).values) } - - private func _sortStr(str: String) -> String { - var strChars = [Character](str.characters) - strChars.sortInPlace({$0 < $1}) - return String(strChars) - } - - private func _convertMapToLists(map: [String: [String]], inout _ res: [[String]]) { - for anagrams in map.values { - let anagrams = anagrams.sort({$0 < $1}) - res.append(anagrams) - } - } -} \ No newline at end of file +} diff --git a/String/IsomorphicStrings.swift b/String/IsomorphicStrings.swift index 18705ba5..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 ..< sChars.count { - let sCurrent = sChars[i] - let tCurrent = tChars[i] + var stDict = [Character: Character](), tsDict = [Character: Character]() + let s = Array(s), t = Array(t) + + for (i, sChar) in s.enumerated() { + let tChar = t[i] - if stDict[sCurrent] == nil && tsDict[tCurrent] == nil { - stDict[sCurrent] = tCurrent - tsDict[tCurrent] = sCurrent - } else if stDict[sCurrent] != tCurrent || tsDict[tCurrent] != sCurrent { + if stDict[sChar] == nil && tsDict[tChar] == nil { + stDict[sChar] = tChar + tsDict[tChar] = sChar + } else if stDict[sChar] != tChar || tsDict[tChar] != sChar { return false } } diff --git a/String/KeyboardRow.swift b/String/KeyboardRow.swift new file mode 100644 index 00000000..8e81fa3c --- /dev/null +++ b/String/KeyboardRow.swift @@ -0,0 +1,23 @@ +/** + * Question Link: https://leetcode.com/problems/keyboard-row/ + * Primary idea: Use filter to determine the word is subset or not. + * + * Note: You can also use intersect() or union() functions to solve this problem. + * + * Time Complexity: O(nm), Space Complexity: O(n) + * + */ + +class KeyboardRow { + func findWords(_ words: [String]) -> [String] { + let rowOne = "qwertyuiop", rowTwo = "asdfghjkl", rowThree = "zxcvbnm" + + 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) }.count == 0 + } +} diff --git a/String/LengthLastWord.swift b/String/LengthLastWord.swift index 7c93c9af..7a24d19c 100644 --- a/String/LengthLastWord.swift +++ b/String/LengthLastWord.swift @@ -13,7 +13,7 @@ class LengthLastWord { return 0 } - for i in (0 ... sChars.count - 1).reverse() { + for i in (0...sChars.count - 1).reverse() { if res == 0 { if sChars[i] == " " { continue 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 29e18d6c..74c6b859 100644 --- a/String/LongestCommonPrefix.swift +++ b/String/LongestCommonPrefix.swift @@ -1,32 +1,34 @@ /** * Question Link: https://leetcode.com/problems/longest-common-prefix/ * Primary idea: Use the first string as the result at first, trim it while iterating the array - * Time Complexity: O(nm), Space Complexity: O(m), m stands for the length of first string + * Time Complexity: O(nm), Space Complexity: O(m), m stands for the length of longest prefix */ class LongestCommonPrefix { - func longestCommonPrefix(strs: [String]) -> String { - guard strs.count > 0 else { + func longestCommonPrefix(_ strs: [String]) -> String { + guard let firstStr = strs.first else { return "" } - - var res = [Character](strs[0].characters) - for str in strs { - var strContent = [Character](str.characters) - - if res.count > strContent.count { - res = Array(res[0 ..< strContent.count]) - } - - for i in 0 ..< res.count { - if res[i] != strContent[i] { - res = Array(res[0 ..< i]) - break + var res = "" + + 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 + } + + // 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 } } + res.append(char) } - return String(res) + return res } } \ No newline at end of file diff --git a/String/LongestSubstringWithoutRepeatingCharacters.swift b/String/LongestSubstringWithoutRepeatingCharacters.swift deleted file mode 100644 index 68ff715e..00000000 --- a/String/LongestSubstringWithoutRepeatingCharacters.swift +++ /dev/null @@ -1,42 +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 { - guard s.characters.count != 0 else { - return 0 - } - - var set = Set() - var maxLen = 0 - var startIndex = 0 - var chars = [Character](s.characters) - - for i in 0 ..< chars.count { - var current = chars[i] - - if set.contains(current) { - maxLen = max(maxLen, i - startIndex) - while chars[startIndex] != current { - set.remove(chars[startIndex]) - startIndex += 1 - } - startIndex += 1 - } else { - set.insert(current) - } - } - - maxLen = max(maxLen, chars.count - startIndex) - - return maxLen - } -} \ 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 new file mode 100644 index 00000000..c1c0302d --- /dev/null +++ b/String/MultiplyStrings.swift @@ -0,0 +1,53 @@ +/** + * Question Link: https://leetcode.com/problems/multiply-strings/ + * Primary idea: reverse two strings and multiply them use nested loop by every digit, + * then use sum && carry idea to get the final result + * + * Note: do not forget to remove leading 0 afterwards + * + * Time Complexity: O(nm), Space Complexity: O(n) + * + */ + + +class MultiplyStrings { + func multiply(_ num1: String, _ num2: String) -> String { + let num1 = num1.reversed(), num2 = num2.reversed() + var res = Array(repeating: 0, count: num1.count + num2.count), resStr = "" + + // 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 + } + } + + // update digits + for i in 0.. Bool { - let sLen = s.characters.count - let tLen = t.characters.count - - guard abs(sLen - tLen) <= 1 else { - return false + func isOneEditDistance(_ s: String, _ t: String) -> Bool { + guard s != t else { + return false } + var editType = Edit.insert - if sLen > tLen { - return _isAddChar(t, s) - } else if sLen == tLen { - return _isReplaceChar(s, t) + 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 _isAddChar(s, t) + return false } - } - - private func _isAddChar(s: String, _ t: String) -> Bool { - let sChars = [Character](s.characters) - let tChars = [Character](t.characters) - var tIndex = 0 - var sIndex = 0 + let arr = Array(s), brr = Array(t) + var flag = false, aIdx = 0, bIdx = 0 - while sIndex < sChars.count && tIndex < tChars.count { - if sChars[sIndex] != tChars[tIndex] { - tIndex += 1 + while aIdx < arr.count && bIdx < brr.count { + + if arr[aIdx] != brr[bIdx] { + + guard !flag else { + return false + } + + flag = true + + switch editType { + case .insert: + bIdx += 1 + case .delete: + aIdx += 1 + case .replace: + aIdx += 1 + bIdx += 1 + } } else { - tIndex += 1 - sIndex += 1 - } - } - - return tIndex - sIndex <= 1 - } - - private func _isReplaceChar(s: String, _ t: String) -> Bool { - let sChars = [Character](s.characters) - let tChars = [Character](t.characters) - - var tIndex = 0 - var diff = 0 - - for sIndex in 0 ..< sChars.count { - if sChars[sIndex] != tChars[tIndex] { - diff += 1 + aIdx += 1 + bIdx += 1 } - tIndex += 1 } - - return diff == 1 + return true } -} \ No newline at end of file +} diff --git a/String/PalindromePermutation.swift b/String/PalindromePermutation.swift index 467fd28a..8d012c6f 100644 --- a/String/PalindromePermutation.swift +++ b/String/PalindromePermutation.swift @@ -5,29 +5,23 @@ */ class PalindromePermutation { - func canPermutePalindrome(s: String) -> Bool { - let sChars = [Character](s.characters) - var oddNum = 0 - var charFrequency = [Character: Int]() - - for char in sChars { - if charFrequency[char] != nil { - charFrequency[char]! += 1 - } else { - charFrequency[char] = 1 - } - } - - for char in charFrequency.keys { - let fre = charFrequency[char]! - if fre % 2 == 1 { - oddNum += 1 - } - if oddNum >= 2 { - return false - } - } - - return true + func canPermutePalindrome(_ s: String) -> Bool { + var oddNum = 0 + + for (_, value) in s.frequencies where value % 2 == 1 { + oddNum += 1 + + if oddNum >= 2 { + return false + } } + + return true + } +} + +extension Sequence where Element: Hashable { + var frequencies: [Element: Int] { + return Dictionary(self.map{ ($0, 1)}, uniquingKeysWith: +) + } } \ 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/RansomNote.swift b/String/RansomNote.swift new file mode 100644 index 00000000..b79ca95e --- /dev/null +++ b/String/RansomNote.swift @@ -0,0 +1,39 @@ +/** + * Question Link: https://leetcode.com/problems/ransom-note/ + * Primary idea: Use a dictionary to calculate the existence of characters in magazine + * and check with the ransom Note + * + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class RansomNote { + func canConstruct(ransomNote: String, _ magazine: String) -> Bool { + var magazineMap = _strToMap(magazine) + + for char in ransomNote.characters { + if magazineMap[char] == nil { + return false + } else if magazineMap[char] == 0 { + return false + } else { + magazineMap[char]! -= 1 + } + } + + return true + } + + private func _strToMap(magazine: String) -> [Character: Int] { + var res = [Character: Int]() + + for char in magazine.characters { + if res[char] == nil { + res[char] = 1 + } else { + res[char]! += 1 + } + } + + return res + } +} \ No newline at end of file diff --git a/String/RegularExpressionMatching.swift b/String/RegularExpressionMatching.swift new file mode 100644 index 00000000..1a626a54 --- /dev/null +++ b/String/RegularExpressionMatching.swift @@ -0,0 +1,81 @@ +/** + * Question Link: https://leetcode.com/problems/regular-expression-matching/ + * Primary idea: recursion, compare the first char of s and the first token of p on each round. For tokens with *, consider the two possibilities of keeping or consuming this token for the next round. + * + * Note: A simple recursion violates the time limit. We need to first simplify the pattern in the following ways: + * 1. a*a* -> a* merge identical "x*"s + * 2. a*b*.*c*d* -> .* ignore all the "x*"s before and after ".*" + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +struct Token { + var char:Character + var isStar:Bool +} + +class RegularExpressionMatching { + func isMatch(_ s: String, _ p: String) -> Bool { + return isMatch(sChars: [Character](s.characters), tokens: generateTokensFrom(pattern: p)) + } + + func generateTokensFrom(pattern:String) -> [Token] { // simplify the pattern and generate tokens + let chars = [Character](pattern.characters) + var tokens = [Token]() + var index = 0 + while index < chars.count { + let char = chars[index] + let nextChar = index + 1 < chars.count ? chars[index + 1] : " " + + if nextChar == "*" { + index = index + 2 + let lastToken = tokens.count > 0 ? tokens.last! : Token(char: " ", isStar: false) + if lastToken.isStar == true && (lastToken.char == char || lastToken.char == ".") { + continue // ignore x* after x* or .* + } + if char == "." { + while tokens.count > 0 && tokens.last?.isStar == true { + tokens.popLast() // remove every x* before .* + } + } + tokens.append(Token(char: char, isStar: true)) + } else { + index = index + 1 + tokens.append(Token(char: char, isStar: false)) + } + } + + return tokens + } + + func isMatch(sChars : [Character], tokens : [Token]) -> Bool { + guard tokens.count > 0 else { + return sChars.count == 0 ? true : false + } + + guard sChars.count > 0 else { + for token in tokens { + if !token.isStar { + return false + } + } + return true + } + + let token = tokens[0] + let sChar = sChars[0] + let nextS = Array(sChars.dropFirst()) + let nextTokens = Array(tokens.dropFirst()) + + let match = token.char == "." || token.char == sChar + if token.isStar { + if match && (isMatch(sChars:nextS, tokens:nextTokens) || isMatch(sChars:nextS, tokens:tokens)) { + return true + } + return isMatch(sChars:sChars, tokens:nextTokens) + } else { + return match && isMatch(sChars:nextS, tokens:nextTokens) + } + } +} diff --git a/String/ReverseString.swift b/String/ReverseString.swift index 78bd8ca8..83543645 100644 --- a/String/ReverseString.swift +++ b/String/ReverseString.swift @@ -8,6 +8,6 @@ class ReverseString { func reverseString(s: String) -> String { - return String(s.characters.reverse()) + return String(s.reversed()) } -} \ No newline at end of file +} diff --git a/String/ReverseStringII.swift b/String/ReverseStringII.swift new file mode 100644 index 00000000..b223d05d --- /dev/null +++ b/String/ReverseStringII.swift @@ -0,0 +1,39 @@ +/** + * Question Link: https://leetcode.com/problems/reverse-string-ii/ + * Primary idea: Use reversed() to help reverse the string + * + * Note: Avoid index out of range error as k will pass the right edge of the string + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class ReverseStringII { + func reverseStr(_ s: String, _ k: Int) -> String { + var chars = [Character](s.characters), res = [Character]() + + if k < 0 { + fatalError("Invalid k") + } + + for i in stride(from: 0, to: chars.count, by: 2 * k) { + print(i) + + if chars.count < i + k { + res += chars[i.. 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: [Character], _ 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 new file mode 100644 index 00000000..2d7e9358 --- /dev/null +++ b/String/ReverseWordsString.swift @@ -0,0 +1,14 @@ +/** + * Question Link: https://leetcode.com/problems/reverse-words-in-a-string/ + * 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) + * + */ + +class ReverseWordsString { + func reverseWords(_ s: String) -> String { + return s.split(separator: " ").reversed().joined(separator: " ") + } +} diff --git a/String/ReverseWordsStringII.swift b/String/ReverseWordsStringII.swift new file mode 100644 index 00000000..88b14a95 --- /dev/null +++ b/String/ReverseWordsStringII.swift @@ -0,0 +1,32 @@ +/** + * Question Link: https://leetcode.com/problems/reverse-words-in-a-string-ii/ + * Primary idea: Reverse the whole string, then reverse every word + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class ReverseWordsStringII { + func reverseWords(_ str: inout [Character]) { + var last = 0 + + reverse(&str, 0, str.count - 1) + + for i in 0..(_ array: inout [T], _ startIdx: Int, _ endIdx: Int) { + var (left, right) = (startIdx, endIdx) + + while left < right { + (array[left], array[right]) = (array[right], array[left]) + left += 1 + right -= 1 + } + } +} diff --git a/String/ReverseWordsStringIII.swift b/String/ReverseWordsStringIII.swift new file mode 100644 index 00000000..7881f9ae --- /dev/null +++ b/String/ReverseWordsStringIII.swift @@ -0,0 +1,32 @@ +/** + * Question Link: https://leetcode.com/problems/reverse-words-in-a-string-iii/ + * Primary idea: Check the empty space to get the previous word, then reverse it + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class ReverseWordsStringIII { + func reverseWords(_ s: String) -> String { + var last = 0, str = Array(s) + + for i in 0..(_ array: inout [T], _ startIdx: Int, _ endIdx: Int) { + var (left, right) = (startIdx, endIdx) + + while left < right { + (array[left], array[right]) = (array[right], array[left]) + left += 1 + right -= 1 + } + } +} diff --git a/String/ShortestDistanceToACharacter.swift b/String/ShortestDistanceToACharacter.swift new file mode 100644 index 00000000..0eb1ebd1 --- /dev/null +++ b/String/ShortestDistanceToACharacter.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-distance-to-a-character/ + * Primary idea: Iterate through left and right to get min distance by compared between indices of C at two sides. + * + * Time Complexity: O(n), Space Complexity: O(1) + * + */ + + class ShortestDistanceToACharacter { + func shortestToChar(_ S: String, _ C: Character) -> [Int] { + var res = Array(repeating: 0, count: S.count), cIndex = -10000, sChars = Array(S) + + for (i, sChar) in sChars.enumerated() { + if sChar == C { + cIndex = i + } + + res[i] = i - cIndex + } + + cIndex = -10000 + + for i in (0.. Int { + var res = 0, idx = 0 + let s = Array(source), t = Array(target) + + while idx < t.count { + let pre = idx + + for i in 0.. Int { - var hChars = [Character](haystack.characters) - var nChars = [Character](needle.characters) - - guard hChars.count >= nChars.count else { + func strStr(_ haystack: String, _ needle: String) -> Int { + let hChars = Array(haystack.characters), nChars = Array(needle.characters) + let hLen = hChars.count, nLen = nChars.count + + guard hLen >= nLen else { return -1 } - if nChars.count == 0 { + guard nLen != 0 else { return 0 } - for i in 0 ... hChars.count - nChars.count { - guard hChars[i] == nChars[0] else { - continue - } - - for j in 0 ..< nChars.count { - guard hChars[i + j] == nChars[j] else { - break - } - - if j == nChars.count - 1 { - return i + for i in 0...hLen - nLen { + if hChars[i] == nChars[0] { + for j in 0.. Int { + var index = 0, currentCount = 0 + + 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 new file mode 100644 index 00000000..90efbb84 --- /dev/null +++ b/String/TextJustification.swift @@ -0,0 +1,73 @@ +/** + * Question Link: https://leetcode.com/problems/text-justification/ + * Primary idea: Iterate the words, keep track of the index of first word and the length + * of the line. Insert spaces with fix spaces and extra spaces. + * Time Complexity: O(n), Space Complexity: O(n) + */ + +class TextJustification { + func fullJustify(_ words: [String], _ maxWidth: Int) -> [String] { + var res = [String](), rowStart = 0, currentLen = 0 + + for (i, word) in words.enumerated() { + + if currentLen + word.count + (i - rowStart) > maxWidth { + res.append(buildRow(rowStart, i - 1, words, maxWidth, currentLen)) + + rowStart = i + currentLen = 0 + } + + currentLen += word.count + } + + res.append(buildLastRow(rowStart, words, maxWidth)) + + return res + } + + private func buildRow(_ start: Int, _ end: Int, _ words: [String], _ maxWidth: Int, _ currentLen: Int) -> String { + var res = "", spaceNum = 0, extraSpaceNum = 0 + + if end > start { + spaceNum = (maxWidth - currentLen) / (end - start) + extraSpaceNum = (maxWidth - currentLen) % (end - start) + } else { + spaceNum = maxWidth - currentLen + } + + for i in start...end { + res += words[i] + + if start != end && i == end { + break + } + + res.append(String(repeating: " ", count: spaceNum)) + if extraSpaceNum > 0 { + res.append(" ") + extraSpaceNum -= 1 + } + } + + return res + } + + 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 new file mode 100644 index 00000000..07c64474 --- /dev/null +++ b/String/ValidPalindrome.swift @@ -0,0 +1,39 @@ +/** + * Question Link: https://leetcode.com/problems/valid-palindrome/ + * Primary idea: For every index in the first half of the String, compare two values at mirroring indices. + * + * Time Complexity: O(n), Space Complexity: O(n) + * + */ + +class ValidPalindrome { + func isPalindrome(_ s: String) -> Bool { + var i = 0, j = s.count - 1 + let sChars = Array(s.lowercased()) + + 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 new file mode 100644 index 00000000..8a18495e --- /dev/null +++ b/String/ValidPalindromeII.swift @@ -0,0 +1,40 @@ +/** + * Question Link: https://leetcode.com/problems/valid-palindrome-ii/ + * 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(1) + * + */ + +class ValidPalindromeII { + func validPalindrome(_ s: String) -> Bool { + var i = 0, j = s.count - 1, isDeleted = false + let s = Array(s) + + while i < j { + if s[i] != s[j] { + 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 + } +} 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/WordPattern.swift b/String/WordPattern.swift index 178dee6b..bdd4a630 100644 --- a/String/WordPattern.swift +++ b/String/WordPattern.swift @@ -5,26 +5,27 @@ */ class WordPattern { - func wordPattern(pattern: String, _ str: String) -> Bool { - var wordDict = [String: Character]() - var charDict = [Character: String]() - let strs = str.characters.split{$0 == " "}.map(String.init) - let patterns = [Character](pattern.characters) + func wordPattern(_ pattern: String, _ str: String) -> Bool { + let strs = str.split(separator: " ").map(String.init) - guard patterns.count == strs.count else { + guard pattern.count == strs.count else { return false } - for i in 0 ..< strs.count { - let currentWord = strs[i] - let currentChar = patterns[i] + var patternToWord = [Character: String]() - if wordDict[currentWord] == nil && charDict[currentChar] == nil{ - wordDict[currentWord] = currentChar - charDict[currentChar] = currentWord + for (i, char) in pattern.enumerated() { + let word = strs[i] + + if let charWord = patternToWord[char] { + if charWord != word { + return false + } } else { - if wordDict[currentWord] != currentChar { + if patternToWord.values.contains(word) { return false + } else { + patternToWord[char] = word } } } diff --git a/String/ZigzagConversion.swift b/String/ZigzagConversion.swift new file mode 100644 index 00000000..cbde9d45 --- /dev/null +++ b/String/ZigzagConversion.swift @@ -0,0 +1,41 @@ +/** + * Question Link: https://leetcode.com/problems/zigzag-conversion/ + * + * Primary idea: The first and the last row, loop length is (2 * numRows - 2) + * For each row between them, should insert another number, index = index + 2 * (numRows - i - 1) + * + * Time Complexity: O(log(n + m)), Space Complexity: O(1) + * + */ + +class ZigzagConversion { + func convert(s: String, _ numRows: Int) -> String { + if numRows == 1 { + return s + } + + var ret: [Character] = [] + var chars: [Character] = [Character](s.characters) + let cnt = chars.count + + + for i in 0.. Bool { - guard let root = root else { - return true - } - - if _checkHeight(root) == -1 { - return false - } - - return true + func isBalanced(_ root: TreeNode?) -> Bool { + return checkHeight(root) != -1 } - private func _checkHeight(root: TreeNode?) -> Int { + private func checkHeight(_ root: TreeNode?) -> Int { guard let root = root else { return 0 } - // check left and right subtree - var leftHeight = _checkHeight(root.left) - if leftHeight == -1 { - return -1 - } - var rightHeight = _checkHeight(root.right) - if rightHeight == -1 { + let left = checkHeight(root.left), right = checkHeight(root.right) + + if left == -1 || right == -1 { return -1 } - // check current - if abs(leftHeight - rightHeight) > 1 { + if abs(left - right) > 1 { return -1 } - return max(leftHeight, rightHeight) + 1 + return max(left, right) + 1 } } \ No newline at end of file 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 new file mode 100644 index 00000000..f5e9c223 --- /dev/null +++ b/Tree/BinaryTreePaths.swift @@ -0,0 +1,45 @@ +/** + * Question Link: https://leetcode.com/problems/binary-tree-paths/ + * Primary idea: Classic DFS problem, add the path to paths when the node is leaf + * 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 BinaryTreePaths { + func binaryTreePaths(_ root: TreeNode?) -> [String] { + var paths = [String]() + + guard let root = root else { + return paths + } + + 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(path) + return + } + + if let left = root.left { + dfs(left, &paths, path + "->\(left.val)") + } + if let right = root.right { + dfs(right, &paths, path + "->\(right.val)") + } + } +} diff --git a/Tree/BinaryTreeRightSideView.swift b/Tree/BinaryTreeRightSideView.swift new file mode 100644 index 00000000..dca72c68 --- /dev/null +++ b/Tree/BinaryTreeRightSideView.swift @@ -0,0 +1,49 @@ +/** + * Question Link: https://leetcode.com/problems/binary-tree-right-side-view/ + * Primary idea: use a queue to keep TreeNode, and for each level add the last one + * 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 BinaryTreeRightSideView { + func rightSideView(_ root: TreeNode?) -> [Int] { + var res = [Int]() + var nodeQ = [TreeNode]() + + if let root = root { + nodeQ.append(root) + } + + while nodeQ.count > 0 { + var size = nodeQ.count + + for i in 0.. TreeNode? { + var parent: TreeNode? + var node: TreeNode? = root + var right: TreeNode? + + while node != nil { + let left = node!.left + node!.left = right + right = node!.right + node!.right = parent + parent = node + node = left + } + + return parent + } +} \ No newline at end of file diff --git a/Tree/BinaryTreeVerticalOrderTraversal.swift b/Tree/BinaryTreeVerticalOrderTraversal.swift new file mode 100644 index 00000000..aca714a3 --- /dev/null +++ b/Tree/BinaryTreeVerticalOrderTraversal.swift @@ -0,0 +1,49 @@ +/** + * Question Link: https://leetcode.com/problems/binary-tree-vertical-order-traversal/ + * Primary idea: Use queues to hold TreeNode and column level, and a dictionary to + * map column with its correspond TreeNode vals + * + * 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 BinaryTreeVerticalOrderTraversal { + func verticalOrder(_ root: TreeNode?) -> [[Int]] { + func verticalOrder(_ root: TreeNode?) -> [[Int]] { + guard let root = root else { + return [[Int]]() + } + + var orderToVals = [0: [root.val]], nodes = [(root, 0)] + + while !nodes.isEmpty { + let size = nodes.count + + for _ in 0.. [[Int]] { - var res = [[Int]]() - var queue = [TreeNode]() - var isOdd = false - - if let root = root { - queue.append(root) + guard let root = root else { + return [[Int]]() } - while queue.count > 0 { - var size = queue.count - var level = [Int]() + var res = [[Int]](), isReverse = false, nodeQ = [root] + + while !nodeQ.isEmpty { + let currentLevel = nodeQ.map { $0.val } + res.append(isReverse ? currentLevel.reversed() : currentLevel) - for _ in 1...size { - let node = queue[0] - queue.removeAtIndex(0) - - // add val - if isOdd { - level.insert(node.val, atIndex: 0) - } else { - 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) - } - } + isReverse = !isReverse - res.append(level) - isOdd = !isOdd + nodeQ = nodeQ.flatMap { [$0.left, $0.right].compactMap { $0 } } } return res diff --git a/Tree/ConstructBinaryTreeInorderPostorder.swift b/Tree/ConstructBinaryTreeInorderPostorder.swift index 7da9a7a2..482fb900 100644 --- a/Tree/ConstructBinaryTreeInorderPostorder.swift +++ b/Tree/ConstructBinaryTreeInorderPostorder.swift @@ -2,7 +2,7 @@ * Question Link: https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/ * Primary idea: Always use the last element in postorder as root, * then find that one in inorder to get left and right subtrees - * Time Complexity: O(n), Space Complexity: O(1) + * Time Complexity: O(nlogn), Space Complexity: O(1) * * Definition for a binary tree node. * public class TreeNode { @@ -34,11 +34,9 @@ class ConstructBinaryTreeInorderPostorder { let root = TreeNode(postorder[postEnd]) var mid = 0 - for i in inStart ... inEnd { - if inorder[i] == root.val { - mid = i - break - } + for i in inStart...inEnd where inorder[i] == root.val { + mid = i + break } root.left = _buildHelper(inorder, inStart, mid - 1, postorder, postStart, mid - 1 - inStart + postStart) diff --git a/Tree/ConstructBinaryTreePreorderInorder.swift b/Tree/ConstructBinaryTreePreorderInorder.swift index 4f3b5a0f..d35cffe3 100644 --- a/Tree/ConstructBinaryTreePreorderInorder.swift +++ b/Tree/ConstructBinaryTreePreorderInorder.swift @@ -2,7 +2,7 @@ * Question Link: https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ * Primary idea: Always use the first element in preorder as root, * then find that one in inorder to get left and right subtrees - * Time Complexity: O(n), Space Complexity: O(1) + * Time Complexity: O(nlogn), Space Complexity: O(1) * * Definition for a binary tree node. * public class TreeNode { @@ -18,32 +18,27 @@ */ class ConstructBinaryTreePreorderInorder { - func buildTree(preorder: [Int], _ inorder: [Int]) -> 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/DiameterBinaryTree.swift b/Tree/DiameterBinaryTree.swift new file mode 100644 index 00000000..e77c8db8 --- /dev/null +++ b/Tree/DiameterBinaryTree.swift @@ -0,0 +1,39 @@ +/** + * Question Link: https://leetcode.com/problems/diameter-of-binary-tree/ + * Primary idea: recursion, update globel diameter along with maxDepth + * 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 DiameterBinaryTree { + var diameter = 0 + + func diameterOfBinaryTree(_ root: TreeNode?) -> Int { + maxDepth(root) + + return diameter + } + + fileprivate func maxDepth(_ node: TreeNode?) -> Int { + guard let node = node else { + return 0 + } + + let (left, right) = (maxDepth(node.left), maxDepth(node.right)) + + diameter = max(diameter, left + right) + + return max(left, right) + 1 + } +} \ No newline at end of file diff --git a/Tree/FlattenBinaryTreeLinkedList.swift b/Tree/FlattenBinaryTreeLinkedList.swift new file mode 100644 index 00000000..abf9c460 --- /dev/null +++ b/Tree/FlattenBinaryTreeLinkedList.swift @@ -0,0 +1,47 @@ +/** + * Question Link: https://leetcode.com/problems/flatten-binary-tree-to-linked-list/ + * Primary idea: Reset left to nil and change current node to left child every time + * 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 FlattenBinaryTreeLinkedList { + func flatten(_ root: TreeNode?) { + helper(root) + } + + private func helper(_ node: TreeNode?) -> TreeNode? { + var node = node + if node == nil { + return node + } + if node!.left == nil && node!.right == nil { + return node + } + + let left = node!.left, right = node!.right + node!.left = nil + + if let left = left { + node!.right = left + node = helper(left) + } + if let right = right { + node!.right = right + node = helper(right) + } + + return node + } +} \ No newline at end of file diff --git a/Tree/HouseRobberIII.swift b/Tree/HouseRobberIII.swift new file mode 100644 index 00000000..074ec0a9 --- /dev/null +++ b/Tree/HouseRobberIII.swift @@ -0,0 +1,40 @@ +/** + * Question Link: https://leetcode.com/problems/house-robber-iii/ + * Primary idea: Using two sums to track rob sum starting from current node or not, + * compare and get the maximum one + * 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 HouseRobberIII { + func rob(_ root: TreeNode?) -> Int { + let (robRoot, notRobRoot) = helper(root) + + return max(robRoot, notRobRoot) + } + + fileprivate func helper(_ node: TreeNode?) -> (Int, Int) { + guard let node = node else { + return (0, 0) + } + + let (robLeft, notRobLeft) = helper(node.left) + let (robRight, notRobRight) = helper(node.right) + + let robNode = notRobLeft + notRobRight + node.val + let notRobNode = max(robLeft, notRobLeft) + max(robRight, notRobRight) + + return (robNode, notRobNode) + } +} \ No newline at end of file diff --git a/Tree/InvertBinaryTree.swift b/Tree/InvertBinaryTree.swift index 4b5b8273..720643c4 100644 --- a/Tree/InvertBinaryTree.swift +++ b/Tree/InvertBinaryTree.swift @@ -1,7 +1,7 @@ /** * Question Link: https://leetcode.com/problems/invert-binary-tree/ * Primary idea: recursion, swap left and right of current node each time - * Time Complexity: O(n), Space Complexity: O(1) + * Time Complexity: O(n), Space Complexity: O(n) * * Definition for a binary tree node. * public class TreeNode { @@ -17,18 +17,16 @@ */ class InvertBinaryTree { - func invertTree(root: TreeNode?) -> TreeNode? { + func invertTree(_ root: TreeNode?) -> TreeNode? { guard let root = root else { return nil } - var temp = root.left - root.left = root.right - root.right = temp + (root.left, root.right) = (root.right, root.left) invertTree(root.left) invertTree(root.right) return root } -} \ No newline at end of file +} diff --git a/Tree/KthSmallestElementBST.swift b/Tree/KthSmallestElementBST.swift new file mode 100644 index 00000000..3f6173fe --- /dev/null +++ b/Tree/KthSmallestElementBST.swift @@ -0,0 +1,41 @@ +/** + * Question Link: https://leetcode.com/problems/kth-smallest-element-in-a-bst/ + * Primary idea: use stack to do inorder traverse and track k to find answer + * 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 KthSmallestElementBST { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var stack = [TreeNode](), currentNode = root, k = k + + while !stack.isEmpty || currentNode != nil { + if currentNode != nil { + stack.append(currentNode!) + currentNode = currentNode!.left + } else { + let node = stack.removeLast() + k -= 1 + + if k == 0 { + return node.val + } + + currentNode = node.right + } + } + + return -1 + } +} \ No newline at end of file diff --git a/Tree/MaximumDepthOfBinaryTree.swift b/Tree/MaximumDepthOfBinaryTree.swift index b8bf1848..c85f7134 100644 --- a/Tree/MaximumDepthOfBinaryTree.swift +++ b/Tree/MaximumDepthOfBinaryTree.swift @@ -17,7 +17,7 @@ */ class MaximumDepthOfBinaryTree { - func maxDepth(root: TreeNode?) -> Int { + func maxDepth(_ root: TreeNode?) -> Int { guard let root = root else { return 0 } diff --git a/Tree/MergeTwoBinaryTrees.swift b/Tree/MergeTwoBinaryTrees.swift new file mode 100644 index 00000000..103e048b --- /dev/null +++ b/Tree/MergeTwoBinaryTrees.swift @@ -0,0 +1,27 @@ +/** + * 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 MergeTwoBinaryTrees { + func mergeTrees(_ t1: TreeNode?, _ t2: TreeNode?) -> TreeNode? { + guard let t1 = t1 else { + return t2 + } + guard let t2 = t2 else { + return t1 + } + t1.val += t2.val + t1.left = mergeTrees(t1.left, t2.left) + t1.right = mergeTrees(t1.right, t2.right) + return t1 + } +} diff --git a/Tree/PathSum.swift b/Tree/PathSum.swift index 6e0e8ed9..23fe7820 100644 --- a/Tree/PathSum.swift +++ b/Tree/PathSum.swift @@ -1,7 +1,7 @@ /** * Question Link: https://leetcode.com/problems/path-sum/ * Primary idea: recursion - * Time Complexity: O(n), Space Complexity: O(1) + * Time Complexity: O(n), Space Complexity: O(n) * * Definition for a binary tree node. * public class TreeNode { @@ -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/PathSumII.swift b/Tree/PathSumII.swift index f960f88f..c8defe17 100644 --- a/Tree/PathSumII.swift +++ b/Tree/PathSumII.swift @@ -4,7 +4,7 @@ * * Note: Structs in Swift are passed by value. Classes are passed by reference. Array and Dictionaryin Swift * are implemented as structs. In order to pass the array by reference, use inout to declare the variable. * - * Time Complexity: O(n), Space Complexity: O(1) + * Time Complexity: O(n), Space Complexity: O(n) * * Definition for a binary tree node. * public class TreeNode { @@ -20,30 +20,29 @@ */ class PathSumII { - func pathSum(root: TreeNode?, _ sum: Int) -> [[Int]] { - var res = [[Int]]() - var list = [Int]() - - _dfs(&res, &list, root, sum) - - return res + func pathSum(_ root: TreeNode?, _ sum: Int) -> [[Int]] { + var paths = [[Int]](), path = [Int]() + + dfs(root, sum, &paths, &path) + + return paths } - - private func _dfs(inout res: [[Int]], inout _ list:[Int], _ root: TreeNode?, _ sum: Int) { - guard let root = root else { + + fileprivate func dfs(_ root: TreeNode?, _ sum: Int, _ paths: inout [[Int]], _ path: inout [Int]) { + guard let root = root else { return } - if root.left == nil && root.right == nil && root.val == sum { - list.append(root.val) - res.append(list) + path.append(root.val) + + if root.val == sum && root.left == nil && root.right == nil { + paths.append(path) return } - - list.append(root.val) - var dupListLeft = list - var dupListRight = list - _dfs(&res, &dupListLeft, root.left, sum - root.val) - _dfs(&res, &dupListRight, root.right, sum - root.val) + + var pathLeft = path, pathRight = path + + dfs(root.left, sum - root.val, &paths, &pathLeft) + dfs(root.right, sum - root.val, &paths, &pathRight) } } \ No newline at end of file diff --git a/Tree/PathSumIII.swift b/Tree/PathSumIII.swift new file mode 100644 index 00000000..b0e16b88 --- /dev/null +++ b/Tree/PathSumIII.swift @@ -0,0 +1,48 @@ +/** + * Question Link: https://leetcode.com/problems/path-sum-iii/ + * Primary idea: Get path number of every node as root while iterating the tree + * Time Complexity: O(n^2), 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 PathSumIII { + func pathSum(_ root: TreeNode?, _ sum: Int) -> Int { + guard let root = root else { + return 0 + } + + var res = totalPaths(root, sum) + + let left = pathSum(root.left, sum) + let right = pathSum(root.right, sum) + + return res + left + right + } + + func totalPaths(_ root: TreeNode?, _ sum: Int) -> Int { + guard let root = root else { + return 0 + } + + var res = 0 + if sum == root.val { + res += 1 + } + + res += totalPaths(root.left, sum - root.val) + res += totalPaths(root.right, sum - root.val) + + return res + } +} \ No newline at end of file diff --git a/Tree/RecoverBinarySearchTree.swift b/Tree/RecoverBinarySearchTree.swift new file mode 100644 index 00000000..5ced05bd --- /dev/null +++ b/Tree/RecoverBinarySearchTree.swift @@ -0,0 +1,92 @@ +/** + * Question Link: https://leetcode.com/problems/recover-binary-search-tree/ + * Primary idea: Morris Traversal (In-order) + * The key is to construct the connection between child & parent + * 1) If cur.left == nil: + * - Output cur.val + * - Set cur = cur.right + * 2) If cur.left != nil: + * - Find the precursor of cur, precursor + * i. If precursor.right == nil: + * - Set precursor.right = cur + * - Set cur = cur.left + * ii. If precursor.right != nil (which means precursor.right === cur): + * - Set precursor.right = nil (Recover the structure of original tree) + * - Output cur.val + * - Set cur = cur.right + * 3) Repeat 1) & 2) until cur == nil + * Time Complexity: O(n), Space Complexity: O(1) + * Recommand Reading: http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html + * + * 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 RecoverBinarySearchTree { + func recoverTree(_ root: TreeNode?) { + var pre: TreeNode? // Store the pre-node in the sorted list + var first: TreeNode? + var second: TreeNode? + + // Morris Traversal + var cur = root + var precursor: TreeNode? + while cur != nil { + // 2) If cur.left != nil: + if cur!.left != nil { + // Find the precursor of cur + precursor = cur!.left + while precursor!.right != nil && precursor!.right !== cur { + precursor = precursor!.right + } + // 2)ii. If the connection already existed + if precursor!.right === cur { + // First time we meet pre.val >= cur.val must be the first node + // But the second node need to be the last time we meet pre.val >= cur.val + // e.g 1, 4, 3, 5, 6 v.s 1, 5, 4, 3, 6 + if pre != nil && pre!.val >= cur!.val { + if first == nil { + first = pre + second = cur + } else { + second = cur + } + } + pre = cur! + precursor!.right = nil + cur = cur!.right + // 2)i. Construct the connection + } else { + precursor!.right = cur + cur = cur!.left + } + // 1) If cur.left == nil: + } else { + if pre != nil && pre!.val >= cur!.val { + if first == nil { + first = pre + second = cur + } else { + second = cur + } + } + pre = cur! + cur = cur!.right + } + } + + // Swap the 2 nodes + if first != nil && second != nil { + swap(&first!.val, &second!.val) + } + } +} diff --git a/Tree/SameTree.swift b/Tree/SameTree.swift index 3e6d3251..e4b77127 100644 --- a/Tree/SameTree.swift +++ b/Tree/SameTree.swift @@ -1,7 +1,7 @@ /** * Question Link: https://leetcode.com/problems/same-tree/ * Primary idea: recursion - * Time Complexity: O(n), Space Complexity: O(1) + * Time Complexity: O(n), Space Complexity: O(n) * * Copyright © 2016 YiGu. All rights reserved. * @@ -19,14 +19,18 @@ */ class SameTree { - func isSameTree(p: TreeNode?, _ q: TreeNode?) -> Bool { - if p == nil && q == nil { - return true + func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool { + guard let p = p else { + return q == nil } - if p == nil || q == nil || p!.val != q!.val { + guard let q = q else { + return p == nil + } + + if p.val != q.val { return false } - - return isSameTree(p!.left, q!.left) && isSameTree(p!.right, q!.right) + + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right) } } \ 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/SumLeftLeaves.swift b/Tree/SumLeftLeaves.swift new file mode 100644 index 00000000..4418af39 --- /dev/null +++ b/Tree/SumLeftLeaves.swift @@ -0,0 +1,43 @@ +/** + * Question Link: https://leetcode.com/problems/sum-of-left-leaves/ + * Primary idea: Recursion. Go to left and right and add to res if it is left leaf. + * 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 SumLeftLeaves { + func sumOfLeftLeaves(_ root: TreeNode?) -> Int { + guard let root = root else { + return 0 + } + + var res = 0 + helper(root.left, true, &res) + helper(root.right, false, &res) + + return res + } + + private func helper(_ node: TreeNode?, _ isLeft: Bool, _ res: inout Int) { + guard let node = node else { + return + } + if node.left == nil && node.right == nil && isLeft { + res += node.val + } + + helper(node.left, true, &res) + helper(node.right, false, &res) + } +} \ No newline at end of file diff --git a/Tree/SymmetricTree.swift b/Tree/SymmetricTree.swift index 7be2dc60..a2f1037e 100644 --- a/Tree/SymmetricTree.swift +++ b/Tree/SymmetricTree.swift @@ -1,7 +1,7 @@ /** * Question Link: https://leetcode.com/problems/symmetric-tree/ * Primary idea: recursion - * Time Complexity: O(n), Space Complexity: O(1) + * Time Complexity: O(n), Space Complexity: O(n) * * Definition for a binary tree node. * public class TreeNode { @@ -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/UniqueBinarySearchTrees.swift b/Tree/UniqueBinarySearchTrees.swift new file mode 100644 index 00000000..ce3eb5d3 --- /dev/null +++ b/Tree/UniqueBinarySearchTrees.swift @@ -0,0 +1,37 @@ +/** + * Question Link: https://leetcode.com/problems/unique-binary-search-trees/ + * Primary idea: Dynamic programming, for each node as root, dp[i] += dp[j] * dp[i - j - 1] + * + * Time Complexity: O(n^2), 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 UniqueBinarySearchTrees { + func numTrees(_ n: Int) -> Int { + guard n > 1 else { + return 1 + } + + var dp = Array(repeating: 0, count: n + 1) + dp[0] = 1 + + for i in 1...n { + for j in 0.. Bool { + private func _helper(_ node: TreeNode?, _ min: Int?, _ max: Int?) -> Bool { guard let node = node else { return true } - if min != nil && node.val <= min { + if let min = min, node.val <= min { return false } - if max != nil && node.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 +} diff --git a/logo.png b/logo.png new file mode 100644 index 00000000..164b28df Binary files /dev/null and b/logo.png differ