查找算法——二分查找

news2024/11/24 17:01:46

笔记:二分查找算法 | 数据结构与算法 系列教程(笔记)

题目描述

请对一个 有序数组 进行二分查找 {1,8, 10, 89, 1000, 1234},输入一个数看看该数组是否存在此数,并且求出下 标,如果没有就提示「没有这个数」。

思路

(1)左边索引  left 为 0 ,  右边索引 right 为 数组长度 - 1;

  (2)  当left 和 right可能变化时,中间值始终为,middle = (left + right) / 2

(3)循环条件:while(left <=  ritgh) , 数组索范围。

 (4)   如果arr[中间值]  > 目标值 ,那么在左边可能有答案,此时,最右边的值 = 中间值 - 1;

(5)如果arr[中间值] < 目标值 ,那么在右边可能有答案,此时,最左边的值 = 中间值 + 1;

(6)(最后找到中间值就是答案) 

图解

实现

解法(1)——初始版

package com.ma.test;
public class BinarySearch1 {
    public static void main(String[] args) {
        int [] arr = {50,89,505,10052};
        System.out.println(binarySearchBasic(arr,50));
    }
    public static int binarySearchBasic(int[] arr, int target) {
        int left = 0;  //  设定左边的索引
        int right = arr.length - 1; // 设定右边的索引范围
        while (left <= right) {  // 索引的范围
            int middle = (left + right) / 2;
            if (arr[middle] > target) { // 中间值 > 目标值 ,说明在中间值的左边
                right = middle - 1;  // 右边值的范围 = 中间值 - 1
            } else if (arr[middle] < target) { // 中间值 < 目标值,说明在中间值的右边
                left = middle + 1;   //  左边值的范围 = 中间值 + 1
            } else {
                return middle;
            }
        }
        return -1;
    }
}

解法(2)——改动版

    public static int getValue(int arr[], int target) {
        int left = 0;
        int right = arr.length;

        while (left < right) {
            int middle = (left + right) >>> 1;
            if (target < arr[middle]) {     // 目标值  >  中间值
                right = middle;
            } else if (arr[middle] < target) {
                left = middle + 1;
            } else {
                return middle;
            }
        }
        return -1;
    }

解法(3)——二分查找(平衡板)

    public static int binarySearch(int[] a, int target) {
        int i = 0, j = a.length;
        while (1 < j - i) {
            int m = (i + j) >>> 1;
            if (target < a[m]) {
                j = m;
            } else {
                i = m;
            }
        }
        if (a[i] == target) {
            return i;
        } else {
            return -1;
        }
    }

 分析:

这是一个二分查找的实现,目的是在有序数组 a 中查找目标值 target。下面对代码进行逐步分析:

  1. int i = 0, j = a.length;:初始化两个指针,i 表示搜索范围的起始位置,j 表示搜索范围的结束位置。一开始,搜索范围为整个数组。

  2. while (1 < j - i):使用二分查找,进入循环,条件是搜索范围的长度大于 1。这是因为当搜索范围只有一个元素时,就已经找到了目标或确定目标不在数组中。

  3. int m = (i + j) >>> 1;:计算中间位置 m,使用无符号右移操作符 >>> 来防止溢出。

  4. if (target < a[m]) { j = m; } else { i = m; }:根据目标值与中间值的大小关系,缩小搜索范围。如果目标值小于中间值,则在左半边搜索;否则,在右半边搜索。

  5. 循环结束后,搜索范围缩小到一个元素,此时 i 就是目标值在数组中的位置。

  6. if (a[i] == target) { return i; } else { return -1; }:最后检查 a[i] 是否等于目标值,如果相等,返回 i;否则,返回 -1 表示未找到目标值。

这段代码实现了二分查找算法,时间复杂度为 O(log n),其中 n 是数组的长度。值得注意的是,这里的实现返回的是目标值在数组中的索引,如果目标值可能重复出现,这个函数只返回其中一个索引。

