【数组】二分查找(减不减一,看初始化!)

这篇具有很好参考价值的文章主要介绍了【数组】二分查找(减不减一,看初始化!)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、力扣习题链接

704. 二分查找 - 力扣(LeetCode)

【数组】二分查找(减不减一,看初始化!),代码随想录刷题,c++,算法

二、思路

这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。

二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。

例如到底是 while(left < right) 还是 while(left <= right)

到底是right = middle呢,还是要right = middle - 1呢?

大家写二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。

写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)

一句秒杀:说实话,看完下面的解释我还是一团糟,但是我可以理解的是,如果如果是左闭右开,那么right就要end-1,好的,现在我更改口令:只要初始化-1,代表着父区间右侧-1,所以接下来的子区间右侧都要乖乖听话-1;如果左闭右闭,那么由于初始化不-1,所以middle就不-1;对于所有的左区间,middle通通+1!!!!

2.1左闭右闭

第一种写法,我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right] 

区间的不同决定了二分法代码的不同!

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) 的话,right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1

总结:因为这里是右闭,那么引发的俩个问题:左可等于右,把左闭右开想成原型的话,右闭多了一个元素,所以要-1

例如在数组:1,2,3,4,7,9,10中查找元素2,如图所示:

【数组】二分查找(减不减一,看初始化!),代码随想录刷题,c++,算法

发现元素在左区间,更新right

发现元素在右区间,更新left

classs Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // end指向最后一个元素的下一个位置
        while (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; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

2.2左闭右开

如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式则截然不同。

有如下两点:

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) 的话,right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

在数组:1,2,3,4,7,9,10中查找元素2,如图所示:

【数组】二分查找(减不减一,看初始化!),代码随想录刷题,c++,算法

// 版本二
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // [left, right)
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) /2);
            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; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

2.3distance函数

class Solution {
public:
    int search(vector<int>& nums, int target) {
      vector<int>::iterator it=find(nums.begin(),nums.end(),target);
      if(it!=nums.end())
      return distance(nums.begin(),it);
      else
      return -1;
    }
};

 三、小结

二分法是非常重要的基础算法,为什么很多同学对于二分法都是一看就会,一写就废

其实主要就是对区间的定义没有理解清楚,在循环中没有始终坚持根据查找区间的定义来做边界处理。

区间的定义就是不变量,那么在循环中坚持根据查找区间的定义来做边界处理,就是循环不变量规则。

本篇根据两种常见的区间定义,给出了两种二分法的写法,每一个边界为什么这么处理,都根据区间的定义做了详细介绍。

四、拓展练习(35,34,69,367) 

以下为题解

  • 35.搜索插入位置(opens new window)​​​​​​
  • 34.在排序数组中查找元素的第一个和最后一个位置(opens new window)
  • 69.x 的平方根(opens new window)
  • 367.有效的完全平方数(opens new window)

希望给大家带来帮助!!文章来源地址https://www.toymoban.com/news/detail-723066.html

到了这里,关于【数组】二分查找(减不减一,看初始化!)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 顺序表创建,初始化,赋值,取值,查找,插入与删除(附小例题)

    由n(n≥0)个数据结构相同的元素构成的有限序列。 1)除了第一个元素外,结构中的每一个数据元素均只有一个前驱 2)除了最后一个元素外,结构中的每一个数据元素均只有一个后驱 用一组地址 连续的存储单元依次 存储线性表的数据元素。 优点 : 随机存储 缺点 :在做插

    2024年02月07日
    浏览(32)
  • 数组练习题,数组的动态初始化

    定义一个数组,求和 定义一个数组,统计数组里面一共有多少能够被3 整除的数字: 定义一整数类型数组,如果该数字是奇数,则将当前数字扩大两倍,如果是偶数,则将该数字变成该数字的1/2. 一个循环尽量只做一件事情,虽然把打印写的同一个循环里面可以,结果一样,

    2024年02月15日
    浏览(38)
  • 线性表(顺序表)的初始化,取值,查找,插入及删除(c++)

    顺序表中的基本操作以及描述(基本操作包括对线性表进行初始化,取值,查找元素,插入元素以及删除元素) 构造一个空的顺序表,并将表的长度设置为0,具体代码实现如下: 本用例为方便,仅存储5个数据,可以更改循环次数从而增加线性表刚开始存储元素的个数。 利用

    2024年02月07日
    浏览(42)
  • C语言字符串初始化详解:用常量字符串进行字符数组初始化

    简介 字符串初始化 用常量字符串 初始化过程 示范代码 结论 在C语言中,字符串被定义为字符数组。字符串的初始化是指将一个常量字符串复制到字符数组中。本文将详细介绍字符串的初始化方法,并提供相应的示范代码。 在C语言中,有几种常用的方法可以用常量字符串来

    2024年02月15日
    浏览(35)
  • java中初始化数组的方法

    方式一: 注:此种方式创建的数组,如不显式初始化数组元素,则各元素为当前数据类型的默认值。基本数据类型为0,对象类型为null。所以使用前需要将各元素显式赋值。 方式二: 注:此方式与方式一的结果相同,但是更简便。 方式三: 注:此方式与方式一和方式二的结

    2024年02月12日
    浏览(32)
  • C语言二维数组的初始化

    二维数组的初始化可以按行分段赋值,也可按行连续赋值。 例如,对于数组 a[5][3],按行分段赋值应该写作: int a[5][3]={{80,75,92},{61,65,71},{59,63,70},{85,87,90},{76,77,85}}; 其中,花括号的对数代表行数,方括号中的值的个数代表列数。 按行连续赋值应该写作: int a[5][3]={80,75,92,61,6

    2024年02月04日
    浏览(34)
  • Python如何创建二维数组和初始化

            严格意义上说,Python中并没有数组的概念,Python中表达一组数据有多种形式,例如list,tuple,set等数据结构都可以表达一组数,并且这组数也没有C和C++中数组的的同质限制,这些数可以是任何一种数据类型。         以list为例(list又叫列表),要想实现一个所

    2024年02月20日
    浏览(38)
  • C++二维数组的初始化赋值及示例

    C++二维数组可以看作一个表格,横向为表格的行,纵向为表格的列,数组定义时行号在前,列号在后。二维数组的定义格式为: 数据类型  数组名[常量行表达式][常量列表达式] 。 二维数组的元素是按先行后列的顺序存放的,例如,定义一个int a[3][2]的数组,其形式为: a[

    2024年02月12日
    浏览(42)
  • C语言:结构体数组的使用和初始化:

    前文:在C语言中,结构体是经常会用到的自定义数据类型,通常在使用结构体时,我们会进行单一的结构体初始化。但在使用同一个结构体,初始化不同的数据时,则可以用到结构体数组来进行初始化。 结构体数组是指在一个数组中存储多个结构体对象的集合。结构体是一

    2024年02月04日
    浏览(41)
  • C/C++初始化数组全为0的方法

    使用C99标准的方式 使用C99标准时,可以直接在定义数组时使用空的花括号来初始化数组,这将会把数组中的所有元素都初始化为0。 当使用大括号初始化器{}来初始化数组时,如果没有指定初始值,则数组的所有元素将被初始化为默认值。对于整型数组,其默认值为0。 在C+

    2024年02月16日
    浏览(71)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包