代码随想录刷题|数组 | 二分法

二分查找适用与有序的数据结构,比如有序数组。

二分查找关键点

  1. 关于闭区间和开区间的定义
    如果我能取到这个数那就是闭区间,如果取不到而是取到旁边的数那就是开区间
    1.1.1 比如 int left = 0 那就是闭区间, 因为最开始的这个数是一定得取得到的所以左边一定是闭区间
    1.1.2 那么开区间是对象是右边,如果 int right = nums.size() - 1 那就是闭区间,因为数组是从零开始,比如数组有10个元素,那下标范围就是0~9, 所以右指针为数组大小-1代表的是最后一个元素,那就是取得到最后一个元素,那就是闭区间。由于闭区间是取得到的元素,因此闭区间所指的值是参与运算的。
    1.1.3 那么开区间呢,开区间就是取不到,比如 int right = nums.size() 这实际上已经超出了数组的范围了,所以这是取不到的值,并且开区间所指的值是不参与运算的。

  2. 开闭区间的while判断条件
    上面说的闭区间所指的值参与运算,开区间所指的值不参与运算在哪里发挥作用呢?
    2.2.1 闭区间是while(left<=right)
    请思考一下闭区间的最小区间是什么样的,此时的middle等于什么,循环的结束条件是什么?
    可以来思考非常极端的例子,就是left和right靠的非常近甚至相等,闭区间的定义是 [left, right] 这里的left是可以等于right的,当left=right那就区间已经缩小到只有一个元素的程度了,此时middle=left=right,此时还满足while(left<=right)还要再进行一次运算并且是最后一次,这最后一次运算结果当middle是期望值那就中,不是的话就会要么left跑到right右边去或者right跑到left的左边来,循环while(left<=right)结束。这个非常极端的区间只有一个值例子事实上middle=left=right的时候已经用了left和right的值了,所以right更新要middle-1,left要更新middle+1。
    2.2.2 开区间 while(left<right)
    请思考一下此时最小区间应该是怎样的,此时的middle应该等于什么,此时的循环结束条件是什么?
    由于是开区间 [left, right) 所以left是不等于right的,由于int middle = left + ((right - left) / 2) 是向下取整的,所以最后一次运算的middle=left,此时如果middle是期望值就中,不是的话此时要么middle赋值给right,此时left=right导致循环while(left<right)结束,要么左边闭区间left + 1导致left=right导致循环while(left<right)结束。因为最后用到的是left等于middle的值,这个非常极端小的区间至少有两个值,准确地说是左边的值取得到右边的值取不到,因此左边更新的时候应该middle+1(毕竟左边取得到的时候已经使用过了),右边更新的时候应该right=middle(因为没有使用到但是还是要保留这个值作为开区间的边界)

以上是大白话的说明,接下来是代码演示

代码演示

1. 区间定义

1.1左闭右闭

int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]

1.2 左闭右开

int left = 0;
int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)

2.循环条件和区间赋值

2.1 左闭右闭

while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
    int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
    if (nums[middle] > target) {
        right = middle - 1; // target 在左区间,所以[left, middle - 1]
    } else if (nums[middle] < target) {
        left = middle + 1; // target 在右区间,所以[middle + 1, right]
    } else { // nums[middle] == target
        return middle; // 数组中找到目标值,直接返回下标
    }
}

2.2 左闭右开

while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
    int middle = left + ((right - left) >> 1);
    if (nums[middle] > target) {
        right = middle; // target 在左区间,在[left, middle)中
    } else if (nums[middle] < target) {
        left = middle + 1; // target 在右区间,在[middle + 1, right)中
    } else { // nums[middle] == target
        return middle; // 数组中找到目标值,直接返回下标
    }
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值