{"id":"sorting-searching-binary-search","slug":"binary-search","title":"Binary search","text":"Think of a guessing game: someone picks a number from 1 to 100, and after each guess tells you \"higher\" or \"lower\". Guessing 1, 2, 3 and so on is a linear search, and could take 100 guesses. A smarter first guess is 50: whatever the answer, half of the numbers are ruled out. Then guess the middle of the half that's left, and so on. Every guess halves what's left, and 7 guesses are always enough.\n\n**Binary search** plays this game with a sorted array. It looks at the middle element of the range where the value could still be:\n\n- If it's the wanted value, the search is done.\n- If it's smaller than the wanted value, the wanted value can only be to its right, since the array is sorted. The range becomes the right half.\n- If it's larger, the range becomes the left half.\n\nIt repeats this until it finds the value, or until the range is empty, which means the value isn't there.\n\n### In code\n\nTwo variables, `low` and `high`, are the first and the last index of the range. At first, the range is the whole array. Here a race keeps the bib numbers of the runners who finished, sorted:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        int[] finishers = {3, 8, 12, 15, 21, 27, 30, 34, 41};\n        System.out.println(findInSorted(finishers, 27));\n        System.out.println(findInSorted(finishers, 13));\n    }\n\n    public static int findInSorted(int[] sorted, int wanted) {\n        int low = 0;\n        int high = sorted.length - 1;\n        while (low <= high) {\n            int middle = (low + high) / 2;\n            if (sorted[middle] == wanted) {\n                return middle;\n            }\n            if (sorted[middle] < wanted) {\n                low = middle + 1;\n            } else {\n                high = middle - 1;\n            }\n        }\n        return -1;\n    }\n}\n```\n\n```output\n5\n-1\n```\n\nThe search for 27, round by round:\n\n| `low` | `high` | `middle` | `sorted[middle]` | Then |\n| --- | --- | --- | --- | --- |\n| 0 | 8 | 4 | 21 | smaller than 27: `low = 5` |\n| 5 | 8 | 6 | 30 | larger than 27: `high = 5` |\n| 5 | 5 | 5 | 27 | found: return 5 |\n\nAnd for 13, which isn't there:\n\n| `low` | `high` | `middle` | `sorted[middle]` | Then |\n| --- | --- | --- | --- | --- |\n| 0 | 8 | 4 | 21 | larger than 13: `high = 3` |\n| 0 | 3 | 1 | 8 | smaller than 13: `low = 2` |\n| 2 | 3 | 2 | 12 | smaller than 13: `low = 3` |\n| 3 | 3 | 3 | 15 | larger than 13: `high = 2` |\n\nNow `low` is 3 and `high` is 2: the range is empty, the loop ends, and the method returns -1.\n\n- `(low + high) / 2` is integer division, so the middle is always a whole index. When the range has an even number of elements, it's the left one of the two in the middle.\n- The middle element has been checked, so the new range leaves it out: `middle + 1` and `middle - 1`. With `low = middle`, the range could stop shrinking, and the loop would never end.\n- The loop runs `while (low <= high)`: when `low` equals `high`, the range still has one element to look at.\n\n### It needs sorted data\n\nBinary search throws away half of the range at every look, trusting that everything to the left of the middle is smaller and everything to the right is larger. In an array that isn't sorted, that isn't true, and the search can miss a value that's there. So sort the data first (with `Arrays.sort` or `Collections.sort`), or keep it sorted as values are added. Sorting takes time too, so binary search pays off best when the same data is searched many times.\n\nJava has binary search ready as well, in `Arrays.binarySearch` and `Collections.binarySearch`, for sorted arrays and lists. In this module you write your own, to see how it works.\n\n### How much work is it?\n\nEach look at a middle value halves the range, so the range runs out fast:\n\n| Values | Values looked at, linear search | Values looked at, binary search |\n| --- | --- | --- |\n| 100 | 100 | 7 |\n| 1,000 | 1,000 | 10 |\n| 1,000,000 | 1,000,000 | 20 |\n| 1,000,000,000 | 1,000,000,000 | 30 |\n\nA sorted array of a million values needs at most 20 looks, because 2 to the 20th power is 1,048,576, more than a million: halving the range 20 times always gets it down to nothing. Each look compares the middle value with the wanted one up to twice (`==`, then `<`), so that's at most 40 comparisons, where linear search may need a million. Twice as many values take only one look more.\n","task":"A mail carrier has a sorted array of the house numbers that get mail today. Fill in the blanks of `findHouse`, a binary search that returns the index of a house number, or -1 if the house gets no mail. The check runs:\n\n```java\nint[] houses = {2, 4, 8, 10, 14, 16, 20, 22, 26};\nSystem.out.println(findHouse(houses, 20));\nSystem.out.println(findHouse(houses, 12));\n```\n\nand expects:\n\n```\n6\n-1\n```\n","kind":"fill","seed":"public class Main {\n    public static void main(String[] args) {\n        int[] houses = {2, 4, 8, 10, 14, 16, 20, 22, 26};\n        System.out.println(findHouse(houses, 20));\n        System.out.println(findHouse(houses, 12));\n    }\n\n    public static int findHouse(int[] houses, int number) {\n        int low = 0;\n        int high = houses.length - 1;\n        while (low <= high) {\n            int middle = [[(low + high) / 2]];\n            if (houses[middle] == number) {\n                return middle;\n            }\n            if (houses[middle] < number) {\n                low = [[middle + 1]];\n            } else {\n                high = [[middle - 1]];\n            }\n        }\n        return -1;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        int[] houses = {2, 4, 8, 10, 14, 16, 20, 22, 26};\n        System.out.println(findHouse(houses, 20));\n        System.out.println(findHouse(houses, 12));\n    }\n\n    public static int findHouse(int[] houses, int number) {\n        int low = 0;\n        int high = houses.length - 1;\n        while (low <= high) {\n            int middle = (low + high) / 2;\n            if (houses[middle] == number) {\n                return middle;\n            }\n            if (houses[middle] < number) {\n                low = middle + 1;\n            } else {\n                high = middle - 1;\n            }\n        }\n        return -1;\n    }\n}\n","hints":["The middle of the range from `low` to `high` is their sum divided by two: `(low + high) / 2`.","When the middle house number is smaller than the wanted one, the wanted one can only be to the right of the middle, so the range starts after it: `middle + 1`. Otherwise it ends before it: `middle - 1`."],"tests":[{"name":"Test 1","stdin":"","call":"int[] houses = {2, 4, 8, 10, 14, 16, 20, 22, 26};\nSystem.out.println(findHouse(houses, 20));\nSystem.out.println(findHouse(houses, 12));\n","expect":"6\n-1","hidden":false},{"name":"Test 2","stdin":"","call":"int[] houses = {2, 4, 8, 10, 14, 16, 20, 22, 26};\nSystem.out.println(findHouse(houses, 2));\nSystem.out.println(findHouse(houses, 26));\nSystem.out.println(findHouse(houses, 1));\nSystem.out.println(findHouse(houses, 30));\nSystem.out.println(findHouse(houses, 14));\n","expect":"0\n8\n-1\n-1\n4","hidden":true},{"name":"Test 3","stdin":"","call":"int[] one = {7};\nSystem.out.println(findHouse(one, 7) + \" \" + findHouse(one, 8));\nint[] none = new int[0];\nSystem.out.println(findHouse(none, 7));\nint[] pair = {3, 5};\nSystem.out.println(findHouse(pair, 3) + \" \" + findHouse(pair, 5) + \" \" + findHouse(pair, 4));\n","expect":"0 -1\n-1\n0 1 -1","hidden":true}],"require":[],"forbid":[],"more":[{"task":"This binary search prints each value it looks at. What does the program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        int[] sorted = {2, 5, 8, 12, 16, 23, 38};\n        System.out.println(find(sorted, 23));\n        System.out.println(find(sorted, 3));\n    }\n\n    public static int find(int[] sorted, int wanted) {\n        int low = 0;\n        int high = sorted.length - 1;\n        while (low <= high) {\n            int middle = (low + high) / 2;\n            System.out.println(\"looking at \" + sorted[middle]);\n            if (sorted[middle] == wanted) {\n                return middle;\n            }\n            if (sorted[middle] < wanted) {\n                low = middle + 1;\n            } else {\n                high = middle - 1;\n            }\n        }\n        return -1;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        int[] sorted = {2, 5, 8, 12, 16, 23, 38};\n        System.out.println(find(sorted, 23));\n        System.out.println(find(sorted, 3));\n    }\n\n    public static int find(int[] sorted, int wanted) {\n        int low = 0;\n        int high = sorted.length - 1;\n        while (low <= high) {\n            int middle = (low + high) / 2;\n            System.out.println(\"looking at \" + sorted[middle]);\n            if (sorted[middle] == wanted) {\n                return middle;\n            }\n            if (sorted[middle] < wanted) {\n                low = middle + 1;\n            } else {\n                high = middle - 1;\n            }\n        }\n        return -1;\n    }\n}\n","hints":["The first middle is `(0 + 6) / 2`, index 3, which holds 12. For 23, the search goes on to the right: `low` becomes 4, and the next middle is `(4 + 6) / 2`, index 5.","The method prints its lines before `main` prints the index it returns. For 3, the range shrinks toward the start of the array, and is empty after three looks, so `main` prints -1."],"tests":[{"name":"Output","stdin":"","expect":"looking at 12\nlooking at 23\n5\nlooking at 12\nlooking at 5\nlooking at 2\n-1","hidden":false}],"require":[],"forbid":[],"lines":["looking at 12","looking at 23","5","looking at 12","looking at 5","looking at 2","-1"]},{"task":"How many values does binary search look at? Write the method `looksNeeded(int[] sorted, int wanted)`. It does the binary search of this step, with the middle at `(low + high) / 2`, but instead of an index it returns how many middle values it looked at, whether it found the wanted value or not.\n\nThe check makes a sorted array of the 1,000 even numbers from 0 to 1998, and looks for 998, 1998 and 7. A linear search would look at 500, 1,000 and 1,000 values for them. (998 happens to sit right in the middle of the array.) The check runs:\n\n```java\nint[] evens = new int[1000];\nfor (int i = 0; i < evens.length; i++) {\n    evens[i] = i * 2;\n}\nSystem.out.println(looksNeeded(evens, 998));\nSystem.out.println(looksNeeded(evens, 1998));\nSystem.out.println(looksNeeded(evens, 7));\n```\n\nand expects:\n\n```\n1\n10\n10\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        int[] evens = new int[1000];\n        for (int i = 0; i < evens.length; i++) {\n            evens[i] = i * 2;\n        }\n        System.out.println(looksNeeded(evens, 998));\n        System.out.println(looksNeeded(evens, 1998));\n        System.out.println(looksNeeded(evens, 7));\n    }\n\n    // Write the method looksNeeded here.\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        int[] evens = new int[1000];\n        for (int i = 0; i < evens.length; i++) {\n            evens[i] = i * 2;\n        }\n        System.out.println(looksNeeded(evens, 998));\n        System.out.println(looksNeeded(evens, 1998));\n        System.out.println(looksNeeded(evens, 7));\n    }\n\n    public static int looksNeeded(int[] sorted, int wanted) {\n        int low = 0;\n        int high = sorted.length - 1;\n        int looks = 0;\n        while (low <= high) {\n            int middle = (low + high) / 2;\n            looks++;\n            if (sorted[middle] == wanted) {\n                return looks;\n            }\n            if (sorted[middle] < wanted) {\n                low = middle + 1;\n            } else {\n                high = middle - 1;\n            }\n        }\n        return looks;\n    }\n}\n","hints":["Start from the binary search of this step, and add a counter, `int looks = 0;`. Each round of the loop looks at one middle value, so it adds one to `looks`.","Return `looks` where the search would return `middle`, and also after the loop, where it would return -1."],"tests":[{"name":"Test 1","stdin":"","call":"int[] evens = new int[1000];\nfor (int i = 0; i < evens.length; i++) {\n    evens[i] = i * 2;\n}\nSystem.out.println(looksNeeded(evens, 998));\nSystem.out.println(looksNeeded(evens, 1998));\nSystem.out.println(looksNeeded(evens, 7));\n","expect":"1\n10\n10","hidden":false},{"name":"Test 2","stdin":"","call":"int[] sorted = {3, 8, 12, 15, 21, 27, 30, 34, 41};\nSystem.out.println(looksNeeded(sorted, 21));\nSystem.out.println(looksNeeded(sorted, 27));\nSystem.out.println(looksNeeded(sorted, 13));\nSystem.out.println(looksNeeded(sorted, 3));\nSystem.out.println(looksNeeded(sorted, 50));\n","expect":"1\n3\n4\n3\n4","hidden":true},{"name":"Test 3","stdin":"","call":"int[] none = new int[0];\nSystem.out.println(looksNeeded(none, 5));\nint[] one = {5};\nSystem.out.println(looksNeeded(one, 5) + \" \" + looksNeeded(one, 6));\nint[] big = new int[100000];\nfor (int i = 0; i < big.length; i++) {\n    big[i] = i;\n}\nSystem.out.println(looksNeeded(big, 99999) + \" \" + looksNeeded(big, -1));\n","expect":"0\n1 1\n17 16","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+int\\s+looksNeeded\\s*\\(\\s*int\\s*\\[\\s*\\]\\s*\\w+\\s*,\\s*int\\s+\\w+\\s*\\)","message":"Write the method as `public static int looksNeeded(int[] sorted, int wanted)`."},{"pattern":"\\bwhile\\s*\\(","message":"Do the binary search with a `while` loop, as in this step."}],"forbid":[]}]}