二分搜索,呵呵

二分搜索 呵呵


写在前面的话:

二分搜索原理看似都懂,可是真的让我敲起来,我还真不敢保证写的必定没有bug,甚至陷入bug里不知道怎么改呢.
其实,二分法真的不是那么简单,尤为是二分的各个变种.下面一次介绍各类变种二分法,而且加上个人总结吧.数组

1. 基本二分法

在一个排好序的数组里查找一个key值.code

int search(int *arr, int n, int key)
{
    int left = 0, right = n-1;
    while(left<=right) {
        int mid = left + ((right - left) << 1);
        if (arr[mid] == key) return mid; 
        else if(arr[mid] > key) right = mid - 1;
        else left = mid + 1;
    }
    return -1;
}

若是条件变化成, 数组中的数据可能重复,要求范围等于key值最小的下标,或者找出数组中第一个大于(或等于)key值的元素下标,请看下面ast

2. 找出第一个与key相等的元素

int searchFirstEqual(int *arr, int n, int key)
{
    int left = 0, right = n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] >= key) right = mid - 1;
        else if(arr[mid] < key) left = mid + 1;
    }
    if( left < n && arr[left] == key) return left;
    return -1;
}

3. 找出最后一个与key相等的元素

int searchLastEqual(int *arr, int n, int key)
{
    int left = 0, right = n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] > key) right = mid - 1;
        else if(arr[mid] <= key) left = mid + 1; 
    }
    if( right>=0 && arr[right] == key) return right;
    return -1;
}

4. 找出第一个大于或等于key的元素

int searchFirstEqualOrLarger(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] >= key) right = mid-1;
        else if (arr[mid] < key) left = mid+1;
    }
    return left;
}

5. 找出第一个大于key的元素

int searchFirstLarger(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] > key) right = mid-1;
        else if (arr[mid] <= key) left = mid+1;
    }
    return left;
}

6. 找出最后一个小于或等于key的元素

int searchLastEqualOrSmaller(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int m = (left+right)/2;
        if(arr[m] > key) right = m-1;
        else if (arr[m] <= key) left = m+1;
    }
    return right;
}

7. 找出最后一个小于key的元素

int searchLastSmaller(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] >= key) right = mid-1;
        else if (arr[mid] < key) left = mid+1;
    }
    return right;
}

个人总结:

  1. 各类二分有一些代码部分是不变的,记住就好.也很容易记住.
int search(int *arr, int n, int key)
{
    int left=0, right=n-1;
    while(left<=right) {
        int mid = (left+right)/2;
        if(arr[mid] /****/ key) right = mid-1;
        else if (arr[mid] /****/ key) left = mid+1;
    }
    return /****/;
}

其中除了3处/****/以外,其余部分能够说是模板,很简短,用的时候敲出来就好了模板

  1. 返回值问题:
    记住left和right是key值存在的范围. 因此当要找最后一个等于key时,必然返回right . 当要找最大的小鱼key值时, 必然返回left
  2. a[mid] == key 时, 究竟是改变left仍是right?
    这里要用 "不会更坏的"想法来理解比较好.
    好比,当咱们知道要 返回值 和 条件是否包括等于 后,咱们再来看等号如何处理.
    好比:条件包括等号,返回值为left . 那么就意味着不取等号那种状况后所作的操做不能够将left变化成mid+1,由于一旦这样变化,最终返回left的时候再也不多是mid,而mid是符合条件(包括等于)要求的 . 这确定是不合理的.因此等号毫不是加在这里 .

说到这里,咱们的返回值和等号加在哪里,2个问题都解决了,就能够写完美的二分法啦!
原理

相关文章
相关标签/搜索