你真的会二分查找吗?

二分查找简介

二分查找是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。

时间复杂度为O(log n)

例如对于{1,3,4,6,7,8,10,13,14}构成的有序序列对应的查找树:



算法实现

最简单的二分查找算法:

即找到返回下标,找不到返回-1,(坐标范围:[0~nums.size() - 1])

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <string>
#include <sstream>
#include <cstring>
#include <cmath>
using namespace std;

int BinarySearch(vector<int>& nums, int target)
{
	int left = 0;
	int right = nums.size() - 1;
    while(left <= right)
    {
        int mid = left + ((right - left) >> 1);
        if (nums[mid] > target)
        {
            right = mid - 1;
        }
        else if(nums[mid] < target)
        {
            left = mid + 1;
        }
        else
        {
        	return mid;
        }
    }
    return -1;
}

int main()
{
	vector<int>vec{1,3,5,5,5,7,8};
	cout << BinarySearch(vec, 3);
	return 0;
}

二分查找的变种

查找第一个与target相等的元素位置,找不到返回-1

int BinarySearchLeft(vector<int>& nums, int target)
    {
        int left = 0;
        int right = nums.size() - 1;
        while(left <= right)
        {
            int mid = left + ((right - left) >> 1);
          
            if (nums[mid] >= target)
            {
                right = mid - 1;
            }
            else
            {
                left = mid + 1;
            }
        }
        if (left <= (int)nums.size() - 1 && nums[left] == target)
        {
            return left;
        }
        return -1;
    }

查找最后一个与target相等的元素位置,找不到返回-1


int BinarySearchRight(vector<int>& nums, int target)
    {
        int left = 0;
        int right = nums.size() - 1;
        while(left <= right)
        {
            int mid = left + ((right - left) >> 1);
          
            if (nums[mid] > target)
            {
                right = mid - 1;
            }
            else
            {
                left = mid + 1;
            }
        }
        if (right >= 0 && nums[right] == target)
        {
            return right;
        }
        return -1;
    }

查找最后一个小于key的元素

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <string>
#include <sstream>
#include <cstring>
#include <cmath>
using namespace std;

int BinarySearch(vector<int>& nums, int target)
{
	int left = 0;
	int right = nums.size() - 1;
    while(left <= right)
    {
        int mid = left + ((right - left) >> 1);
        if (nums[mid] >= target)
        {
            right = mid - 1;
        }
        else if(nums[mid] < target)
        {
            left = mid + 1;
        }
    }
    return right;
}

int main()
{
	vector<int>vec{1,3,5,5,5,7,8};
	cout << BinarySearch(vec, 7);
	return 0;
}

查找第一个大于key的元素

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <string>
#include <sstream>
#include <cstring>
#include <cmath>
using namespace std;

int BinarySearch(vector<int>& nums, int target)
{
	int left = 0;
	int right = nums.size() - 1;
    while(left <= right)
    {
        int mid = left + ((right - left) >> 1);
        if (target >= nums[mid])
        {
            left = mid + 1;
        }
        else if(target < nums[mid])
        {
            right = mid - 1;
        }
    }
    return left;
}

int main()
{
	vector<int>vec{1,3,5,5,5,7,8};
	cout << BinarySearch(vec, 5);
	return 0;
}
如果返回nums.size()则说明不存在。

查找第一个等于或者小于key的元素

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <string>
#include <sstream>
#include <cstring>
#include <cmath>
using namespace std;

int BinarySearch(vector<int>& nums, int target)
{
    int left = 0;
    int right = nums.size() - 1;
    while(left <= right)
    {
        int mid = left + ((right - left) >> 1);
        if (target <= nums[mid])
        {
            right = mid - 1;
        }
        else
        {
            left = mid + 1;
        }
    }
    if (nums[left] != target)
    {
        return left - 1;
    }
    return left;
}

int main()
{
    vector<int>vec{1,3,5,5,5,7,8};
    cout << BinarySearch(vec, 5);
    return 0;
}

查找最后一个等于或者大于key的元素

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <string>
#include <sstream>
#include <cstring>
#include <cmath>
using namespace std;

int BinarySearch(vector<int>& nums, int target)
{
    int left = 0;
    int right = nums.size() - 1;
    while(left <= right)
    {
        int mid = left + ((right - left) >> 1);
        if (target >= nums[mid])
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }
    if (nums[right] != target)
    {
        return right + 1;
    }
    return right;
}

int main()
{
    vector<int>vec{1,3,5,5,5,7,8};
    cout << BinarySearch(vec, 5);
    return 0;
}


STL中的二分查找

STL中与二分查找相关的函数有4个,分别是lower_bound, upper_bound, equal_range和binary_search,下面通过一个简单的例子说明各个函数的使用方法。
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
	int a[6] = {1, 2, 2, 2, 3, 4}; //已排序
	vector<int> v(a, a + 6);

	typedef vector<int>::iterator It; //迭代器类型
	It st = v.begin(), ed = v.end();  //起始迭代器

	It p1 = lower_bound(st, ed, 2);//>=2的第一个位置
	printf("%d
", p1 - st);       // 1

	It p2 = upper_bound(st, ed, 2);//>2的第一个位置
	printf("%d
", p2 - st);       // 4

	pair<It, It> p = equal_range(st, ed, 2);        //==2的位置
	printf("%d-%d
", p.first - st, p.second - st); // 1-4

	bool exist = binary_search(st, ed, 2); //2是否存在
	printf("%d
", exist);                 //1 (是)
}
其中每个函数实现的功能如下:
binary_search:查找某个元素是否出现。
lower_bound:查找第一个大于或等于某个元素的位置。
upper_bound:查找第一个大于某个元素的位置。
equal_range:查找某个元素出现的起止位置。注意,终止位置为最后一次出现的位置加一。
其中lower_bound和upper_bound的功能可能比较别扭,可以这样看:对于已排序的数组1 2 2 2 3 4,元素2出现了3次,第一次出现的位置为1,最后一次的位置为3,lower_bound即为这个第一次出现的位置,而upper_bound本意是要标志最后一次出现的位置,但STL中习惯使用左闭右开的区间,于是upper_bound返回的结果应该为最后一次出现的位置的下一个位置。当查找的元素一次都未出现时,二者返回的结果都是第一个大于该元素的位置。

参考:http://www.cnblogs.com/ider/archive/2012/04/01/binary_search.html


Keep it simple!
作者:N3verL4nd
知识共享,欢迎转载。
原文地址:https://www.cnblogs.com/lgh1992314/p/6616322.html