[Binary Search] Do you really know binary search? (C language implementation, training with questions)

Beginners tend to underestimate the function of binary search after learning binary search. It has a wider range of usage scenarios , not just looking for specific values . Random changes will greatly change the function of binary search.
Just like Knuth The boss (the one who invented the KMP algorithm) said binary search:

The idea is simple, the devil is in the details.

This article mainly introduces from binary search 两种形式实现to 题目训练strengthening the understanding and use of binary search, and more importantly, to learn to be flexible

Two implementations of binary search

As the saying goes, nothing changes

These two writing methods are the basis of binary search, and any change is inseparable from these two writing methods.
This is the basic framework of binary search:

int Search(int[] nums,int len,int target) 
{
    
    
    int left = 0, right = ...;

    while(...) 
    {
    
    
        int mid = left + (right - left) / 2;
        //不直接相加除2是为了防止溢出
        if (nums[mid] > target) 
            right = ...
        else if (nums[mid] < target) 
            left = mid + 1;
        else
            return mid;
    }
    return -1;
}

We found that the only thing that needed to change in this framework was the case of whilethe main loop and this is where the binary search details and the devilnums[mid]>target

We also need to know that binary search can traverse the array at least once if we want to search, and there are two commonly used methods to traverse this array: left closed right open and left closed right closed

Close left and close right

So what is left-closed-right-closed?
Let's look at the following example:

If there is an array {1, 2, 3, 4}
, then the subscript is {0, 1, 2, 3}
, then left=0; right=len-1;
then the subscript of the search range is [left,right], which is what we say left closed right closed

Next, look at the two places that need to be changed:

Now that we have the subscript range of retrieval
, we can know left==rightthat when is meaningful,
the main loop can be written as while(left<=right)
while target < arr[mid], because target!=arr[mid]
soright=mid-1

Code:

int search(int arr[], int len, int target)
//target是查找的目标值
{
    
    
	int left = 0;
	int right = len - 1;
	while (left <= right)
	{
    
    
		int mid = (right - left) / 2 + left;
		if (target < arr[mid])
			right = mid - 1;
		else if (target > arr[mid])
			left = mid + 1;
		else
			return mid;
	}
	return -1;
}

left closed right open

What is left-closed-right-open?

If there is an array {1, 2, 3, 4}
, then the subscript is {0, 1, 2, 3},
then left=0; right=len;
then the subscript of the search range is [left,right), which is what we call left close right open

Next, look at two details:

Now that we have the subscript range of the search,
we can know left==rightthat time is meaningless .
The main loop can be written as while(left<right)
while target < arr[mid]time, because target!=arr[mid]it is a left-closed right-open interval ,
so right=midthere is no need to recheck arr[mid]

Code:

int search(int arr[], int len, int target)
{
    
    
	int left = 0;
	int right = len ;
	while (left < right)
	{
    
    
		int mid = (right - left) / 2 + left;
		if (target < arr[mid])
			right = mid;
		else if (target > arr[mid])
			left = mid + 1;
		else
			return mid;
	}
	return -1;
}

topic training

Minimum number of rotated arrays

Niuke link , the link is here.
insert image description here

I believe that everyone has heard such a sentence when learning binary search. The premise of binary search is an ordered array , but is this really the case?
The answer is obviously no,

If it is clear that after the dichotomy, the answer exists on one side of the dichotomy, then dichotomy can be used.

Ideas:

We found that this question did not give the target value
. If there is a target value target, then directly compare arr[mid] with target.
If there is no target value, it can generally be considered. 端点
Here we consider the right endpoint .
At that time At that time , it means that mid is on the right side of the minimum value, but it is not sure whether mid is the minimum value . The left-closed right-open interval, so in the main loop because there will be repeated phenomena, so when repeatedarr[mid]>target
arr[mid]<targetright=mid
left<rigth
right--

Code:

int minNumberInRotateArray(int* nums, int numsLen ) 
{
    
    
    // write code here二分查找
    int right=numsLen-1;
    int left=0;
    int mid=0;
    while(left<right)
    {
    
    
        mid=(left+right)/2;
        if(nums[mid]>nums[right])
//利用right是因为利用右界实现比左界便捷,不需要额外的判断
//情况1 :1 2 3 4 5 , nums[mid] = 3. target = 1, 
//nums[mid] > target, 答案在mid 的左侧

//情况2 :3 4 5 1 2 , nums[mid] = 5, target = 3, 
//nums[mid] > target, 答案却在mid 的右侧
            left=mid+1;
        else if(nums[mid]<nums[right])
            right=mid;
        else 
            right--;
            //使用left++时会跳过最小值
            //例如当直接为顺序数组时,会直接跳过
    }
    return nums[left];
}

The number of times a number appears in an ascending array (upper and lower bounds problem)

Niuke link , the link is here.
insert image description here
Ideas:

The first time I saw this question, I thought of binary search, because this question is also looking for a number, but it is the upper and lower bounds of this number, but
we need to make some adjustments,
for example, when looking for the left bound, the equality needs to be shifted to the left, and when looking for the right bound , equal to the right,
but also pay attention to the judgment of special circumstances

Code:

int GetNumberOfK(int* data, int dataLen, int k) 
{
    
    
    // write code here
    if (dataLen == 0)
        //特殊情况,数组长度为0
        return 0;
    else if (data[dataLen - 1]<k || data[0]>k)
        //特殊情况,k小于或大于最小值或最大值
        return 0;
    else
    {
    
    
        int leftbound, rightbound;
        int mid;
        int left = 0, right = dataLen - 1;
        if (left == right)
            return k == data[left];
        else
        {
    
    
            //寻找左界
            while (left <= right)
            {
    
    
                //可以利用第一种情况,左闭右闭
                //第二种也可以,但是要改主循环条件为left<right
                //right要=mid
                mid = (left + right) / 2;
                if (data[mid] > k)
                    right = mid - 1;
                else if (data[mid] < k)
                    left = mid + 1;
                else
                    right--;
            }
            leftbound = left;
            //寻找右界
            left = 0, right = dataLen - 1;
            while (left <= right)
            {
    
    
                mid = (left + right) / 2;
                if (data[mid] > k)
                    right = mid - 1;
                else if (data[mid] < k)
                    left = mid + 1;
                else
                    left++;
                rightbound = right;
            }
            return rightbound - leftbound + 1;
        }
    }
}

If there is a mistake, welcome to correct it, definitely correct it

Guess you like

Origin blog.csdn.net/2301_78636079/article/details/132641291