【算法题】二分查找及升级版

一、二分查找法(无重复数)
1、算法思想
针对一个有序数据集合a(无重复数),查找元素x的下标位置。我们可以将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到了x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x;如果x>a[n/2],则只要在数组a的右半部搜索x。
2、时间复杂度:O(logn)
3、代码实现(Java)
(1)通过while循环实现:

	public static void main(String[] args) {
        int[] a = {1, 3, 5, 6, 10, 12};
        System.out.println(binarySearch(a, 10));
    }

    public static int binarySearch(int[] a, int x) {
        if (a == null || a.length == 0) {
            return -1;
        }
        int low = 0;
        int high = a.length - 1;
        int mid;
        while (low <= high) {
            mid = low + (high - low) / 2;
            if (x == a[mid]) {
                return mid;
            }
            else if (x < a[mid]) {
                high = mid - 1;
            }
            else {
                low = mid + 1;
            }
        }
        return -1;
    }

(2)通过for循环实现

	public static void main(String[] args) {
        int[] a = {1, 3, 5, 6, 10, 12};
        System.out.println(binarySearch(a, 10));
    }

    public static int binarySearch(int[] a, int x) {
        if (a == null || a.length == 0) {
            return -1;
        }
        int low = 0;
        int high = a.length - 1;
        int mid = low + (high - low) / 2;
        for (int i = mid; low <= high; mid = low + (high - low) / 2) {
            if (x == a[mid]) {
                return mid;
            }
            else if (x < a[mid]) {
                high = mid - 1;
            }
            else {
                low = mid + 1;
            }
        }
        return -1;
    }

二、二分查找(有重复数)
如果一个n个数的有重复数的有序数据集合a,如果查找的x有连续多个,返回一个列表,保存x的起始位置;如果查找的x只有一个,则直接返回该数下标;如果没找到,则返回列表-1。

	public static void main(String[] args) {
        int[] a = {1, 3, 3, 3, 5, 6, 10, 12, 12};
        System.out.println(binarySearch(a, 12));
    }
    
    // 有序数组,有重复数,返回查找数的起始位置
    public static ArrayList<Integer> binarySearch(int[] a, int x) {
        ArrayList<Integer> result = new ArrayList<>();
        if (a == null || a.length == 0) {
            result.add(-1);
            return result;
        }
        int low = 0;
        int high = a.length - 1;
        int mid = low + (high - low) / 2;
        int start, end;
        boolean isExist = false; // 记录是否找到x
        while(low <= high) {
            mid = low + (high - low) / 2;
            if (x < a[mid]) {
                high = mid - 1;
            }
            else if (x > a[mid]) {
                low = mid + 1;
            }
            else {
                isExist = true; // 查找到至少一个x
                break;
            }
        }
        // 如果没有找到x,则直接返回-1
        if (!isExist) {
            result.add(-1);
            return result;
        }
        // 如果已经找到了元素,则先从左边继续找
        start = mid;
        end = mid;
        // 如果起始位置大于等于0且a[start] == x,则继续
        while(start >= 0 && a[start] == x) {
            start--;
        }
        start = start + 1;
        // 如果终点位置小于等于数组长度且a[end] == x,则继续
        while(end <= high && a[end] == x) {
            end++;
        }
        end = end - 1;
        // 如果起始位置相等,说明只找到一个数
        if (start == end) {
            result.add(start);
        }
        else {
            result.add(start);
            result.add(end);
        }
        return result;
    }

三、二分查找第一个等于给定的数的下标

	public static void main(String[] args) {
        int[] a = {1, 3, 3, 3, 3, 6, 10, 12, 12};
        System.out.println(binarySearch(a, 3));
    }

    public static int binarySearch(int[] a, int x) {
        if (a == null || a.length == 0) {
            return -1;
        }
        int low = 0;
        int high = a.length - 1;
        int mid;
        while(low <= high) {
        	mid = low + (high - low) / 2;
            if (x < a[mid]) {
                high = mid - 1;
            }
            else if(x > a[mid]){
                low = mid + 1;
            }
            // 否则,x == a[mid],至少找到一个值等于x
            else {
                // 如果中间位置等于最左边,或者a[mid - 1] != x,说明再往左没有找到等于x的值,则此时第一个等于x的值位置就是mid
                if (mid == low || a[mid - 1] != x) {
                    return mid;
                }
                high = mid - 1;
            }
        }
        return -1;
    }

四、二分查找最后一个等于给定的数的下标

	public static void main(String[] args) {
        int[] a = {1, 3, 3, 3, 3, 6, 10, 12, 12};
        System.out.println(binarySearch(a, 3));
    }

    public static int binarySearch(int[] a, int x) {
        if (a == null || a.length == 0) {
            return -1;
        }
        int low = 0;
        int high = a.length - 1;
        int mid;
        while (low <= high) {
            mid = low + (high - low) / 2;
            if (x < a[mid]) {
                high = mid - 1;
            }
            else if(x > a[mid]){
                low = mid + 1;
            }
            else {
                if (mid == high || a[mid + 1] != x) {
                    return mid;
                }
                low = mid + 1;
            }
        }
        return -1;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章