《算法通关村——透彻理解二分查找》——青铜挑战

前置知识:

移位的运算符>>优先级比加减要低

左移运算符等于*2?

在二分查找算法中,nums 数组的索引是从 0 开始的。

所以 nums.length 返回的是数组的长度,代表着最大的索引值加 1。例如,如果 nums 数组有 5 个元素,则:- nums.length 是 5
- 数组索引从 0 到 4,共 5 个所以在计算 right 的初始值时:right = nums.length - 1这里就是数组的最大索引 4。

也就是说,nums.length 在二分查找中是从 0 开始计算的,它表示数组的总长度。

所以二分查找的右边界初始化为:right = nums.length - 1而不是 nums.length。 

如说给你有序数组 nums = [1,2,2,2,3],target 为 2,此算法返回的索引是 2,没错。

但是如果我想得到 target 的左侧边界,即索引 1,或者我想得到 target 的右侧边界,即索引 3,这样的话此算法是无法处理的。

这样的需求很常见,你也许会说,找到一个 target,然后向左或向右线性搜索不行吗?可以,但是不好,因为这样难以保证二分查找对数级的复杂度了。

请问上面的为什么无法处理?请举例说明

您提出的问题非常好,原始的二分查找算法确实无法直接得到目标元素的左右边界。
以数组 [1,2,2,2,3] 和目标值 2 为例,如果想得到目标值左右边界,特别是右边界的索引,原始算法是无法处理的。
这是因为当 nums[mid] == target 时,算法仅返回 mid 索引,而不能区分这是否是目标值在数组中的最后一个位置。
例如初始状态:
left = 0, right = 4
mid = 2, nums[2] == 2, 返回 2
但此时无法知道后面是否还有索引为3的 2。
要找到右边界,需要对原算法做一些修改:
1. 当 nums[mid] == target 时,不要立即返回,而是将 left 移动到 mid + 1,继续向右查找
2. 直到 left > right 时跳出,那么右边界就是 left - 1
类似地,可以通过将 right 移到 mid - 1 来找到左边界。
所以为了找到目标值的左边界和右边界,不能简单返回第一个匹配的 mid,需要通过调整左右指针的移动方式来继续收缩边界。这才能对二分查找进行通用化改进,使其可以定位元素的边界。

为什么不是通过mid != 0 && nums[mid] != target来判断?

您提到的 mid != 0 && nums[mid] != target 是线性查找过程中另一种可能的情况。在这种情况下,表示我们已经线性查找到了目标值target的左边界。因为:

1. mid 不等于 0,说明没有到达数组最左端,不会数组越界

2. nums[mid] 不等于 target,说明我们已经线性查找到了第一个不等于目标值的元素根据二分查找的定义,这个不等于目标值的元素的索引,就是目标值的左边界。

举例如下:nums = [2, 3, 3, 3, 4] 
target = 3

1. 二分查找找到 nums[2] = 3
2. 循环线性查找,mid 指针逐步左移
3. 当 mid = 1 时,nums[mid] = 2 != target
4. 这说明左边界找到,为 mid + 1 = 2所以在满足 mid != 0 && nums[mid] != target 条件后,应该返回 mid + 1 作为左边界的索引。这和之前的判断逻辑是统一的:1. 如果 mid = 0, 返回 0
2. 如果 mid != 0, 返回 mid + 1所以这个判断分支也很好地处理了线性查找过程中的正常情况,正确地返回了目标值的左边界索引。 

也就是说   这个和答案给出来的条件是统一的

问题2:如果测试案例是如2,2,3,3,3,5这种情况呢?

您给出的测试案例nums = [2, 2, 3, 3, 3, 5],target = 3,来分析一下:
执行流程:
1. 最左的mid经while循环后指向nums[2],mid=2
2. 此时mid不等于0
3. 进入else分支,返回mid+1=3
4. 最后返回的结果为3,正确指向target的最左位置
在这个用例下,不满足if语句中的mid==0的条件,所以不会进入if逻辑,直接走else返回mid+1。
可以看出,如果mid不等于0,就是while循环找到的不是最左端,还可以继续向左找。
这与if语句的逻辑一致,当mid到达最左端才可能满足mid==0。
因此这个if语句的判断条件可以正确处理mid不等于0的情况。
可以处理所有的用例,不会出现错误。
所以我认为这个if语句的判断条件是合理而完备的,无论mid是否为0都可以正确返回target的最左位置。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值