解法(4)——二分查找(解决值重复问题,最左边)

    public static int binarySearchBasic(int[] arr, int target) {
        int left = 0;  //  设定左边的索引
        int right = arr.length - 1; // 设定右边的索引范围
        int candidate = -1;
        while (left <= right) {  // 索引的范围
            int middle = (left + right) / 2;
            if (arr[middle] > target) { // 中间值 > 目标值 ,说明在中间值的左边
                right = middle - 1;  // 右边值的范围 = 中间值 - 1
            } else if (arr[middle] < target) { // 中间值 < 目标值,说明在中间值的右边
                left = middle + 1;   //  左边值的范围 = 中间值 + 1
            } else {
                candidate = middle;
                right = middle - 1;
            }
        }
        return candidate;
    }

 解法(5)——二分查找(解决值重复问题,最右边)

    public static int binarySearchBasic(int[] arr, int target) {
        int left = 0;  //  设定左边的索引
        int right = arr.length - 1; // 设定右边的索引范围
        int candidate = -1;
        while (left <= right) {  // 索引的范围
            int middle = (left + right) / 2;
            if (arr[middle] > target) { // 中间值 > 目标值 ,说明在中间值的左边
                right = middle - 1;  // 右边值的范围 = 中间值 - 1
            } else if (arr[middle] < target) { // 中间值 < 目标值,说明在中间值的右边
                left = middle + 1;   //  左边值的范围 = 中间值 + 1
            } else {
                candidate = middle;
                left = middle + 1;
            }
        }
        return candidate;
    }

  解法(6)——二分查找(大于目标值的 最靠左的值。)

    public static int binarySearchBasic(int[] arr, int target) {

        // left : 大于目标值的 最靠左的值。
        int left = 0;  //  设定左边的索引
        int right = arr.length - 1; // 设定右边的索引范围
        while (left <= right) {  // 索引的范围
            int middle = (left + right) / 2;
            if (target <= arr[middle]) { // 中间值 > 目标值 ,说明在中间值的左边
                right = middle - 1;  // 右边值的范围 = 中间值 - 1
            } else { // 中间值 < 目标值,说明在中间值的右边
                left = middle + 1;   //  左边值的范围 = 中间值 + 1
            }
        }
        return left;
    }

解法(7)——二分查找(目标值的最靠左元素)

    public static int binarySearchBasic(int[] arr, int target) {

        // left : 大于目标值的 最靠左的值。
        int left = 0;  //  设定左边的索引
        int right = arr.length - 1; // 设定右边的索引范围
        while (left <= right) {  // 索引的范围
            int middle = (left + right) >>> 1;
            if (target <= arr[middle]) { // 中间值 > 目标值 ,说明在中间值的左边
                right = middle - 1;  // 右边值的范围 = 中间值 - 1
            } else { // 中间值 < 目标值,说明在中间值的右边
                left = middle + 1;   //  左边值的范围 = 中间值 + 1
            }
        }
        return left - 1;
    }

产生的问题

1.    /  & <<< 的区别?

        int a = 2;
        int b = Integer.MAX_VALUE-1;

        System.out.println((a+b) / 2);
        System.out.println((a+b) >>> 1);


输出:-1073741824
      1073741824

2. 为什么结果可能不一致?

结果不一致的原因在于右移运算符和除法运算符对负数的处理方式不同。在 Java 中,右移运算符会用0填充左边空出的位,而除法运算符会根据数学规则产生截断效应。

考虑以下情况:

  • a 是正整数,b 是一个接近 Integer.MAX_VALUE 的整数。在 (a + b) 中,结果可能接近 Integer.MAX_VALUE
  • (a + b) / 2 中,除法运算符会将结果截断为整数,丢失了部分信息。
  • (a + b) >>> 1 中,无符号右移运算符则会在右边用0填充,不会截断,结果更接近原值。

因此,当 (a + b) 很大并且接近 Integer.MAX_VALUE 时,这两种方式的结果可能不一致。

衡量算法的好坏

时间复杂度

定义:时间复杂度是用来衡量:一个算法的执行,随着数据规模增大,而增长的时间成本。

说明:

* 假设算法要处理的数据规模是n,代码总的执行行数用函数f(n)来表示,例如:

   线性查找算法的函数f(n) = 3 * n + 3

   二分查找算法的函数f(n) = (floor(log2(n)) + 1) * 5 + 4

