二分查找

41 阅读4分钟

概述

  • 基本原理:数组是一个有序数组,对数组对半分,比较中间元素是否与查找元素相等,如果相等则返回下标;如果当前中间元素的值大于目标值,将中间元素往前一个元素作为新的结束下标,重新进行二分查找;如果当前中间元素的值小于目标值,将中间元素往后一个元素作为新的起始下标,重新进行二分查找。

最基本的实现

循环实现

public class BinarySearch {

    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }

        int start = 0;
        int end = nums.length - 1;

        while (end >= start) {
            int mid = start + ((end - start) >> 1); // 等效于 (start + end)/2,避免溢出
            if (nums[mid] == num) {
                return mid;
            } else if (nums[mid] > num) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }
        return -1;
    }
}

递归实现

public class BinarySearch {

    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }
        return doSearch(nums, num, 0, nums.length - 1);
    }

    /**
     * @param nums
     * @param num
     * @param start
     * @param end
     * @return
     */
    public static int doSearch(int[] nums, int num, int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = start + ((end - start) >> 1);
        if (nums[mid] == num) {
            return mid;
        } else if (nums[mid] > num) {
            return doSearch(nums, num, start, mid - 1);
        } else {
            return doSearch(nums, num, mid + 1, end);
        }
    }
}

查找第一个相等的元素

循环实现

public class BinarySearchFirstEqual {

    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }

        int start = 0;
        int end = nums.length - 1;
        int idx = -1; // 缓存下标结果

        while (end >= start) {
            int mid = start + ((end - start) >> 1); // 等效于 (start + end)/2,避免溢出
            if (nums[mid] == num) {
                // 每次会刷新上限,因此最后一次设置的下标就是第一个等于的下标
                idx = mid;
                end = mid - 1;
            } else if (nums[mid] > num) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }
        return idx;
    }

}

递归实现

public class BinarySearchFirstEqual {
    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }
        return doSearch(nums, num, 0, nums.length - 1);
    }

    /**
     * @param nums
     * @param num
     * @param start
     * @param end
     * @return
     */
    public static int doSearch(int[] nums, int num, int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = start + ((end - start) >> 1);
        if (nums[mid] == num) {
            int idx = doSearch(nums, num, start, mid - 1);
            return idx == -1 ? mid : idx;
        } else if (nums[mid] > num) {
            return doSearch(nums, num, start, mid - 1);
        } else {
            return doSearch(nums, num, mid + 1, end);
        }
    }
}

查找第一个大于等于的元素

循环实现

public class BinarySearchFirstGreaterEqual {

    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }

        int start = 0;
        int end = nums.length - 1;
        int idx = -1;

        while (end >= start) {
            int mid = start + ((end - start) >> 1); // 等效于 (start + end)/2,避免溢出
            // 大于等于直接合并就好了。并且缓存下标。如果能找到更好的,那就更新。
            if (nums[mid] >= num) {
                idx = mid;
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }
        return idx;
    }
}

递归实现

public class BinarySearchFirstGreaterEqual {
    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }
        return doSearch(nums, num, 0, nums.length - 1);
    }

    /**
     * @param nums
     * @param num
     * @param start
     * @param end
     * @return
     */
    public static int doSearch(int[] nums, int num, int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = start + ((end - start) >> 1);
        if (nums[mid] >= num) {
            int idx = doSearch(nums, num, start, mid - 1);
            return idx == -1 ? mid : idx;
        } else {
            return doSearch(nums, num, mid + 1, end);
        }
    }
}

查找最后一个等于的元素

循环实现

public class BinarySearchLastEqual {

    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }

        int start = 0;
        int end = nums.length - 1;
        int idx = -1;

        while (end >= start) {
            int mid = start + ((end - start) >> 1); // 等效于 (start + end)/2,避免溢出
            if (nums[mid] == num) {
                idx = mid;
                start = mid + 1;
            } else if (nums[mid] > num) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }
        return idx;
    }
}

递归实现

public class BinarySearchLastEqual {
    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }
        return doSearch(nums, num, 0, nums.length - 1);
    }

    /**
     * @param nums
     * @param num
     * @param start
     * @param end
     * @return
     */
    public static int doSearch(int[] nums, int num, int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = start + ((end - start) >> 1);
        if (nums[mid] == num) {
            int idx = doSearch(nums, num, mid + 1, end);
            return idx == -1 ? mid : idx;
        } else if (nums[mid] > num) {
            return doSearch(nums, num, start, mid - 1);
        } else {
            return doSearch(nums, num, mid + 1, end);
        }
    }
}

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

循环实现

public class BinarySearchLastLessEqual {

    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }

        int start = 0;
        int end = nums.length - 1;
        int idx = -1;

        while (end >= start) {
            int mid = start + ((end - start) >> 1); // 等效于 (start + end)/2,避免溢出
            if (nums[mid] <= num) {
                idx = mid;
                start = mid + 1;
            } else if (nums[mid] > num) {
                end = mid - 1;
            }
        }
        return idx;
    }
}

递归实现

public class BinarySearchLastLessEqual {
    /**
     * @param nums 数组
     * @param num  被查找的数据
     * @return 下标
     */
    public static int search(int[] nums, int num) {
        if (nums == null) {
            return -1;
        }
        return doSearch(nums, num, 0, nums.length - 1);
    }

    /**
     * @param nums
     * @param num
     * @param start
     * @param end
     * @return
     */
    public static int doSearch(int[] nums, int num, int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = start + ((end - start) >> 1);
        if (nums[mid] <= num) {
            int idx = doSearch(nums, num, mid + 1, end);
            return idx == -1 ? mid : idx;
        } else {
            return doSearch(nums, num, start, mid - 1);
        }
    }
}