东寻

导航

数字在排序数组中出现的次数

##题目描述 统计一个数字在排序数组中出现的次数。

思路

二分查找:

  1. 利用二分查找找到一个符合条件的值,然后循环搜索这个值前后重复的次数。(循环搜索使得算法一部分退化到O(m))。
    时间复杂度O(m+lgn),空间复杂度O(1)。

  2. 使用两个修改判定的二分查找,分别找到数字第一次出现和最后一次出现的位置。(推荐)
    时间复杂度O(lgn),空间复杂度O(1)。

  3. 由于题目数组的特殊性(int),可以分别对搜索值±0.5,然后使用二分查找搜索两个值。(特殊)
    时间复杂度O(lgn),空间复杂度O(1)。

代码1

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        if(array == null || array.length == 0)    return 0;
        int cnt = 0;
        int low = 0;
        int mid = 0;
        int high = array.length - 1;
        while(low <= high) {
            mid = (low + high) / 2;
            if(array[mid] == k) {
                break;
            } else if(array[mid] < k) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        for(int i = mid - 1; i >= 0; i--) {
            if(array[i] == k) {
                cnt++;
            } else {
                break;
            }
        }
        for(int i = mid; i < array.length; i++) {
            if(array[i] == k) {
                cnt++;
            } else {
                break;
            }
        }
        return cnt;
    }
}

代码2

public class Solution {
    private int biSearchfirst(int[] arr, int k, int low, int high, int mid) {
        while(low <= high) {
            mid = (low + high) / 2;
            if(arr[mid] == k) {
                if(mid == 0 ||  arr[mid-1]!=k) {
                    return mid;
                } else {
                    high = mid - 1;
                }
            } else if(arr[mid] < k) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }
    
    private int biSearchlast(int[] arr, int k, int low, int high, int mid) {
        while(low <= high) {
            mid = (low + high) / 2;
            if(arr[mid] == k) {
                if(mid == arr.length - 1 ||  arr[mid+1]!=k) {
                    return mid;
                } else {
                    low = mid + 1;
                }
            } else if(arr[mid] < k) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }
    
    public int GetNumberOfK(int [] array , int k) {
        if(array == null || array.length == 0)    return 0;
        int first = biSearchfirst(array, k, 0, array.length - 1, 0);
        int last = biSearchlast(array, k, 0, array.length - 1, 0);
        if(first == -1 || last == -1)    return 0;
        return last - first + 1;
    }
}

代码3

public class Solution {
    private int biSearch(int[] arr, double k) {
        int low = 0;
        int mid = 0;
        int high = arr.length - 1;
        while(low <= high) {
            mid = (low + high) / 2;
            if(arr[mid] < k) {
                low = mid + 1;
            } else {
                high = mid -1;
            }
        }
        return low;
    }
    public int GetNumberOfK(int [] array , int k) {
        if(array == null || array.length == 0)    return 0;
        int left = biSearch(array, k - 0.5);
        int right = biSearch(array, k + 0.5);
        return right - left;
    }
}

笔记

二叉查找,while(low <= high),若k存在:

  • 此时搜索k-ε的值,无论是否存在,mid返回的都是k的前一位,low返回的是k,high返回的是mid。
  • 此时搜索k+ε的值,无论是否存在,mid返回的都是k的后一位,high返回的是k,low返回的是mid。
  • 所以代码3里right-left实际上就是low2-low1 = k最后一次出现的后一位 - k第一次出现的位置。

若k不存在:k最后一次出现的后一位 等于 k第一次出现的位置,相当于插进来一个数。
所以二分法使用第三种代码方法时,只需要用low或者high代替mid返回就行,最后位置的逼近自然会完成。

总结,二分法搜索若存在,返回mid,若不存在,则返回low或者high(类比向上向下取整,同时取一个方向的选择)。

二分法与分治法的问题都可以先写出基本框架,再通过修改判定或添加操作解决问题。

posted on 2020-02-25 15:04  东寻  阅读(258)  评论(0编辑  收藏  举报