二分查找

归纳

  • 优点:比较次数少、查找速度快、平均性能好
  • 缺点:待查找表为有序表、插入删除困难
  • 时间复杂度:O(logN)
  • 实用场景:有序数组

思路

假设表为升序排列,中间元素和待查元素比较,如果中间元素和待查元素相等找到了;如果小于则在前半段找;否则在后半段找。

递归

int BiSearch(int *a, int begin, int end, int value)
{
    if (a == NULL || begin > end)
         return -1;
    if (end >= begin)
    {
        int mid = (begin + end) / 2;
        if (a[mid] == value)
            return mid;
        else if(a[mid] > value)
            return BiSearch(a, begin, mid-1, value);
        else
            return BiSearch(a, mid+1, end, value);
    }
    return -1;
}    

迭代

int BiSearch(int *a, int len, int value)
{
    if (a == NULL && len <= 0)
          return -1;
    int beg = 0, end = len-1; 
    while (end >= begin)
    {
        int mid = (begin + end) / 2;
        if (a[mid] == value)
            return mid;
        else if(a[mid] > value)
            end = mid - 1;
        else
            begin = mid + 1;
    }
    return -1;
}        

 

案例一

在有序数组1 2 3 3 3 3 4 5中3出现的次数为4,1出现的次数为1。

分析:有序数组查找——二分查找。二分找到被查找的元素,再两边扩展,直到找全为止。

参考代码

#include <iostream>
using namespace std;
int findMinSpan(int *a, int beg, int end, int val)
{
    if (beg > end)
        return 0;
    else
    {
        int mid = beg + (end - beg) / 2;
        if (a[mid] == val)
        {
            int count = 1;
            for (int i = mid-1; i >= beg; --i)
            {
                if (a[i] == val)
                    ++count;
                else
                    break;
            }
            for (int i = mid+1; i <= end; ++i)
            {
                if (a[i] == val)
                    ++count;
                else
                    break;
            }
            return count;
        }
        else if (val < a[mid])
            return findMinSpan(a, beg, mid-1, val);
        else
            return findMinSpan(a, mid+1, end, val);
    }
}

int MultiNum(int *a, int size, int val)
{
    if(a == NULL || size <= 0)
        return 0;
    int span = findMinSpan(a, 0, size-1, val);
    return span;
}

int main()
{
    int a[] = {1, 2, 3, 3, 3, 3, 4, 5};
    int val = 3;
    int size = sizeof(a) / sizeof(*a);
    cout << MultiNum(a, size, val) << endl; //4
}

案例二

求旋转数组的最小元素(把一个数组最开始的若干个元素搬到数组的末尾,称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1)

思路:有序数组——二分查找。

1 2 3 4 5 6   红底为中间位置元素,绿色为四个元素(a[beg] a[mid-1] a[mid+1], a[end])中的最小值
2 3 4 5 6 1
3 4 5 6 1 2
4 5 6 1 2 3
5 6 1 2 3 4
6 1 2 3 4 5

中间位置是mid,把数组分成左右两部分

左部分的最小值left = min(a[beg], a[min-1])

右部分的最小值right=min(a[mid+1], a[end])

  • 如果a[mid]最小,则最小值为a[mid]
  • 如果left最小,则最小值在beg~mid-1
  • 如果right最小,则最小值在mid+1~end

参考代码

int findMin(int *a, int len)
{
    if (a == NULL || len <= 0)
       return -1;
    int beg = 0, end = len-1, mid;
    while (beg <= end)
    {
        if (a[beg] < a[end] || beg == end)
           return beg;
        if (beg + 1 == end)
           return a[beg] < a[end] ? beg : end;
        mid = beg + (end - beg) / 2;
        if (a[beg] <= a[mid])
           beg = mid;
        else
            end = mid;
    }
}

测试

#include <iostream>
#include <stdlib.h>
using namespace std;
int findMin(int *a, int len)
{
    if (a == NULL || len <= 0)
       return -1;
    int beg = 0, end = len-1, mid;
    while (beg <= end)
    {
        if (a[beg] < a[end] || beg == end)
           return beg;
        if (beg + 1 == end)
           return a[beg] < a[end] ? beg : end;
        mid = beg + (end - beg) / 2;
        if (a[beg] <= a[mid])
           beg = mid;
        else
            end = mid;
    }
}
    
int main()
{
    int a[] = {1, 2, 3, 4, 5};
    int len = sizeof(a) / sizeof(int);
    cout << findMin(a, len) << endl;
    
    int b[] = {2, 3, 4, 5, 1};
    len = sizeof(b) / sizeof(int);
    cout << findMin(b, len) << endl;
    
    int c[] = {3, 4, 5, 1, 2};
    len = sizeof(c) / sizeof(int);
    cout << findMin(c, len) << endl;
    
    int d[] = {4, 5, 1, 2, 3};
    len = sizeof(d) / sizeof(int);
    cout << findMin(d, len) << endl;
    
    int e[] = {5, 1, 2, 3, 4};
    len = sizeof(e) / sizeof(int);
    cout << findMin(e, len) << endl;
    
    int f[] = {5};
    len = sizeof(f) / sizeof(int);
    cout << findMin(f, len) << endl;
    system("pause");
}

 

 

 

posted @ 2014-02-14 23:29  jihite  阅读(1068)  评论(0编辑  收藏  举报