From 870706d976d39f9ff9d3f42b4583b71c86ffc6b9 Mon Sep 17 00:00:00 2001 From: Viktor Lesyk Date: Sat, 14 Sep 2019 14:19:56 +0200 Subject: [PATCH] Add: Merge Sort --- .../MergeSort.playground/Contents.swift | 41 +++++ .../contents.xcplayground | 4 + Algorithms/Sorting/MergeSort/MergeSort.ts | 65 +++++++ README.md | 158 ++++++++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 Algorithms/Sorting/MergeSort/MergeSort.playground/Contents.swift create mode 100644 Algorithms/Sorting/MergeSort/MergeSort.playground/contents.xcplayground create mode 100644 Algorithms/Sorting/MergeSort/MergeSort.ts diff --git a/Algorithms/Sorting/MergeSort/MergeSort.playground/Contents.swift b/Algorithms/Sorting/MergeSort/MergeSort.playground/Contents.swift new file mode 100644 index 0000000..ca2b877 --- /dev/null +++ b/Algorithms/Sorting/MergeSort/MergeSort.playground/Contents.swift @@ -0,0 +1,41 @@ +func mergeSort(numbers: [Int]) -> [Int] { + // If only one element - already sorted. + if numbers.count == 1 { + return numbers + } + + // First, divide the list into equal-sized sublists + // consisting of the first half and second half of the list. + let iMiddle = numbers.count/2 + let left = mergeSort(numbers: Array(numbers[0.. [Int] { + var leftIndex = 0 + var rightIndex = 0 + var ordered: [Int] = [] + + while leftIndex < left.count && rightIndex < right.count { + if left[leftIndex] < right[rightIndex] { + ordered.append(left[leftIndex]) + leftIndex += 1 + } else { + ordered.append(right[rightIndex]) + rightIndex += 1 + } + } + + // Going through leftovers + ordered += Array(left[leftIndex.. + + + \ No newline at end of file diff --git a/Algorithms/Sorting/MergeSort/MergeSort.ts b/Algorithms/Sorting/MergeSort/MergeSort.ts new file mode 100644 index 0000000..e87f79d --- /dev/null +++ b/Algorithms/Sorting/MergeSort/MergeSort.ts @@ -0,0 +1,65 @@ +function mergeSort(numbers: number[]): number[] { + + // If only one element - already sorted. + if (numbers.length === 1) { + return numbers; + } + + // First, divide the list into equal-sized sublists + // consisting of the first half and second half of the list. + const iMiddle = Math.floor(numbers.length/2); + + const leftArray = []; + numbers.forEach((el, index) => { + if (0 <= index && index < iMiddle) { + leftArray.push(el); + } + }); + + const rightArray = []; + numbers.forEach((el, index) => { + if (iMiddle <= index && index <= numbers.length) { + rightArray.push(el); + } + }); + + const left = mergeSort(leftArray); + const right = mergeSort(rightArray); + + // Recursively sort both sublists. + return compareAndMerge(left, right); +} + +function compareAndMerge(left: number[], right: number[]): number[] { + let ordered = []; + let leftIndex = 0; + let rightIndex = 0; + + while (leftIndex < left.length && rightIndex < right.length) { + if (left[leftIndex] < right[rightIndex]) { + ordered.push(left[leftIndex]); + leftIndex++; + } else { + ordered.push(right[rightIndex]); + rightIndex++; + } + } + + // Going through leftovers + left.forEach((el, index) => { + if (leftIndex <= index && index <= left.length) { + ordered.push(el); + } + }); + + right.forEach((el, index) => { + if (rightIndex <= index && index <= right.length) { + ordered.push(el); + } + }); + + return ordered; +} + +const unsortedArrayOfNumbers = [5, 15, 14, 1, 26, 0, 99]; +console.log(mergeSort(unsortedArrayOfNumbers)); \ No newline at end of file diff --git a/README.md b/README.md index c10b583..38173a3 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,164 @@ console.log(insertionSort(unsortedArray)); +## ⚥ Merge Sort + +![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif "Merge Sort") + +
+ +Conceptually, a merge sort works as follows: + +- Divide the unsorted list into n sublists, each containing one element (a list of one element is considered sorted). +- Repeatedly merge sublists to produce new sorted sublists until there is only one sublist remaining. This will be the sorted list. + +[Wikipedia says](https://en.wikipedia.org/wiki/Merge_sort): +> Merge sort takes advantage of the ease of merging already sorted lists into a new sorted list. It starts by comparing every two elements (i.e., 1 with 2, then 3 with 4...) and swapping them if the first should come after the second. It then merges each of the resulting lists of two into lists of four, then merges those lists of four, and so on; until at last two lists are merged into the final sorted list. Of the algorithms described here, this is the first that scales well to very large lists, because its worst-case running time is O(n log n). It is also easily applied to lists, not only arrays, as it only requires sequential access, not random access. However, it has additional O(n) space complexity, and involves a large number of copies in simple implementations. + +| Algorithm | Time Complexity | | | Space Complexity | +| ------------ |----------------:| ------------:|------------:|-----------------:| +| | Best | Average | Worst | Worst | +| Merge Sort | Θ(n log(n)) | Θ(n log(n)) | O(n log(n)) | O(n) | + +### Swift + +**Example:** +```swift +func mergeSort(numbers: [Int]) -> [Int] { + // If only one element - already sorted. + if numbers.count == 1 { + return numbers + } + + // First, divide the list into equal-sized sublists + // consisting of the first half and second half of the list. + let iMiddle = numbers.count/2 + let left = mergeSort(numbers: Array(numbers[0.. [Int] { + var leftIndex = 0 + var rightIndex = 0 + var ordered: [Int] = [] + + while leftIndex < left.count && rightIndex < right.count { + if left[leftIndex] < right[rightIndex] { + ordered.append(left[leftIndex]) + leftIndex += 1 + } else { + ordered.append(right[rightIndex]) + rightIndex += 1 + } + } + + // Going through leftovers + ordered += Array(left[leftIndex.. { + if (0 <= index && index < iMiddle) { + leftArray.push(el); + } + }); + + const rightArray = []; + numbers.forEach((el, index) => { + if (iMiddle <= index && index <= numbers.length) { + rightArray.push(el); + } + }); + + const left = mergeSort(leftArray); + const right = mergeSort(rightArray); + + // Recursively sort both sublists. + return compareAndMerge(left, right); +} + +function compareAndMerge(left: number[], right: number[]): number[] { + let ordered = []; + let leftIndex = 0; + let rightIndex = 0; + + while (leftIndex < left.length && rightIndex < right.length) { + if (left[leftIndex] < right[rightIndex]) { + ordered.push(left[leftIndex]); + leftIndex++; + } else { + ordered.push(right[rightIndex]); + rightIndex++; + } + } + + // Going through leftovers + left.forEach((el, index) => { + if (leftIndex <= index && index <= left.length) { + ordered.push(el); + } + }); + + right.forEach((el, index) => { + if (rightIndex <= index && index <= right.length) { + ordered.push(el); + } + }); + + return ordered; +} + +const unsortedArrayOfNumbers = [5, 15, 14, 1, 26, 0, 99]; +console.log(mergeSort(unsortedArrayOfNumbers)); +``` + +#### Output: +``` +[ 0, 1, 5, 14, 15, 26, 99 ] +``` + +
+``` + +#### Output: +``` +[ 0, 1, 5, 14, 15, 26, 99 ] +``` + + + ## 🔘 Selection Sort ![Selection Sort](https://upload.wikimedia.org/wikipedia/commons/9/94/Selection-Sort-Animation.gif "Selection Sort")