diff --git a/3Sum and 4Sum/index.html b/3Sum and 4Sum/index.html new file mode 100644 index 000000000..3642cca80 --- /dev/null +++ b/3Sum and 4Sum/index.html @@ -0,0 +1,129 @@ + +
+3Sum and 4Sum are extensions of a popular algorithm question, the 2Sum.
+++Given an array of integers, find all subsets of the array with 3 values where the 3 values sum up to a target number.
+Note: The solution subsets must not contain duplicate triplets.
+For example, given the array [-1, 0, 1, 2, -1, -4], and the target 0:
+
+The solution set is: [[-1, 0, 1], [-1, -1, 2]] // The two -1 values in the array are considered to be distinct
There are 2 key procedures in solving this algorithm. Sorting the array, and avoiding duplicates.
+Sorting your input array allows for powerful assumptions:
+You'll make use of these two rules to create an efficient algorithm.
+Since you pre-sort the array, duplicates will be adjacent to each other. You just need to skip over duplicates by comparing adjacent values:
+extension Collection where Element: Equatable {
+
+ /// In a sorted collection, replaces the given index with a successor mapping to a unique element.
+ ///
+ /// - Parameter index: A valid index of the collection. `index` must be less than `endIndex`
+ func formUniqueIndex(after index: inout Index) {
+ var prev = index
+ repeat {
+ prev = index
+ formIndex(after: &index)
+ } while index < endIndex && self[prev] == self[index]
+ }
+}
A similar implementation is used to get the unique index before a given index:
+extension BidirectionalCollection where Element: Equatable {
+
+ /// In a sorted collection, replaces the given index with a predecessor that maps to a unique element.
+ ///
+ /// - Parameter index: A valid index of the collection. `index` must be greater than `startIndex`.
+ func formUniqueIndex(before index: inout Index) {
+ var prev = index
+ repeat {
+ prev = index
+ formIndex(before: &index)
+ } while index > startIndex && self[prev] == self[index]
+ }
+}
You'll keep track of 3 indices to represent the 3 numbers. The sum at any given moment is array[l] + array[m] + array[r]
:
m -> <- r
+[-4, -1, -1, 0, 1, 2]
+ l
The premise is quite straightforward (given that you're familiar with 2Sum). You'll iterate l
through the array. For every iteration, you also apply the 2Sum algorithm to elements after l
. You'll check the sum every time you moving the indices to check if you found match. Here's the algorithm:
func threeSum<T: BidirectionalCollection>(_ collection: T, target: T.Element) -> [[T.Element]] where T.Element: Numeric & Comparable {
+ let sorted = collection.sorted()
+ var ret: [[T.Element]] = []
+ var l = sorted.startIndex
+
+ ThreeSum: while l < sorted.endIndex { defer { sorted.formUniqueIndex(after: &l) }
+ var m = sorted.index(after: l)
+ var r = sorted.index(before: sorted.endIndex)
+
+ TwoSum: while m < r && r < sorted.endIndex {
+ let sum = sorted[l] + sorted[m] + sorted[r]
+ if sum == target {
+ ret.append([sorted[l], sorted[m], sorted[r]])
+ sorted.formUniqueIndex(after: &m)
+ sorted.formUniqueIndex(before: &r)
+ } else if sum < target {
+ sorted.formUniqueIndex(after: &m)
+ } else {
+ sorted.formUniqueIndex(before: &r)
+ }
+ }
+ }
+
+ return ret
+}
++Given an array S of n integers, find all subsets of the array with 4 values where the 4 values sum up to a target number.
+Note: The solution set must not contain duplicate quadruplets.
+
Foursum is a very straightforward extension to the threeSum algorithm. In threeSum, you kept track of 3 indices:
+ m -> <- r
+[-4, -1, -1, 0, 1, 2]
+ l
For fourSum, you'll keep track of 4:
+ mr -> <- r
+[-4, -1, -1, 0, 1, 2]
+ l ml ->
Here's the code for it (notice it is very similar to 3Sum):
+func fourSum<T: BidirectionalCollection>(_ collection: T, target: T.Element) -> [[T.Element]] where T.Element: Numeric & Comparable {
+ let sorted = collection.sorted()
+ var ret: [[T.Element]] = []
+
+ var l = sorted.startIndex
+ FourSum: while l < sorted.endIndex { defer { sorted.formUniqueIndex(after: &l) }
+ var ml = sorted.index(after: l)
+
+ ThreeSum: while ml < sorted.endIndex { defer { sorted.formUniqueIndex(after: &ml) }
+ var mr = sorted.index(after: ml)
+ var r = sorted.index(before: sorted.endIndex)
+
+ TwoSum: while mr < r && r < sorted.endIndex {
+ let sum = sorted[l] + sorted[ml] + sorted[mr] + sorted[r]
+ if sum == target {
+ ret.append([sorted[l], sorted[ml], sorted[mr], sorted[r]])
+ sorted.formUniqueIndex(after: &mr)
+ sorted.formUniqueIndex(before: &r)
+ } else if sum < target {
+ sorted.formUniqueIndex(after: &mr)
+ } else {
+ sorted.formUniqueIndex(before: &r)
+ }
+ }
+ }
+ }
+ return ret
+}
Written for the Swift Algorithm Club by Kai Chen and Kelvin Lau