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

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

二分查找关键点

  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; // 数组中找到目标值,直接返回下标
    }
}
在Java中,数组二分查找(也称为折半查找或对分查找)通常用于已排序的数组中找到特定元素的位置。这种算法的时间复杂度为O(log n),相比于线性查找(时间复杂度为O(n)),效率更高。以下是基本的数组二分查找的步骤: 1. 确定搜索范围:初始时,左指针`low`设为0,右指针`high`设为数组长度减一。 2. 计算中间索引:`mid = (low + high) / 2`。 3. 比较目标值:如果中间元素等于目标值,返回其索引;如果目标值小于中间元素,将`high`设置为`mid - 1`;如果目标值大于中间元素,将`low`设置为`mid + 1`。 4. 重复步骤2和3,直到找到目标值或搜索范围为空(`low > high`)。 然而,对于寻找数组中的最大值或最小值,由于我们并不需要精确的索引,而是结果本身,可以稍微调整一下思路。可以直接从头开始比较每个元素,每次遍历都将当前遇到的元素分别作为最大值或最小值,直到遍历完整个数组。这种方法非常简单,时间复杂度为O(n)。 下面是寻找数组中最大值和最小值的基本代码片段: ```java public int findMax(int[] array) { if (array.length == 0) return Integer.MIN_VALUE; int max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] > max) { max = array[i]; } } return max; } public int findMin(int[] array) { if (array.length == 0) return Integer.MAX_VALUE; int min = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] < min) { min = array[i]; } } return min; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值