二分查找及其变种大全详解

Posted bitcarmanlee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分查找及其变种大全详解相关的知识,希望对你有一定的参考价值。

1.经典二分查找

经典的二分查找,是对一个已排序的数组,通过查找数组中间位置元素值的大小与要查找的目标值大小进行对比,从而缩小数组的搜素范围,直到最后找到目标值出现的位置,或者将数组查找完毕一直没有找到目标值直至算法结束。

其基本的算法框架为(c++版本)

int binaray_search(int[] nums, int n, int target) 
	int left = ..., right = ..., mid;
	while(...) 
		int mid = (left+right)/2;
		if (nums[mid] == target) 
			...
		 else if (nums[mid] > target) 
			right = ...
		 else if (nums[mid] < target) 
			left = ...
		
	

二分查找的基本版本以及其他各种变种,就是针对上面的框架进行相应修改。

2.经典版本二分查找

首先我们实现经典的二分查找。

int classical_version(int nums[], int n, int target) 
    int left=0, right=n-1, mid;
    while(left<=right) 
        mid = (left+right) / 2;
        if (nums[mid] > target) right=mid-1;
        else if (nums[mid] < target) left=mid+1;
        else return mid;
    
    return -1;

其中,n是数组nums的长度,target是需要查找的目标数值。
注意循环退出的条件是left<=right,等号不能丢。因为如果退出条件为left<right,那么当left=right时,循环就退出了,这样将少查一个元素。如果该元素刚好为target,程序将会返回错误的结果。

3.查找target出现的第一个位置

上面的经典算法,适合数组中没有重复元素的情况。如果数组中有重复元素,而且需要查找的target刚好是重复元素,那么找到的只是其中一个元素。如果要查找指定的第一个或者最后一个出现位置,上面的写法是搞不定的。
下面我们来实现查找target出现的第一个位置。

int find_first_target(int nums[], int n, int target) 
    int left=0, right=n-1, mid;
    while(left<=right) 
        mid = (left+right)/2;
        if (nums[mid] > target) right=mid-1;
        else if (nums[mid] < target) left=mid+1;
        else 
            if (mid == 0 || nums[mid-1] != target) return mid;
            else right=mid-1;
        
    
    return -1;

注意与经典版本不一样的地方在于,经典版本在nums[mid] == target时就支持返回退出了。而如果数组中有重复元素,需要进一步进行判断。因为是要找到出现的第一个位置,所以搜素范围需要向左移动。我们将mid-1个元素与target进行对比,如果不相等,直接返回。如果相等,则将right向左移动一个位置。

4.查找target出现的最后一个位置

很显然思路与上面类似,区别就是查找第一个位置,当nums[mid]==target搜索范围向左移动,而查找最后一个位置则是向右移动。

int find_last_target(int nums[], int n, int target) 
    int left=0, right=n-1, mid;
    while(left<=right) 
        mid = (left+right)/2;
        if (nums[mid] > target) right=mid-1;
        else if (nums[mid] < target) left=mid+1;
        else 
            if (mid==right || nums[mid+1] != target) return mid;
            else left = mid+1;
        
    
    return -1;

5.查找第一个大于等于target的元素

有时候我们还需要查找第一个大于等于target的元素。先上代码,然后再针对代码分析。

int find_first_euqal_big_element(int nums[], int n, int target) 
    int left=0, right=n-1, mid;
    while(left <= right) 
        mid = (left+right) / 2;
        if(nums[mid] < target) left=mid+1;
        else 
            if (mid == 0 || nums[mid-1] < target) return mid;
            else right=mid-1;
        
    
    return -1;

上面算法几个要点:
1.因为需要找的是大于等于的第一个元素,这个元素必然出现在right一侧,left一侧无需做特殊处理,只需要跟原始版本一样即可。
2.如果mid==0且nums[mid]>=target,直接返回mid。
3.如果nums[mid]>=target,且nums[mid-1]<target,说明mid则是第一个大于等于target值的位置。否则,right侧向左移动。

6. 查找最后一个小于等于target的元素

int find_last_equal_small_element(int nums[], int n, int target) 
    int left=0, right=n-1, mid;
    while(left <= right) 
        mid = (left+right)/2;
        if(nums[mid] > target) right=mid-1;
        else 
            if (mid==n-1 || nums[mid+1] > target) return mid;
            else left=mid+1;
        
    
    return -1;


与第五部分类似的是:
1.因为查找的是小于等于target的元素,所以必然是出现在left侧,right一侧无需特殊处理。
2.如果mid==n-1且nums[mid]<=target,直接返回mid。
3.如果nums[mid]<=target,且nums[mid+1]>target,说明mid则是第一个小于等于target值的位置。否则,left侧向右移动。

以上是关于二分查找及其变种大全详解的主要内容,如果未能解决你的问题,请参考以下文章

面试前必知必会的二分查找及其变种

二分法——二分查找的变种

二分查找+所有细节保姆级详解

二分变种

二分查找-变种

二分查找以及变种的总结