{"id":"recursion-binary-search","slug":"binary-search","title":"Recursive binary search","text":"Module 28 searched a sorted array with binary search, in a `while` loop: look at the middle value, and if it isn't the wanted one, go on in the half where the wanted value would be. \"Go on in the half\" is the same search on a smaller range, so binary search can also be written recursively.\n\n### The range as parameters\n\nThe loop kept the range in two variables, `low` and `high`. The recursive method gets them as parameters, and the recursive call gets the new range. A greengrocer keeps the prices of the products, in cents, in a sorted array:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        int[] prices = {45, 80, 120, 199, 250, 310, 475, 520};\n        System.out.println(find(prices, 310, 0, prices.length - 1));\n        System.out.println(find(prices, 100, 0, prices.length - 1));\n    }\n\n    public static int find(int[] sorted, int wanted, int low, int high) {\n        if (low > high) {\n            return -1;\n        }\n        int middle = (low + high) / 2;\n        System.out.println(\"  looking between \" + low + \" and \" + high + \" at \" + sorted[middle]);\n        if (sorted[middle] == wanted) {\n            return middle;\n        }\n        if (sorted[middle] < wanted) {\n            return find(sorted, wanted, middle + 1, high);\n        }\n        return find(sorted, wanted, low, middle - 1);\n    }\n}\n```\n\n```output\n  looking between 0 and 7 at 199\n  looking between 4 and 7 at 310\n5\n  looking between 0 and 7 at 199\n  looking between 0 and 2 at 80\n  looking between 2 and 2 at 120\n-1\n```\n\nThere are two base cases: an empty range (`low > high`), where the value isn't in the array, and a middle value that is the wanted one. Every other call searches a range at most half as long, so the calls reach a base case quickly: for an array of a million values, at most 20 looks, as in module 28. The recursion is at most about 20 calls deep, so the call stack is never a problem here.\n\nEach recursive call is `return find(...)`: the method returns whatever the smaller search found, unchanged. The answer is found at the bottom and passed up through every call.\n\n### A friendlier method on top\n\nCallers shouldn't have to pass `0` and `prices.length - 1` themselves. The usual way is a second method with the same name and fewer parameters, which starts the search on the whole array (two methods with the same name and different parameters are overloads, module 21):\n\n```java\npublic static int find(int[] sorted, int wanted) {\n    return find(sorted, wanted, 0, sorted.length - 1);\n}\n```\n\nThe extra parameters of the recursive method are then a detail that only this class sees. The third challenge of this step uses the pattern.\n","task":"A greengrocer's till has the sorted array of prices, in cents. Write the recursive method `findPrice(int[] prices, int price, int low, int high)`, which returns the index of `price` in `prices` between the indexes `low` and `high`, or -1 if it isn't there. Do it without a loop: binary search, as in this step. The check runs:\n\n```java\nint[] prices = {35, 60, 99, 140, 185, 230, 299, 365, 420};\nSystem.out.println(findPrice(prices, 299, 0, prices.length - 1));\nSystem.out.println(findPrice(prices, 100, 0, prices.length - 1));\n```\n\nand expects:\n\n```\n6\n-1\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        int[] prices = {35, 60, 99, 140, 185, 230, 299, 365, 420};\n        System.out.println(findPrice(prices, 299, 0, prices.length - 1));\n        System.out.println(findPrice(prices, 100, 0, prices.length - 1));\n    }\n\n    public static int findPrice(int[] prices, int price, int low, int high) {\n        return -1;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        int[] prices = {35, 60, 99, 140, 185, 230, 299, 365, 420};\n        System.out.println(findPrice(prices, 299, 0, prices.length - 1));\n        System.out.println(findPrice(prices, 100, 0, prices.length - 1));\n    }\n\n    public static int findPrice(int[] prices, int price, int low, int high) {\n        if (low > high) {\n            return -1;\n        }\n        int middle = (low + high) / 2;\n        if (prices[middle] == price) {\n            return middle;\n        }\n        if (prices[middle] < price) {\n            return findPrice(prices, price, middle + 1, high);\n        }\n        return findPrice(prices, price, low, middle - 1);\n    }\n}\n","hints":["First the empty range: when `low > high`, the price isn't there, so return -1. Then look at the middle, `(low + high) / 2`, and return its index if it holds the price.","If the middle price is smaller, search the right half: `return findPrice(prices, price, middle + 1, high);`. Otherwise search the left half, from `low` to `middle - 1`."],"tests":[{"name":"Test 1","stdin":"","call":"int[] prices = {35, 60, 99, 140, 185, 230, 299, 365, 420};\nSystem.out.println(findPrice(prices, 299, 0, prices.length - 1));\nSystem.out.println(findPrice(prices, 100, 0, prices.length - 1));\n","expect":"6\n-1","hidden":false},{"name":"Test 2","stdin":"","call":"int[] prices = {35, 60, 99, 140, 185, 230, 299, 365, 420};\nSystem.out.println(findPrice(prices, 35, 0, prices.length - 1));\nSystem.out.println(findPrice(prices, 420, 0, prices.length - 1));\nSystem.out.println(findPrice(prices, 185, 0, prices.length - 1));\nSystem.out.println(findPrice(prices, 10, 0, prices.length - 1));\nSystem.out.println(findPrice(prices, 500, 0, prices.length - 1));\nSystem.out.println(findPrice(prices, 60, 2, 8));\n","expect":"0\n8\n4\n-1\n-1\n-1","hidden":true},{"name":"Test 3","stdin":"","call":"int[] one = {75};\nSystem.out.println(findPrice(one, 75, 0, 0) + \" \" + findPrice(one, 80, 0, 0));\nint[] none = new int[0];\nSystem.out.println(findPrice(none, 75, 0, -1));\nint[] many = new int[100000];\nfor (int i = 0; i < many.length; i++) {\n    many[i] = i * 3;\n}\nSystem.out.println(findPrice(many, 299997, 0, many.length - 1) + \" \" + findPrice(many, 299998, 0, many.length - 1));\n","expect":"0 -1\n-1\n99999 -1","hidden":true}],"require":[{"pattern":"\\bfindPrice\\s*\\(","min":4,"message":"`findPrice` should call itself on the half where the price can be."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Do it with recursion, without a loop."},{"pattern":"binarySearch","message":"Write the search yourself, without `Arrays.binarySearch`."}],"more":[{"task":"An auction keeps its bids in a sorted array. When a new bid comes in, where does it go? Write the recursive method `placeFor(int[] bids, int bid, int low, int high)`, which returns the index where `bid` belongs between `low` and `high`: the index of an equal bid if there is one, and otherwise the index of the first bid that's larger (or `high + 1` if no bid is larger). That's the binary search of this step with one change: when the range is empty, return `low` instead of -1. When the range is empty, `low` has moved past every smaller bid. The check runs:\n\n```java\nint[] bids = {100, 150, 220, 300, 410};\nSystem.out.println(placeFor(bids, 220, 0, bids.length - 1));\nSystem.out.println(placeFor(bids, 200, 0, bids.length - 1));\nSystem.out.println(placeFor(bids, 500, 0, bids.length - 1));\n```\n\nand expects:\n\n```\n2\n2\n5\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        int[] bids = {100, 150, 220, 300, 410};\n        System.out.println(placeFor(bids, 220, 0, bids.length - 1));\n        System.out.println(placeFor(bids, 200, 0, bids.length - 1));\n        System.out.println(placeFor(bids, 500, 0, bids.length - 1));\n    }\n\n    // Write the method placeFor here.\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        int[] bids = {100, 150, 220, 300, 410};\n        System.out.println(placeFor(bids, 220, 0, bids.length - 1));\n        System.out.println(placeFor(bids, 200, 0, bids.length - 1));\n        System.out.println(placeFor(bids, 500, 0, bids.length - 1));\n    }\n\n    public static int placeFor(int[] bids, int bid, int low, int high) {\n        if (low > high) {\n            return low;\n        }\n        int middle = (low + high) / 2;\n        if (bids[middle] == bid) {\n            return middle;\n        }\n        if (bids[middle] < bid) {\n            return placeFor(bids, bid, middle + 1, high);\n        }\n        return placeFor(bids, bid, low, middle - 1);\n    }\n}\n","hints":["Start from `findPrice`. Only the empty range changes: `if (low > high) { return low; }`.","Why `low`: the search moves `low` past a middle only when the middle bid is smaller than the new one, and moves `high` before a middle only when it's larger. When they cross, `low` is the first index whose bid is larger."],"tests":[{"name":"Test 1","stdin":"","call":"int[] bids = {100, 150, 220, 300, 410};\nSystem.out.println(placeFor(bids, 220, 0, bids.length - 1));\nSystem.out.println(placeFor(bids, 200, 0, bids.length - 1));\nSystem.out.println(placeFor(bids, 500, 0, bids.length - 1));\n","expect":"2\n2\n5","hidden":false},{"name":"Test 2","stdin":"","call":"int[] bids = {100, 150, 220, 300, 410};\nSystem.out.println(placeFor(bids, 50, 0, bids.length - 1));\nSystem.out.println(placeFor(bids, 100, 0, bids.length - 1));\nSystem.out.println(placeFor(bids, 120, 0, bids.length - 1));\nSystem.out.println(placeFor(bids, 301, 0, bids.length - 1));\nSystem.out.println(placeFor(bids, 410, 0, bids.length - 1));\n","expect":"0\n0\n1\n4\n4","hidden":true},{"name":"Test 3","stdin":"","call":"int[] none = new int[0];\nSystem.out.println(placeFor(none, 80, 0, -1));\nint[] one = {90};\nSystem.out.println(placeFor(one, 80, 0, 0) + \" \" + placeFor(one, 95, 0, 0));\nint[] even = {2, 4, 6, 8, 10, 12};\nSystem.out.println(placeFor(even, 7, 0, 5) + \" \" + placeFor(even, 13, 0, 5) + \" \" + placeFor(even, 1, 0, 5));\n","expect":"0\n0 1\n3 6 0","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+int\\s+placeFor\\s*\\(\\s*int\\s*\\[\\s*\\]\\s*\\w+\\s*,\\s*int\\s+\\w+\\s*,\\s*int\\s+\\w+\\s*,\\s*int\\s+\\w+\\s*\\)","message":"Write the method as `public static int placeFor(int[] bids, int bid, int low, int high)`."},{"pattern":"\\bplaceFor\\s*\\(","min":5,"message":"`placeFor` should call itself on the half where the bid belongs."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Do it with recursion, without a loop."},{"pattern":"binarySearch","message":"Write the search yourself, without `Arrays.binarySearch`."}]},{"task":"A school's lost-and-found keeps the names on its labels in a sorted array, in alphabetical order (as `compareTo` orders them, module 38). Write two methods named `findLabel`:\n\n- `findLabel(String[] labels, String name, int low, int high)`: the recursive binary search between `low` and `high`, returning the index of `name`, or -1. Strings are compared with `compareTo`: `name.compareTo(labels[middle])` is 0 when they are equal, below 0 when `name` comes first, and above 0 when it comes after.\n- `findLabel(String[] labels, String name)`: searches the whole array, by calling the first one.\n\nThe check calls only the second one. It runs:\n\n```java\nString[] labels = {\"Aino\", \"Bilal\", \"Chen\", \"Dara\", \"Emil\", \"Freya\", \"Gus\"};\nSystem.out.println(findLabel(labels, \"Emil\"));\nSystem.out.println(findLabel(labels, \"Bo\"));\n```\n\nand expects:\n\n```\n4\n-1\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        String[] labels = {\"Aino\", \"Bilal\", \"Chen\", \"Dara\", \"Emil\", \"Freya\", \"Gus\"};\n        System.out.println(findLabel(labels, \"Emil\"));\n        System.out.println(findLabel(labels, \"Bo\"));\n    }\n\n    public static int findLabel(String[] labels, String name) {\n        return -1;\n    }\n\n    // Write the recursive findLabel here.\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        String[] labels = {\"Aino\", \"Bilal\", \"Chen\", \"Dara\", \"Emil\", \"Freya\", \"Gus\"};\n        System.out.println(findLabel(labels, \"Emil\"));\n        System.out.println(findLabel(labels, \"Bo\"));\n    }\n\n    public static int findLabel(String[] labels, String name) {\n        return findLabel(labels, name, 0, labels.length - 1);\n    }\n\n    public static int findLabel(String[] labels, String name, int low, int high) {\n        if (low > high) {\n            return -1;\n        }\n        int middle = (low + high) / 2;\n        int order = name.compareTo(labels[middle]);\n        if (order == 0) {\n            return middle;\n        }\n        if (order > 0) {\n            return findLabel(labels, name, middle + 1, high);\n        }\n        return findLabel(labels, name, low, middle - 1);\n    }\n}\n","hints":["The two-parameter method is one line: `return findLabel(labels, name, 0, labels.length - 1);`","In the recursive one, compare once and keep the result: `int order = name.compareTo(labels[middle]);`. If it's above 0, the name comes after the middle, so search from `middle + 1` to `high`."],"tests":[{"name":"Test 1","stdin":"","call":"String[] labels = {\"Aino\", \"Bilal\", \"Chen\", \"Dara\", \"Emil\", \"Freya\", \"Gus\"};\nSystem.out.println(findLabel(labels, \"Emil\"));\nSystem.out.println(findLabel(labels, \"Bo\"));\n","expect":"4\n-1","hidden":false},{"name":"Test 2","stdin":"","call":"String[] labels = {\"Aino\", \"Bilal\", \"Chen\", \"Dara\", \"Emil\", \"Freya\", \"Gus\"};\nSystem.out.println(findLabel(labels, \"Aino\"));\nSystem.out.println(findLabel(labels, \"Gus\"));\nSystem.out.println(findLabel(labels, \"Dara\"));\nSystem.out.println(findLabel(labels, \"Zoe\"));\nSystem.out.println(findLabel(labels, \"Abe\"));\nSystem.out.println(findLabel(labels, \"chen\"));\n","expect":"0\n6\n3\n-1\n-1\n-1","hidden":true},{"name":"Test 3","stdin":"","call":"String[] none = new String[0];\nSystem.out.println(findLabel(none, \"Ida\"));\nString[] two = {\"Ida\", \"Jon\"};\nSystem.out.println(findLabel(two, \"Ida\") + \" \" + findLabel(two, \"Jon\") + \" \" + findLabel(two, \"Jo\"));\n","expect":"-1\n0 1 -1","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+int\\s+findLabel\\s*\\(\\s*String\\s*\\[\\s*\\]\\s*\\w+\\s*,\\s*String\\s+\\w+\\s*,\\s*int\\s+\\w+\\s*,\\s*int\\s+\\w+\\s*\\)","message":"Write the recursive method as `public static int findLabel(String[] labels, String name, int low, int high)`."},{"pattern":"\\bfindLabel\\s*\\(","min":6,"message":"The 4-parameter `findLabel` should call itself on the half where the name can be."},{"pattern":"\\.compareTo\\s*\\(","message":"Compare the names with `compareTo`."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Do it with recursion, without a loop."},{"pattern":"binarySearch|indexOf","message":"Write the search yourself."}]}]}