二分查找法02:二分查找法的变种

upper

查找大于target的第一个元素

public class Algorithm {

    public static void main(String[] args) {

        Integer[] arr = {1, 1, 3, 3, 5, 5};

        for (int i = 0; i <= 6; i++) {
            System.out.print(BinarySearch.upper(arr, i) + " ");  //0 2 2 4 4 6 6
        }
    }
}

class BinarySearch {

    /**
     * 循环不变量:在arr[left, right]中查找大于target的第一个元素,如果找不到,就返回arr.length
     */
    public static<E extends Comparable<E>> int upper(E[] arr, E target){

        int left = 0;

        /**
         * 找不到会返回arr.length,因此right要取到arr.length
         */
        int right = arr.length;

        while (left < right) {

            int mid  = left + (right - left) / 2;

            if (arr[mid].compareTo(target) <= 0) {
                left = mid + 1;
            }
            else {

                /**
                 * 此时mid有可能是要找的元素,因此不能省略
                 */
                right = mid;
            }
        }

        return left;
    }
}

upper_ceil

若target有多个,则返回索引值最大的那个;若不存在,则返回大于target的第一个元素,也即upper()方法的结果

public class Algorithm {

    public static void main(String[] args) {

        Integer[] arr = {1, 1, 3, 3, 5, 5};

        for (int i = 0; i <= 6; i++) {
            System.out.print(BinarySearch.upper_ceil(arr, i) + " ");  //0 1 2 3 4 5 6
        }
    }
}

class BinarySearch {

    public static<E extends Comparable<E>> int upper_ceil(E[] arr, E target) {

        int index = upper(arr, target);

        /**
         * 先执行upper()得到index,这是大于target的第一个元素
         * 如果target存在,就返回其最大的索引index - 1,不存在就返回upper()
         */
        if (index - 1 >= 0 && arr[index - 1].compareTo(target) == 0){
            return index - 1;
        }
        else {
            return index;
        }
    }

    public static<E extends Comparable<E>> int upper(E[] arr, E target){

        int left = 0;
        int right = arr.length;

        while (left < right) {

            int mid  = left + (right - left) / 2;

            if (arr[mid].compareTo(target) <= 0) {
                left = mid + 1;
            }
            else {
                right = mid;
            }
        }

        return left;
    }
}

lower_ceil

若target有多个,则返回索引值最小的那个;若不存在,则返回大于target的第一个元素,也即upper()方法的结果

和upper()相比,区别只在于判断条件arr[mid].compareTo(target) < 0和<=0

public class Algorithm {

    public static void main(String[] args) {

        Integer[] arr = {1, 1, 3, 3, 5, 5};

        for (int i = 0; i <= 6; i++) {
            System.out.print(BinarySearch.lower_ceil(arr, i) + " ");  //0 0 2 2 4 4 6
        }
    }
}

class BinarySearch {

    public static<E extends Comparable<E>> int lower_ceil(E[] arr, E target) {

        int left = 0;
        int right = arr.length;

        while (left < right) {

            int mid  = left + (right - left) / 2;

            /**
             * 因为要返回target的最小索引,如果target在mid右边,那肯定mid不是要找的元素
             */
            if (arr[mid].compareTo(target) < 0) {
                left = mid + 1;
            }
            else {

                /**
                 * 如果target在mid左边,mid有可能是要找的元素,不能省略
                 */
                right = mid;
            }
        }

        return left;
    }
}

lower

查找小于target的最后一个元素

注意死循环

public class Algorithm {

    public static void main(String[] args) {

        Integer[] arr = {1, 1, 3, 3, 5, 5};

        for (int i = 0; i <= 6; i++) {
            System.out.print(BinarySearch.lower(arr, i) + " ");  //-1 -1 1 1 3 3 5
        }
    }
}

class BinarySearch {

    /**
     * 循环不变量:在arr[left, right]中寻找小于target的最后一个元素,如果找不到,就返回-1
     */
    public static <E extends Comparable<E>> int lower(E[] arr, E target) {

        /**
         * 找不到会返回-1,因此left要取到-1
         */
        int left = -1;
        int right = arr.length - 1;

        while (left < right) {
            
            /**
             * 在缩小区间的过程中,会出现left和right相邻的情况,因为计算机默认是向下取整的,因此mid == (left + right) / 2 == left
             * 当arr[mid].compareTo(target) < 0时,left始终等于mid,[mid, right]始终不会变,就会陷入死循环。
             * 在求mid值时加1,就会变成向上取整,可以避免这个死循环
             */
            int mid = left + (right - left + 1) / 2;

            if (arr[mid].compareTo(target) < 0) {
                left = mid;
            } else {

                /**
                 * 如果arr[mid] >= target,mid肯定不是要找的元素
                 */
                right = mid - 1;
            }
        }

        return left;
    }
}

lower_floor

若target有多个,则返回索引值最小的那个;若不存在,则返回小于target的最后一个元素,也即lower()方法的结果

public class Algorithm {

    public static void main(String[] args) {

        Integer[] arr = {1, 1, 3, 3, 5, 5};

        for (int i = 0; i <= 6; i++) {
            System.out.print(BinarySearch.lower_floor(arr, i) + " ");  //-1 0 1 2 3 4 5
        }
    }
}

class BinarySearch {

    public static<E extends Comparable<E>> int lower_floor(E[] arr, E target) {

        int index = lower(arr, target);

        /**
         * 先执行lower()得到index,这是小于target的最大值
         * 如果target存在,就返回其最小的索引index + 1,不存在就返回lower()
         */
        if (index + 1 < arr.length && arr[index + 1].compareTo(target) == 0){
            return index + 1;
        }
        else {
            return index;
        }
    }

    public static <E extends Comparable<E>> int lower(E[] arr, E target) {

        int left = -1;
        int right = arr.length - 1;

        while (left < right) {

            int mid = left + (right - left + 1) / 2;

            if (arr[mid].compareTo(target) < 0) {
                left = mid;
            } else {

                right = mid - 1;
            }
        }

        return left;
    }
}

upper_floor

若target有多个,则返回索引值最大的那个;若不存在,则返回小于target的最后一个元素,也即lower()方法的结果

public class Algorithm {

    public static void main(String[] args) {

        Integer[] arr = {1, 1, 3, 3, 5, 5};

        for (int i = 0; i <= 6; i++) {
            System.out.print(BinarySearch.upper_floor(arr, i) + " ");  //-1 1 1 3 3 5 5
        }
    }
}

class BinarySearch {

    /**
     * 循环不变量:在arr[left, right]中寻找小于target的最后一个元素,如果找不到,就返回-1
     */
    public static <E extends Comparable<E>> int upper_floor(E[] arr, E target) {

        /**
         * 找不到会返回-1,因此left要取到-1
         */
        int left = -1;
        int right = arr.length - 1;

        while (left < right) {

            int mid = left + (right - left + 1) / 2;

            if (arr[mid].compareTo(target) <= 0) {
                left = mid;
            } else {

                /**
                 * 如果arr[mid] > target,mid肯定不是要找的元素
                 */
                right = mid - 1;
            }
        }

        return left;
    }
}

二分查找法模板

class BinarySearch {

    public static <E extends Comparable<E>> int **(E[] arr, E target) {

       int left = **;
       int right = **;

        /**
         * 在arr[left, right]中寻找解
         */
       while (left < right) {

           int mid = **;

           if (arr[mid].compareTo(target) ** 0){
               **;
           }
           else {
               **;
           }
       }

       return left;
    }
}
posted @ 2021-10-25 21:00  振袖秋枫问红叶  阅读(68)  评论(0)    收藏  举报