八、二分查找(Binary Search)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012736685/article/details/83445234

一、概述

二分查找(Binary Search,也称折半查找)——针对有序数据集合的查找算法

1、基本思想

类似分治思想,每次都通过跟区间的中间元素进行对比,将代查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为0(不存在该元素)。

2、时间复杂度分析——O(logn)

不妨假设数据量为n,每次查找后数据量均为原来的1/2,最坏的情况下,直到查找区间被缩小为空,才停止。设经过k次区间缩小操作区间缩小到1,可得 k=log2n 时间复杂度为O(k),也就是O(logn)。

O(1) 常量级时间复杂度的算法可能还没有O(logn)的算法执行效率高。因为:大O标记法表示时间复杂度时,会省略掉常数、系数和低阶,O(1)可能表示一个非常大的常量值,eg: O(10000)。

二、简单实现

二分查找最简单的情况:有序数组中不存在重复元素

1、递归方法实现

#include<iostream>
using namespace std;

int BinarySearchRecursive(int *array, int low, int high, int key)
{
    if(low > high)
        return -1;

	// int mid = (low + high) / 2;  若low和high比较大,两者之和就可能溢出
	int mid = low + (high - low) / 2;  
	// 若性能有要求,可将除以2操作转化为位运算,以提升效率
	// int mid = low + ((high - low)>>1)

    if(array[mid] == key)
        return mid;
    else if(array[mid] < key)
        return BinarySearchRecursive(array, mid+1, high, key);
    else
        return BinarySearchRecursive(array, low, mid-1, key);
}

int main()
{
    int array[10];
    for(int i = 0; i < 10; i++)
        array[i] = i;

    cout<< "Recursive:"<<endl;
    cout<<"postion:"<<BinarySearchRecursive(array,0,9,4)<<endl;
    return 0;
}

2、非递归方法

int BinarySearch(int *array, int aSzie, int key)
{
    if(array == NULL || aSize == 0)
        return -1;
    int low = 0;
    int high = aSize - 1;
    int mid = 0;

    while(low <= high)
    {
        mid = low + (high - low) / 2;

        if(array[mid] == key)
            return mid;
        else if(array[mid] < key)
            low = mid + 1;
        else
            high = mid - 1;
    }
    return -1;
}

3、应用场景的局限性

适用于:插入、删除操作都不频繁,一次排序多次查找且数据量较大的场景。

  • 二分查找依赖的是顺序表数据结构,也就是数组(可以按照下标随机访问元素)

  • 二分查找的数据是有序的:排序算法时间复杂度最低为O(nlogn),若动态变化的数据集合,不再适用。

  • 适合数据量较大的场景,而非特别大。因为数组为了支持随机访问的特性,要求内存空间连续。

特例:若数据之间的比较操作特别耗时,不管数据量大小,都推荐使用二分查找。同on个过尽可能较少比较次数来提升性能。

三、应用实例

1、实例

假设我们有 1000 万个整数数据,每个数据占 8 个字节,如何设计数据结构和算法,快速判断某个整数是否出现在这 1000 万数据中? 我们希望这个功能不要占用太多的内存空间,最多不要超过 100MB,你会怎么做呢?

2、分析

由于每个数据占 8 个字节,内存占用差不多为80M < 100M,符合内存限制。
==》先从小到大排序,然后利用二分查找算法找数据。

注意:散列表、二叉树这些支持快速查找的动态数据结构。但是,在该情况却不行的。虽然在大部分情况下,用二分查找可以解决的问题,用散列表、二叉树都可以解决。但是都会需要比较多的额外的内存空间。所以,如果用散列表或者二叉树来存储这
1000 万的数据,用 100MB 的内存肯定是存不下的。而二分查找底层依赖的是数组,除了数据本身之外,不需要额外存储其他信息,是最省内存空间的存储方式,所以刚好能在限定的内存大小下解决这个问题。

四、二分查找的进级(变形问题)

前提:数据都是从小到大排列的,且数据中存在重复的数据。

1、常见的变形问题

  • 查找第一个值等于给定值的元素
  • 查找最后一个值等于给定值的元素
  • 查找第一个大于等于给定值的元素
  • 查找最后一个小于等于给定值的元素

2、查找第一个值等于给定值的元素

(1)方法一

int bsearch1(int *array, int aSize, int key)
{
    int low = 0;
    int high = aSize - 1;
    while (low <= high) {
        int mid = low + ((high - low)/2);
        if(array[mid] > key)
            high = mid - 1;
        else if(array[mid] < key)
            low = mid + 1;
        else
        // 当array[mid]=key时,
        // 需要确认一下这个 array[mid] 是不是第一个值等于给定值的元素
        {
            // 若mid等于0,说明该元素为第一个元素;
            // array[mid]的前一个元素array[mid-1]不等于key,说明该元素为第一个元素;
            if((mid == 0)||(array[mid-1]!=key))
                return mid;
            // 否则更新区间
            else
                high = mid - 1;
        }
    }
    return -1;
}

(2)方法二

int bsearch2(int *array, int aSize, int key)
{
    int low = 0;
    int high = aSize - 1;
    
    while (low <= high)
    {
        int mid = low + ((high - low) / 2);
        if(array[mid] >= key)
        {
            high = mid - 1;
        }
        else
        {
            low = mid + 1;
        }
    }
    
    if(array[low] == key) 
        return low;
    else 
        return -1;
}

3、查找最后一个值等于给定值的元素

int bSearch(int *array,int aSize,int key)
{
    int low = 0;
    int high = aSize - 1;
    while(low <= high)
    {
        int mid = low + ((high - low) / 2);
        if (array[mid] >= key)
            high = mid - 1;
        else if (array[mid < key]) {
            low = mid + 1;
        }
        else
        {
            if((mid == aSize - 1)||(array[mid+1] != key))
                return mid;
            else
                low = mid + 1;
        }
    }
    return -1;
}

4、查找第一个大于等于给定值的元素

int bSearch(int *array,int aSize,int key)
{
    int low = 0;
    int high = aSize - 1;
    while(low <= high)
    {
        int mid = low + ((high - low) / 2);
        if (array[mid] >= key)
        {
            if((mid == 0)||(array[mid-1] < key))
                return mid;
            else
                high = mid - 1;
        }
        else
        {
            low = mid + 1;
        }
    }
    return -1;
}

5、查找最后一个小于等于给定值的元素

int bSearch(int *array,int aSize,int key)
{
    int low = 0;
    int high = aSize - 1;
    while(low <= high)
    {
        int mid = low + ((high - low) / 2);
        if (array[mid] > key)
        {
            high = mid - 1;
        }
        else
        {
            if((mid == aSize - 1)||(array[mid+1] > key))
                return mid;
            else
                low = mid + 1;
        }
    }
    return -1;
}

猜你喜欢

转载自blog.csdn.net/u012736685/article/details/83445234
今日推荐