Skip to content

Commit 3b7dc01

Browse files
committed
Merge pull request kodecocodes#19 from flCer/master
Add merge sort
2 parents 776269f + c78800d commit 3b7dc01

File tree

6 files changed

+257
-1
lines changed

6 files changed

+257
-1
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
let array = [2, 1, 5, 4, 9]
2+
3+
func mergeSort(array: [Int]) -> [Int] {
4+
guard array.count > 1 else { return array } // 1
5+
let middleIndex = array.count / 2 // 2
6+
7+
let leftArray = mergeSort(Array(array[0..<middleIndex])) // 3
8+
9+
let rightArray = mergeSort(Array(array[middleIndex..<array.count])) // 4
10+
11+
return merge(leftPile: leftArray, rightPile: rightArray) // 5
12+
}
13+
14+
func merge(leftPile leftPile: [Int], rightPile: [Int]) -> [Int] {
15+
// 1
16+
var leftIndex = 0
17+
var rightIndex = 0
18+
19+
// 2
20+
var orderedPile = [Int]()
21+
22+
// 3
23+
while leftIndex < leftPile.count && rightIndex < rightPile.count {
24+
if leftPile[leftIndex] < rightPile[rightIndex] {
25+
orderedPile.append(leftPile[leftIndex])
26+
leftIndex += 1
27+
} else if leftPile[leftIndex] > rightPile[rightIndex] {
28+
orderedPile.append(rightPile[rightIndex])
29+
rightIndex += 1
30+
} else {
31+
orderedPile.append(leftPile[leftIndex])
32+
leftIndex += 1
33+
orderedPile.append(rightPile[rightIndex])
34+
rightIndex += 1
35+
}
36+
}
37+
38+
// 4
39+
while leftIndex < leftPile.count {
40+
orderedPile.append(leftPile[leftIndex])
41+
leftIndex += 1
42+
}
43+
44+
while rightIndex < rightPile.count {
45+
orderedPile.append(rightPile[rightIndex])
46+
rightIndex += 1
47+
}
48+
49+
return orderedPile
50+
}
51+
52+
let sortedArray = mergeSort(array)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='ios'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Timeline
3+
version = "3.0">
4+
<TimelineItems>
5+
</TimelineItems>
6+
</Timeline>

Mergesort/Mergesort.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// Mergesort.swift
3+
//
4+
//
5+
// Created by Kelvin Lau on 2016-02-03.
6+
//
7+
//
8+
9+
func mergeSort(array: [Int]) -> [Int] {
10+
guard array.count > 1 else { return array } // 1
11+
let middleIndex = array.count / 2 // 2
12+
13+
let leftArray = mergeSort(Array(array[0..<middleIndex])) // 3
14+
15+
let rightArray = mergeSort(Array(array[middleIndex..<array.count])) // 4
16+
17+
return merge(leftPile: leftArray, rightPile: rightArray) // 5
18+
}
19+
20+
func merge(leftPile leftPile: [Int], rightPile: [Int]) -> [Int] {
21+
// 1
22+
var leftIndex = 0
23+
var rightIndex = 0
24+
25+
// 2
26+
var orderedPile = [Int]()
27+
28+
// 3
29+
while leftIndex < leftPile.count && rightIndex < rightPile.count {
30+
if leftPile[leftIndex] < rightPile[rightIndex] {
31+
orderedPile.append(leftPile[leftIndex])
32+
leftIndex += 1
33+
} else if leftPile[leftIndex] > rightPile[rightIndex] {
34+
orderedPile.append(rightPile[rightIndex])
35+
rightIndex += 1
36+
} else {
37+
orderedPile.append(leftPile[leftIndex])
38+
leftIndex += 1
39+
orderedPile.append(rightPile[rightIndex])
40+
rightIndex += 1
41+
}
42+
}
43+
44+
// 4
45+
while leftIndex < leftPile.count {
46+
orderedPile.append(leftPile[leftIndex])
47+
leftIndex += 1
48+
}
49+
50+
while rightIndex < rightPile.count {
51+
orderedPile.append(rightPile[rightIndex])
52+
rightIndex += 1
53+
}
54+
55+
return orderedPile
56+
}

