Search code examples
javabinary-search

Recursive binary search on a sorted array of Strings


I made an array that is 10 in length. Each slot has a name. My goal is to randomly pick a name and do a binary search to find it. I don't understand what is wrong with it but if you could at least give me a hint it would be very helpful, thank you. Here's my code:

    private int iRecursiveCalls = 0;

    public void runRecursiveTest(){

        String[] iArraySize = new String[10];

        String[] aiNumbers = new String[iArraySize.length];

        SecureRandom oRand = new SecureRandom();

        iArraySize[0] = "John";
        iArraySize[1] = "Max";
        iArraySize[2] = "Kyle";
        iArraySize[3] = "Sam";
        iArraySize[4] = "Robert";
        iArraySize[5] = "Alex";
        iArraySize[6] = "Bob";
        iArraySize[7] = "Daniel";
        iArraySize[8] = "Felix";
        iArraySize[9] = "Michael";

        String iTarget = aiNumbers[oRand.nextInt(iArraySize.length)];

        Arrays.sort(aiNumbers);

        System.out.println("Target num: " + iTarget);

        System.out.println("--- Begin Binary Search ---");
        long lBegTime = System.nanoTime();
        findNumbersBinarySearch(aiNumbers, iTarget, 0, iArraySize.length -1);
        long lEndTime = System.nanoTime();
        System.out.println("Elapsed time: " + (lEndTime - lBegTime));
        System.out.println("Recursive calls: " + iRecursiveCalls);
        System.out.println("--- End Binary Search ---");
    }

    private int findNumbersBinarySearch(String[] aiNumbers, String iTarget,
                                        int iLow, int iHigh){

        iRecursiveCalls++;

        int iMiddle = (iHigh + iLow) / 2;

        if(iTarget.equals(aiNumbers[iMiddle])){
            return iMiddle;
        }
        else if(iTarget.compareTo(aiNumbers[iMiddle])>0){

            return findNumbersBinarySearch(aiNumbers, iTarget,
                    iMiddle + 1, iHigh);
        }
        else{
            return findNumbersBinarySearch(aiNumbers, iTarget,
                    iLow, iMiddle - 1);
        }
    }
}

What can I do to fix it?


Solution

  • Your algorithm isn't working because you're indexing into, sorting and passing into your function an empty array. You may have been confused between iArraySize and aiNumbers; both of these are misleading variable names because you're adding string names to iArraySize and aiNumbers doesn't contain numbers. Your binary search function name and other variables are similarly misleading; they use the word Numbers and the prefix i even though the function header takes a String[] array.

    Furthermore, your code will blow the call stack if the target isn't found in the array. The classic binary search will return failure once lo > hi; I can't think of any reason not to include the base case.

    Another binary search gotcha is to use the formula mid = (hi - lo) / 2 + lo. This avoids nasty integer overflow bugs that might occur if hi + lo > Integer.MAX_SIZE.

    This code fixes these issues by sticking to one array the whole way through, uses more accurate variable names and won't overflow the stack:

    import java.util.*;
    import java.security.SecureRandom;
    
    class Main {
        private static int recursiveCalls = 0;
    
        public static void main(String[] args) {
            runRecursiveTest();
        }
    
        public static void runRecursiveTest() {
            SecureRandom rand = new SecureRandom();
            String[] names = {
                "John",
                "Max",
                "Kyle",
                "Sam",
                "Robert",
                "Alex",
                "Bob",
                "Daniel",
                "Felix",
                "Michael"
            };
    
            String target = names[rand.nextInt(names.length)];
            Arrays.sort(names);
    
            System.out.println("Target string: " + target);
            System.out.println("--- Begin Binary Search ---");
    
            long begin = System.nanoTime();
            System.out.println(bisect(names, target, 0, names.length - 1));
            long end = System.nanoTime();
    
            System.out.println("Elapsed time: " + (end - begin));
            System.out.println("Recursive calls: " + recursiveCalls);
            System.out.println("--- End Binary Search ---");
        }
    
        private static int bisect(String[] arr, String target, int lo, int hi) {
            recursiveCalls++;
    
            if (lo > hi) { return -1; }
    
            int mid = (hi - lo) / 2 + lo;
    
            if (target.equals(arr[mid])) {
                return mid;
            }
            else if (target.compareTo(arr[mid]) > 0) {
                return bisect(arr, target, mid + 1, hi);
            }
    
            return bisect(arr, target, lo, mid - 1);
        }
    }
    

    Output:

    Target string: Sam
    --- Begin Binary Search ---
    9
    Elapsed time: 81385
    Recursive calls: 4
    --- End Binary Search ---
    

    Try it!