LeetCode704:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

本题是一个原汁原味的二分查找算法,属于算法基础。我在以前学习的过程中时而能一次写对,时而需要写多次。恰好最近参加了学校举行的编程能力培训,写到了类似题,但是我还是不能一次写对,主要对二分查找中条件结束判断用while(left < right) or while(left <= right) 分不清。对此进行了思考,同时也观看了一些学习视频(B站:代码随想录)。为了使印象更加深刻,因此写下此文章以便复习和加深理解。 首先,我们学习一下什么是二分查找。

二分查找

1. 基本介绍

二分查找是一种效率较高的查找算法,只适用于顺序存储结构,且要求该排序序列必须是有序的。

2. 查找步骤

定义两个指针left 和 right,分别指向当前查找区间的下界和上界,mid为中间位置(mid=(left+righ)/2)。
 	1. 如果给定值 = 中间位置的元素值,则查找成功;
 	2. 如果给定值 > 中间位置的元素值,则缩小查找区间,移动区间下界;
 	3. 如果给定值 < 中间位置的元素值,则缩小查找区间,移动区间上界。
重复操作,直到查找成功;或者某一步查找区间为空时,则查找失败。

若文字看不懂,可看以下图解过程:
![在这里插入图片描述](https://img-blog.csdnimg.cn/bf0bc9d9da2b40adaad6618e83b9714e.png#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/df47afd62f5d4dfb825b75d9cc2f2b4e.png#pic_center)

3. 算法实现

   我们在实现算法时,主要对以下两个地方有疑问:
   		1. 是用while(left<right) 还是while(left<=right)
   		2. 缩小区间时,left=mid还是left=mid+1;right=mid还是right=mid-1;
   有疑问的原因是没有很好的区分区间范围,如:
   		1. “左闭右闭”区间:当left = 0,right = numSize-1时,地址范围应为 0~numSize-1,即[0,numSize-1] 或 [left,right]。***区间边界值包括在区间内。***
		2. “左闭右开”区间:当left = 0,right = numSize,地址范围应为 0~numSize-1,即[0,numSize) 或 [left,right)。***右边区间边界值不包括在区间内。***
		3. “左开右闭”区间:即(0,numSize-1] 或 (left,right]。***左边区间边界值不包括在区间内。***
	二分查找常用“左闭右闭”、“左闭右开”,下面也只讲这两种情况的实现方式。

3.1 “左闭右闭”

“左闭右闭”的区间范围为[0,numSize-1] ,其中left=0,right=numSize-1,即:区间边界值全都包括在区间内。实现代码如下:

int search(int* nums, int numsSize, int target){
	// 定义地址区间的下界left、上界right、中间位置mid(mid=(left+right)/2)
    int left=0,right=numsSize-1,mid;
    // “左闭右闭”区间left可能等于right(区间中只有一个元素值时,如[1,1]),用while(left<=right)
    while(left<=right){
    	// 每次循环,都重新计算中间位置mid
        mid=(left+right)/2;
        // 如果mid指向的元素值 < 目标值target,target在左区间,则将left移动到mid+1的位置
        if(nums[mid]<target)
            left=mid+1;
        // 如果mid指向的元素值 > 目标值target,target在右区间,则将right移动到mid-1的位置
        else if(nums[mid]>target)
            right=mid-1;
        // 如果mid指向的元素值 = 目标值target,则返回mid(此时mid等于target)
        else
            return mid;
    }
    return -1;
}

3.1 “左闭右开”

“左闭右闭”的区间范围为[0,numSize),其中left=0,right=numSize-1,即:区间边界值包括左边界,但不包括右边界。实现代码如下:

int search(int* nums, int numsSize, int target){
	// 定义地址区间的下界left、上界right、中间位置mid(mid=(left+right)/2)
    int left=0,right=numsSize,mid;
    // “左闭右开”区间left不等于right,用while(left<right)
    while(left<right){
    	// 每次循环,都重新计算中间位置mid
        mid=(left+right)/2;
        // 如果mid指向的元素值 < 目标值target,target在左区间,则将left移动到mid+1的位置
        if(nums[mid]<target)
            left=mid+1;
        // 如果mid指向的元素值 < 目标值target,target在右区间,而区间是“左闭右开”,所以将right移动到mid,下一个查询区间不会去比较nums[mid]
        else if(nums[mid]>target)
            right=mid;
        else
            return mid;
    }
    return -1;
}

综上,使用“左闭右闭”还是“左闭右开”,主要取决于我们如何定义边界,即left、right的取值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值