{"id":"recursion-merge-sort","slug":"merge-sort","title":"Divide and conquer: merge sort","text":"Binary search splits a problem in half and solves one half. Many algorithms split a problem into parts, solve each part the same way, and combine the answers: that's called **divide and conquer**. Merge sort is the classic example. It sorts an array like this:\n\n1. An array of 0 or 1 values is already sorted (the base case).\n2. Otherwise, split it into two halves, and sort each half, with merge sort (the recursive calls).\n3. Merge the two sorted halves into one sorted array.\n\n### Merging two sorted arrays\n\nThe work is in step 3. Two sorted arrays merge like two sorted piles of exam papers: the paper that goes next is always the smaller of the two on top of the piles. Take it, and look again. When one pile is empty, the rest of the other pile follows as it is.\n\n```java\nimport java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        int[] morning = {3, 8, 9, 15};\n        int[] evening = {1, 8, 12};\n        System.out.println(Arrays.toString(merge(morning, evening)));\n    }\n\n    public static int[] merge(int[] first, int[] second) {\n        int[] merged = new int[first.length + second.length];\n        int i = 0;\n        int j = 0;\n        int k = 0;\n        while (i < first.length && j < second.length) {\n            if (first[i] <= second[j]) {\n                merged[k] = first[i];\n                i++;\n            } else {\n                merged[k] = second[j];\n                j++;\n            }\n            k++;\n        }\n        while (i < first.length) {\n            merged[k] = first[i];\n            i++;\n            k++;\n        }\n        while (j < second.length) {\n            merged[k] = second[j];\n            j++;\n            k++;\n        }\n        return merged;\n    }\n}\n```\n\n```output\n[1, 3, 8, 8, 9, 12, 15]\n```\n\n`i` is the place in `first`, `j` the place in `second`, and `k` the place in `merged`. Each round of the first loop copies one value, so merging takes one round per value: merging is quick. Merging doesn't need recursion; the loops are the simplest way to write it.\n\n### The recursive sort\n\n`Arrays.copyOfRange(values, from, to)` makes a new array of the values from index `from` up to (not including) `to`. With it, the sort is a few lines:\n\n```java\npublic static int[] mergeSort(int[] values) {\n    if (values.length <= 1) {\n        return values;\n    }\n    int middle = values.length / 2;\n    int[] left = mergeSort(Arrays.copyOfRange(values, 0, middle));\n    int[] right = mergeSort(Arrays.copyOfRange(values, middle, values.length));\n    return merge(left, right);\n}\n```\n\nThe halves are sorted by the same method, which splits them again, until the parts have one value each. Then the merges put them together again, in order. Sorting `{5, 2, 7, 1}` goes like this:\n\n```text\nmergeSort [5, 2, 7, 1]\n    mergeSort [5, 2]\n        mergeSort [5]  returns [5]\n        mergeSort [2]  returns [2]\n        merge          returns [2, 5]\n    mergeSort [7, 1]\n        mergeSort [7]  returns [7]\n        mergeSort [1]  returns [1]\n        merge          returns [1, 7]\n    merge              returns [1, 2, 5, 7]\n```\n\n### How much work it does\n\nHalving an array of 1,000 values again and again gets down to single values after 10 levels, since 2 to the 10th is 1,024. At each level, the merges together copy every value once. So merge sort does about 1,000 times 10 steps, where selection sort (module 28) compares about 1,000 times 1,000 divided by 2, close to half a million times. This program counts the comparisons that `merge` makes while sorting 1,000 numbers that start in reverse order, and the comparisons selection sort makes on any 1,000 values:\n\n```java\nimport java.util.Arrays;\n\npublic class Main {\n    private static int comparisons = 0;\n\n    public static void main(String[] args) {\n        int[] values = new int[1000];\n        for (int i = 0; i < values.length; i++) {\n            values[i] = 1000 - i;\n        }\n        int[] sorted = mergeSort(values);\n        System.out.println(sorted[0] + \" \" + sorted[999]);\n        System.out.println(\"Merge sort: \" + comparisons + \" comparisons\");\n        System.out.println(\"Selection sort: \" + (1000 * 999 / 2) + \" comparisons\");\n    }\n\n    public static int[] mergeSort(int[] values) {\n        if (values.length <= 1) {\n            return values;\n        }\n        int middle = values.length / 2;\n        int[] left = mergeSort(Arrays.copyOfRange(values, 0, middle));\n        int[] right = mergeSort(Arrays.copyOfRange(values, middle, values.length));\n        return merge(left, right);\n    }\n\n    public static int[] merge(int[] first, int[] second) {\n        int[] merged = new int[first.length + second.length];\n        int i = 0;\n        int j = 0;\n        int k = 0;\n        while (i < first.length && j < second.length) {\n            comparisons++;\n            if (first[i] <= second[j]) {\n                merged[k] = first[i];\n                i++;\n            } else {\n                merged[k] = second[j];\n                j++;\n            }\n            k++;\n        }\n        while (i < first.length) {\n            merged[k] = first[i];\n            i++;\n            k++;\n        }\n        while (j < second.length) {\n            merged[k] = second[j];\n            j++;\n            k++;\n        }\n        return merged;\n    }\n}\n```\n\n```output\n1 1000\nMerge sort: 5044 comparisons\nSelection sort: 499500 comparisons\n```\n\n`comparisons` is declared in the class, outside the methods, with `static`: a static variable belongs to the class itself, not to one call, so every call of `merge` adds to the same counter (a variable declared inside `merge` would start again at 0 in each call).\n\nThe difference grows with the size: for a million values, merge sort compares about 20 million times and selection sort about 500 billion times. Merge sort needs extra arrays for the halves, which selection sort doesn't.\n\n### Stable sorting\n\n`merge` takes from `first` when the two values are equal (`<=`, not `<`). So values that compare as equal keep the order they had: a sort that does this is **stable**. It matters when you sort objects by one property, such as words by their length: words of the same length stay in the order they were in. Java's `Collections.sort` is a merge sort too (a refined one), and its documentation promises that it is stable.\n","task":"Two park rangers measured trees, and each wrote the heights, in meters, in a sorted array. Write the method `merge(int[] first, int[] second)`, which returns a new array of all the heights of both, sorted, as in this step. Don't sort the result with `Arrays.sort`: merge. The check runs:\n\n```java\nint[] north = {4, 9, 17, 22};\nint[] south = {6, 9, 30};\nSystem.out.println(Arrays.toString(merge(north, south)));\n```\n\nand expects:\n\n```\n[4, 6, 9, 9, 17, 22, 30]\n```\n","kind":"code","seed":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        int[] north = {4, 9, 17, 22};\n        int[] south = {6, 9, 30};\n        System.out.println(Arrays.toString(merge(north, south)));\n    }\n\n    public static int[] merge(int[] first, int[] second) {\n        int[] merged = new int[first.length + second.length];\n        return merged;\n    }\n}\n","solution":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        int[] north = {4, 9, 17, 22};\n        int[] south = {6, 9, 30};\n        System.out.println(Arrays.toString(merge(north, south)));\n    }\n\n    public static int[] merge(int[] first, int[] second) {\n        int[] merged = new int[first.length + second.length];\n        int i = 0;\n        int j = 0;\n        int k = 0;\n        while (i < first.length && j < second.length) {\n            if (first[i] <= second[j]) {\n                merged[k] = first[i];\n                i++;\n            } else {\n                merged[k] = second[j];\n                j++;\n            }\n            k++;\n        }\n        while (i < first.length) {\n            merged[k] = first[i];\n            i++;\n            k++;\n        }\n        while (j < second.length) {\n            merged[k] = second[j];\n            j++;\n            k++;\n        }\n        return merged;\n    }\n}\n","hints":["Keep a place in each array, `i` in `first` and `j` in `second`, and one in `merged`, `k`. While both arrays have values left, copy the smaller of `first[i]` and `second[j]` and move that array's place on.","When one array runs out, copy the rest of the other one, with a loop for each array (only one of the two has anything left)."],"tests":[{"name":"Test 1","stdin":"","call":"int[] north = {4, 9, 17, 22};\nint[] south = {6, 9, 30};\nSystem.out.println(Arrays.toString(merge(north, south)));\n","expect":"[4, 6, 9, 9, 17, 22, 30]","hidden":false},{"name":"Test 2","stdin":"","call":"int[] a = {1, 2, 3};\nint[] b = {10, 20};\nSystem.out.println(Arrays.toString(merge(a, b)));\nSystem.out.println(Arrays.toString(merge(b, a)));\nint[] none = new int[0];\nSystem.out.println(Arrays.toString(merge(none, b)));\nSystem.out.println(Arrays.toString(merge(a, none)));\nSystem.out.println(Arrays.toString(merge(none, none)));\n","expect":"[1, 2, 3, 10, 20]\n[1, 2, 3, 10, 20]\n[10, 20]\n[1, 2, 3]\n[]","hidden":true},{"name":"Test 3","stdin":"","call":"int[] a = {2, 2, 5, 11, 11, 14};\nint[] b = {1, 2, 11, 12, 15, 16, 17};\nSystem.out.println(Arrays.toString(merge(a, b)));\n","expect":"[1, 2, 2, 2, 5, 11, 11, 11, 12, 14, 15, 16, 17]","hidden":true}],"require":[],"forbid":[{"pattern":"\\bsort\\s*\\(","message":"Don't sort the result: merge the two sorted arrays."}],"more":[{"task":"A paper plane contest records how far each plane flies, in centimeters, in the order the planes are thrown. Write `mergeSort(int[] values)`, which returns a new sorted array of the distances, with merge sort: an array of at most one value is returned as it is; any other array is split in two with `Arrays.copyOfRange`, each half is sorted with `mergeSort`, and the sorted halves are merged with `merge`, which is given. Don't use `Arrays.sort` or `Collections.sort`. The check runs:\n\n```java\nint[] distances = {1240, 875, 1630, 875, 1105, 690};\nSystem.out.println(Arrays.toString(mergeSort(distances)));\n```\n\nand expects:\n\n```\n[690, 875, 875, 1105, 1240, 1630]\n```\n","kind":"code","seed":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        int[] distances = {1240, 875, 1630, 875, 1105, 690};\n        System.out.println(Arrays.toString(mergeSort(distances)));\n    }\n\n    public static int[] mergeSort(int[] values) {\n        return values;\n    }\n\n    public static int[] merge(int[] first, int[] second) {\n        int[] merged = new int[first.length + second.length];\n        int i = 0;\n        int j = 0;\n        int k = 0;\n        while (i < first.length && j < second.length) {\n            if (first[i] <= second[j]) {\n                merged[k] = first[i];\n                i++;\n            } else {\n                merged[k] = second[j];\n                j++;\n            }\n            k++;\n        }\n        while (i < first.length) {\n            merged[k] = first[i];\n            i++;\n            k++;\n        }\n        while (j < second.length) {\n            merged[k] = second[j];\n            j++;\n            k++;\n        }\n        return merged;\n    }\n}\n","solution":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        int[] distances = {1240, 875, 1630, 875, 1105, 690};\n        System.out.println(Arrays.toString(mergeSort(distances)));\n    }\n\n    public static int[] mergeSort(int[] values) {\n        if (values.length <= 1) {\n            return values;\n        }\n        int middle = values.length / 2;\n        int[] left = mergeSort(Arrays.copyOfRange(values, 0, middle));\n        int[] right = mergeSort(Arrays.copyOfRange(values, middle, values.length));\n        return merge(left, right);\n    }\n\n    public static int[] merge(int[] first, int[] second) {\n        int[] merged = new int[first.length + second.length];\n        int i = 0;\n        int j = 0;\n        int k = 0;\n        while (i < first.length && j < second.length) {\n            if (first[i] <= second[j]) {\n                merged[k] = first[i];\n                i++;\n            } else {\n                merged[k] = second[j];\n                j++;\n            }\n            k++;\n        }\n        while (i < first.length) {\n            merged[k] = first[i];\n            i++;\n            k++;\n        }\n        while (j < second.length) {\n            merged[k] = second[j];\n            j++;\n            k++;\n        }\n        return merged;\n    }\n}\n","hints":["The base case: `if (values.length <= 1) { return values; }`. Otherwise `int middle = values.length / 2;`, and the halves are `Arrays.copyOfRange(values, 0, middle)` and `Arrays.copyOfRange(values, middle, values.length)`.","Sort each half with `mergeSort`, then `return merge(left, right);`"],"tests":[{"name":"Test 1","stdin":"","call":"int[] distances = {1240, 875, 1630, 875, 1105, 690};\nSystem.out.println(Arrays.toString(mergeSort(distances)));\n","expect":"[690, 875, 875, 1105, 1240, 1630]","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(Arrays.toString(mergeSort(new int[0])));\nint[] one = {500};\nSystem.out.println(Arrays.toString(mergeSort(one)));\nint[] two = {9, 3};\nSystem.out.println(Arrays.toString(mergeSort(two)));\nint[] sorted = {1, 2, 3, 4, 5};\nSystem.out.println(Arrays.toString(mergeSort(sorted)));\nint[] backwards = {7, 6, 5, 4, 3, 2, 1};\nSystem.out.println(Arrays.toString(mergeSort(backwards)));\n","expect":"[]\n[500]\n[3, 9]\n[1, 2, 3, 4, 5]\n[1, 2, 3, 4, 5, 6, 7]","hidden":true},{"name":"Test 3","stdin":"","call":"int[] many = new int[1000];\nfor (int i = 0; i < many.length; i++) {\n    many[i] = (i * 37) % 1000;\n}\nint[] result = mergeSort(many);\nSystem.out.println(result.length + \" \" + result[0] + \" \" + result[1] + \" \" + result[500] + \" \" + result[999]);\n","expect":"1000 0 1 500 999","hidden":true}],"require":[{"pattern":"\\bmergeSort\\s*\\(","min":4,"message":"`mergeSort` should call itself on each half."},{"pattern":"\\bmerge\\s*\\(","min":2,"message":"Merge the sorted halves with `merge`."}],"forbid":[{"pattern":"\\bsort\\s*\\(","message":"Don't use `Arrays.sort` or `Collections.sort`: sort with merge sort."}]},{"task":"A crossword maker lists candidate words and wants them sorted by length, shortest first. Words of the same length must stay in the order they were given in: the sort must be stable. Write `sortByLength(String[] words)`, which returns a new array sorted that way, with merge sort. You need your own merge for Strings, `mergeByLength(String[] first, String[] second)`, which compares the words' lengths, and takes from `first` when two lengths are equal. `Arrays.copyOfRange` works for String arrays too. Don't use `Arrays.sort` or `Collections.sort`. The check runs:\n\n```java\nString[] words = {\"orbit\", \"fig\", \"ash\", \"lantern\", \"elm\", \"pearl\"};\nSystem.out.println(Arrays.toString(sortByLength(words)));\n```\n\nand expects:\n\n```\n[fig, ash, elm, orbit, pearl, lantern]\n```\n","kind":"code","seed":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        String[] words = {\"orbit\", \"fig\", \"ash\", \"lantern\", \"elm\", \"pearl\"};\n        System.out.println(Arrays.toString(sortByLength(words)));\n    }\n\n    public static String[] sortByLength(String[] words) {\n        return words;\n    }\n\n    // Write the method mergeByLength here.\n}\n","solution":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        String[] words = {\"orbit\", \"fig\", \"ash\", \"lantern\", \"elm\", \"pearl\"};\n        System.out.println(Arrays.toString(sortByLength(words)));\n    }\n\n    public static String[] sortByLength(String[] words) {\n        if (words.length <= 1) {\n            return words;\n        }\n        int middle = words.length / 2;\n        String[] left = sortByLength(Arrays.copyOfRange(words, 0, middle));\n        String[] right = sortByLength(Arrays.copyOfRange(words, middle, words.length));\n        return mergeByLength(left, right);\n    }\n\n    public static String[] mergeByLength(String[] first, String[] second) {\n        String[] merged = new String[first.length + second.length];\n        int i = 0;\n        int j = 0;\n        int k = 0;\n        while (i < first.length && j < second.length) {\n            if (first[i].length() <= second[j].length()) {\n                merged[k] = first[i];\n                i++;\n            } else {\n                merged[k] = second[j];\n                j++;\n            }\n            k++;\n        }\n        while (i < first.length) {\n            merged[k] = first[i];\n            i++;\n            k++;\n        }\n        while (j < second.length) {\n            merged[k] = second[j];\n            j++;\n            k++;\n        }\n        return merged;\n    }\n}\n","hints":["`sortByLength` is the `mergeSort` of the last challenge with `String[]` in place of `int[]`, calling `mergeByLength`.","`mergeByLength` is `merge` with Strings, comparing `first[i].length() <= second[j].length()`. The `<=` takes the word from `first` when the lengths are equal, which keeps the sort stable: `first` holds the words that came earlier."],"tests":[{"name":"Test 1","stdin":"","call":"String[] words = {\"orbit\", \"fig\", \"ash\", \"lantern\", \"elm\", \"pearl\"};\nSystem.out.println(Arrays.toString(sortByLength(words)));\n","expect":"[fig, ash, elm, orbit, pearl, lantern]","hidden":false},{"name":"Test 2","stdin":"","call":"String[] same = {\"dd\", \"cc\", \"bb\", \"aa\"};\nSystem.out.println(Arrays.toString(sortByLength(same)));\nString[] mixed = {\"wren\", \"ox\", \"heron\", \"yak\", \"emu\", \"gnu\", \"ibis\", \"ant\", \"a\"};\nSystem.out.println(Arrays.toString(sortByLength(mixed)));\nSystem.out.println(Arrays.toString(sortByLength(new String[0])));\n","expect":"[dd, cc, bb, aa]\n[a, ox, yak, emu, gnu, ant, wren, ibis, heron]\n[]","hidden":true},{"name":"Test 3","stdin":"","call":"String[] first = {\"glow\", \"tide\", \"moss\", \"reef\"};\nSystem.out.println(Arrays.toString(mergeByLength(first, new String[]{\"sea\", \"bay\", \"kelp\"})));\n","expect":"[sea, bay, glow, tide, moss, reef, kelp]","hidden":true}],"require":[{"pattern":"\\bsortByLength\\s*\\(","min":4,"message":"`sortByLength` should call itself on each half."},{"pattern":"\\bmergeByLength\\s*\\(","min":2,"message":"Write `mergeByLength` and merge the sorted halves with it."}],"forbid":[{"pattern":"\\bsort\\s*\\(","message":"Don't use `Arrays.sort` or `Collections.sort`: sort with merge sort."}]}]}