为了对f(n)进行化简,应当抓住主要矛盾,找到一个变化趋势与相近的表示法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1327008.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端ICON库

前端ICON库 1.mingcute mingcute 2.lordicon lordicon 3.字节iconpark&#xff08;推荐&#xff09; 字节iconpark 4.iconbuddy iconbuddy.app/ 5.商标寻找youicons 免费下载数百万个徽标以获得设计灵感 | YouIcons.com 还有一堆工具

一键转换,将HTML智能转换为PDF,轻松解决文档转换需求

在数字时代&#xff0c;HTML网页是我们获取信息的主要来源之一。然而&#xff0c;有时候我们可能需要将网页内容以PDF格式保存&#xff0c;以便于离线阅读、打印或分享。这时&#xff0c;将HTML转换为PDF就变得尤为重要。 首先&#xff0c;我们要进入首助编辑高手主页面&#x…

华为数通方向HCIP-DataCom H12-831题库(多选题:181-200)

第181题 如图所示,R1、R2、R3、R4都部署为SPF区域0,链路的cost值如图中标识。R1、R2R3、R4的Loopback0通告入OSPF。R1、R2、R3与R4使用Loopback0作为连接接口,建立BGP对等体关系,其中R4为RR设备,R1、R2、R3是R4的客户端。当R4的直连地址172.20,1,4/32通告入BGP后,以下关R…

单细胞转录组揭示杨树维管组织结构与发育

之前我们有一篇PC的文章&#xff0c;专门介绍了通过单细胞转录组分析杨树茎中初生和次生生长组织中形成层细胞分化的分子轨迹&#xff08;文章回顾&#xff1a;植物单细胞转录组 | 杨树初生生长和次生生长中形成层细胞分化的分子轨迹&#xff09;。2023年发表于《Molecular Pla…

mysql区分度不高的字段建索引一定没用吗?

建立索引的目的是提高数据库查询的性能&#xff0c;通过索引可以快速定位到符合查询条件的数据。然而&#xff0c;对于区分度不高的字段来说&#xff0c;建立索引可能会带来一些问题。 当字段的区分度很低时&#xff0c;即字段中的唯一值或不同值的数量相对较少&#xff0c;建立…

AI代码翻译神器,用AI翻译代码,轻松学习不同编程语言,已开源!

体验地址&#xff0c;github地址和部署地址在文章底部 AI代码翻译器的优势 近年来&#xff0c;随着技术的快速进步&#xff0c;人工智能技术展现出了在各个领域发挥作用的巨大潜力。AI代码翻译器作为一项创新技术&#xff0c;为开发者带来了全新的可能性。这项技术运用人工智…

FreeSWITCH 1.10 源码阅读(7)-uuid_bridge 命令原理解析

文章目录 1. uuid_bridge 命令执行2. 源码分析3. uuid_bridge 问题处理 1. uuid_bridge 命令执行 uuid_bridge 命令用于将两条腿桥接在一起&#xff0c;以实现双方的通话。下图是该命令在 FreeSWITCH 中的处理流程&#xff0c;大致的流程可以分成以下几个步骤&#xff1a; uui…

图像分割与修复

图像分割的方法 &#xff08;1&#xff09;传统的图像分割方法 &#xff08;2&#xff09;基于深度学习的图像分割方法 传统的图像分割方法 &#xff08;1&#xff09;分水岭法 &#xff08;2&#xff09;GrabCut法 &#xff08;3&#xff09;MeanShift法 &#xff08;4…

Linux调试器gdb的用法

Linux调试器gdb的用法 1. debug/release版本之间的比较2. gdb调试器的基本指令3. 使用展示 1. debug/release版本之间的比较 在之前学习C语言的的时候出过一期vs的调试技巧。 而对于现在的Linux下的调试器gdb其实也是换汤不换药的&#xff0c;基本上的调试思路是不会改变的&am…

Electron Vite打包后,部分图标未显示的解决方案

背景 这个问题&#xff0c;弄了一晚上&#xff0c;头都大了&#xff0c;找了一堆博客也没解决。主要参考这个&#xff1a;https://blog.csdn.net/m0_73845616/article/details/129741099。 下面讲一下我的解决方案。 解决方案 上面链接里的方法&#xff0c;我采用第二、三个都…