Mergesort/README.markdown

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Mergesort
2+
3+
Goal: Sort an array from low to high (or high to low)
4+
5+
Invented in 1945 by John von Neumann, mergesort is a fairly efficient sorting algorithm with a best, worst, and average time complexity of **O(n log n)**. The idea behind Mergesort
6+
is to **divide and conquer**; To divide a big problem into smaller problems and solving many small problems instead of solving a big one. I think of mergesort as **split first** and **merge after**.
7+
8+
Assume you're given an array of *n* numbers and you need to put them in the right order. The merge sort algorithm works as follows:
9+
10+
- Put the numbers in a pile. The pile is unsorted.
11+
- Split the pile into 2. Now you have **two unsorted piles** of numbers.
12+
- Keep splitting the resulting piles until you can't anymore; In the end, you will have *n* piles with 1 number in each pile
13+
- Begin to **merge** the piles together by sequentially pairing a pile with another pile. During each merge, you want to sort the contents in order
14+
15+
## An example
16+
17+
### Splitting
18+
19+
Let's say the numbers to sort are `[2, 1, 5 , 4, 9]`. This is your unsorted pile. Our goal is to keep splitting the pile until you can't anymore.
20+
21+
Split the array into two halves - `[2, 1,]` and `[5, 4, 9]`. Can you keep splitting them? Yes you can!
22+
23+
Focus on the left pile. `[2, 1]` will split into `[2]` and `[1]`. Can you keep splitting them? No. Time to check the other pile.
24+
25+
`[5, 4, 9]` splits to `[5]` and `[4, 9]`. Unsurprisingly, `[5]` can't split into anymore, but `[4, 9]` splits into `[4]` and `[9]`.
26+
27+
The splitting process ends with the following piles:
28+
29+
`[2]` `[1]` `[5]` `[4]` `[9]`
30+
31+
### Merging
32+
33+
Now that you've split the array, you'll **merge** the piles together **while sorting them**. Remember, the idea is to solve many small problems rather than a big one. For each merge iteration you'll only be concerned at merging one pile with another.
34+
35+
Given `[2]` `[1]` `[5]` `[4]` `[9]`, the first pass will result in `[1, 2]` and `[4, 5]` and `[9]`. Since `[9]` is the odd one out, you can't merge it with anything during this pass.
36+
37+
The next pass will merge `[1, 2]` and `[4, 5]` together. This results in `[1, 2, 4, 5]`, with the `[9]` left out again since it's the odd one out.
38+
39+
Since you're left with 2 piles, `[9]` finally gets it's chance to merge, resulting in the sorted array `[1, 2, 4, 5, 9]`.
40+
41+
### Top Down Implementation
42+
43+
Based off of the above example, here's what mergesort may look like:
44+
45+
```swift
46+
func mergeSort(array: [Int]) -> [Int] {
47+
guard array.count > 1 else { return array } // 1
48+
let middleIndex = array.count / 2 // 2
49+
50+
let leftArray = mergeSort(Array(array[0..<middleIndex])) // 3
51+
52+
let rightArray = mergeSort(Array(array[middleIndex..<array.count])) // 4
53+
54+
return merge(leftPile: leftArray, rightPile: rightArray) // 5
55+
}
56+
```
57+
58+
A step-by-step explanation of how the code works:
59+
60+
1. If the array is empty or only contains a single element, there's no way to split it into smaller pieces. You'll just return the array.
61+
62+
2. Find the middle index.
63+
64+
3. Using the middle index from the previous step, recursively split the left side of the resulting arrays.
65+
66+
4. Using the middle index, recursively split the right side of the resulting arrays.
67+
68+
5. Finally, merge all the values together, making sure that it's always sorted
69+
70+
71+
Here's the merging algorithm:
72+
73+
```swift
74+
func merge(leftPile leftPile: [Int], rightPile: [Int]) -> [Int] {
75+
// 1
76+
var leftIndex = 0
77+
var rightIndex = 0
78+
79+
// 2
80+
var orderedPile = [Int]()
81+
82+
// 3
83+
while leftIndex < leftPile.count && rightIndex < rightPile.count {
84+
if leftPile[leftIndex] < rightPile[rightIndex] {
85+
orderedPile.append(leftPile[leftIndex])
86+
leftIndex += 1
87+
} else if leftPile[leftIndex] > rightPile[rightIndex] {
88+
orderedPile.append(rightPile[rightIndex])
89+
rightIndex += 1
90+
} else {
91+
orderedPile.append(leftPile[leftIndex])
92+
leftIndex += 1
93+
orderedPile.append(rightPile[rightIndex])
94+
rightIndex += 1
95+
}
96+
}
97+
98+
// 4
99+
while leftIndex < leftPile.count {
100+
orderedPile.append(leftPile[leftIndex])
101+
leftIndex += 1
102+
}
103+
104+
while rightIndex < rightPile.count {
105+
orderedPile.append(rightPile[rightIndex])
106+
rightIndex += 1
107+
}
108+
109+
return orderedPile
110+
}
111+
```
112+
113+
This method is quite straightforward:
114+
115+
1. You need 2 indexes to keep track of your progress for the two arrays while merging.
116+
117+
2. This is the merged array. It's empty right now, but you'll build it up in subsequent steps by appending elements from the other arrays.
118+
119+
3. This while loop will compare the elements from the left and right sides, and append them to the `orderedPile` while making sure that the result stays in order.
120+
121+
4. If control exits from the previous while loop, it means that either `leftPile` or `rightPile` has it's contents completely merged into the `orderedPile`. At this point, you no longer need to do comparisons. Just append the rest of the contents of the other array until there's no more to append.
122+
123+
124+
Most implementations of Mergesort produce a *stable* sort.
125+
126+
*"A sort is stable when elements that have identical sort keys remain in the same relative order after sorting. This is not important for simple values such as numbers or strings, but it is important when sorting more complex objects. In the example above, if two objects have the same `priority`, regardless of the values of their other properties, those two objects don't get swapped around." Matthijs Hollemans*
127+
128+
### Performance
129+
130+
The speed of mergesort is dependent on the size of the array it needs to sort. Whether or not the initial array is sorted already doesn't affect the speed of the sort since you'll be doing the same amount splits and comparisons irregardless of the initial order of the elements.
131+
132+
Therefore, the time complexity for the best, worst, and average case will always be O(n log n).
133+
134+
## See Also
135+
136+
See also [Wikipedia](https://en.wikipedia.org/wiki/Merge_sort)
137+
138+
*Written by Kelvin Lau*

README.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Basic sorts:
7272
Fast sorts:
7373

7474
- [Quicksort](Quicksort/)
75-
- Merge Sort
75+
- [Merge Sort](Mergesort/)
7676
- Heap Sort
7777

7878
Special-purpose sorts:

0 commit comments

Comments
 (0)