鸿蒙-arkTs:访问控制授权申请

module.json5文件中 requestPermissions 进行配置&#xff08;值为数组&#xff0c;可配置多个&#xff09; ohos.permission.INTERNET {"name": "ohos.permission.INTERNET" }

鸿蒙-HarmonyOS之初见

鸿蒙初识&#xff0c;此事能成&#xff01;&#xff01; 自己安装工具、配置环境并运行成功&#xff0c;流程记录。 一、首先官网下载开发工具 官网地址&#xff1a;https://developer.huawei.com/consumer/cn/ 当前最新的版本3.1 &#xff0c;windows和Mac&#xff0c;Mac又…

GD32移植STM32工程(因为懒,所以移植)

文章目录 一、前言二、差异性三、软件移植部分1.前期准备1.1 安装GD32固件库1.2 选择所用芯片 2.修改程序2.1 启动时间&#xff08;内部时钟可不改&#xff09;2.2 主频2.2.1 系统时钟配置2.2.2 108MHz宏定义第一处第二处第三处第四处第五处 2.2.3 串口2.2.4 FLASH 四、总结 一…

c语言错误总结

函数 A:void类型函数可以 B&#xff1a;不需要&#xff0c;如果return 不返回任何值&#xff0c;函数会在return语句执行后终止执行&#xff0c;后面的语句不会执行 C&#xff1a;对的 D&#xff1a;不可能&#xff0c;return只能返回一个数据 A:函数不一定有返回值 B:可以…

php伪协议 [NISACTF 2022]easyssrf

打开题目 我们直接用 file:/// 协议读取看看flag文件 file:///flag 点击curl得到回响 得到提示告诉我们应该看看提示文件 file:///fl4g 跟着去访问了一下 再跟着去访问 从代码中我们可以看出 get传参file&#xff0c;我们用stristr检测file参数里面是否含有file&#xff…

企业需要哪些数字化管理系统?

企业需要哪些数字化管理系统&#xff1f; ✅企业引进管理系统肯定是为了帮助整合和管理大量的数据&#xff0c;从而优化业务流程&#xff0c;提高工作效率和生产力。 ❌但是&#xff0c;如果各个系统之间不互通、无法互相关联数据的话&#xff0c;反而会增加工作量和时间成本…

Qt之QWidget 自定义倒计时器

简述 Qt提供的带进度显示的只有一个QProgresBar,这个控件要么是加载进度从0~100%,要么是持续的两边滚动;而我想要是倒计时的效果,所以QProgresBar并不满足要求,而Qt重写控件相对于MFC来说简直是轻而易举,所以就整了两种不同的倒计时控件; 效果 代码 QPushButton的绘制部…

边缘计算有哪些常用场景?TSINGSEE边缘AI视频分析技术行业解决方案

随着ChatGPT生成式人工智能的爆发&#xff0c;AI技术在业界又掀起一波新浪潮。值得关注的是&#xff0c;边缘AI智能也在AI人工智能技术进步的基础上得到了快速发展。IDC跟踪报告数据显示&#xff0c;2021年我国的边缘计算服务器整体市场规模达到33.1亿美元&#xff0c;预计2020…

2023最新版JavaSE教程——第11天:常用类和基础API

目录 一、字符串相关类之不可变字符序列&#xff1a;String1.1 String的特性1.2 String的内存结构1.2.1 概述1.2.2 练习类型1&#xff1a;拼接1.2.3 练习类型2&#xff1a;new1.2.4 练习类型3&#xff1a;intern() 1.3 String的常用API-11.3.1 构造器1.3.2 String与其他结构间的…

Spring统一数据返回格式处理String类型出错解析

Spring 统一数据返回格式是使用 Spring 进行开发时很常用的一个功能&#xff0c;但是当其处理返回类型原先为 String 类型的时候就会出错报错&#xff0c;需要我们额外对 String 类型进行处理。 例如&#xff1a;现在我开发一个项目&#xff0c;项目中我想要统一返回下述的 Res…