基础算法:二分查找

目录

  • 1. 二分查找
  • 2. 补充:二进制运算
    • 2.1 十进制与二进制的相互转换
      • 2.1.1 十进制转二进制
      • 2.1.2 二进制转十进制
    • 2.2 机器数 真值
    • 2.3 原码 补码 反码
    • 2.4 二进制的加减乘除
    • 2.5 移位运算

1. 二分查找

思想: 有序数组,从中找值

实现:

  • while 循环:时间复杂度:log(n)

        public static int binarySearch01(int[] arr, int target) {int i = 0;int j = arr.length - 1;// <= 是因为目标值可能在 0 或 arr.length - 1 索引处while (i <= j) {// 用移位运算防止溢出int mid = (i + j) >>> 1;if ( target < arr[mid]) {j = mid - 1;} else if (arr[mid] < target){i = mid + 1;} else {return mid;}}return -1;}
    

    优化:把 j 只作为一个右边界,不能指向目标值

        public static int binarySearch02(int[] arr, int target) {int i = 0;int j = arr.length;// 这里如果使用 i <= j,会导致查找不存在的元素的时候出现死循环while (i < j) {int mid = (i + j) >>> 1;if (arr[mid] > target) {j = mid;} else if (arr[mid] < target){i = mid + 1;} else {return mid;}}return -1;}
    

    分析: 往左查找的消耗是往右查找消耗的 1/2,不均衡。(因为往右查找需要比较两次 if)
    优化:平均两侧查找消耗

        public static int binarySearch04(int[] arr, int target) {int i = 0;int j = arr.length;while (1 < j - i) {int mid = (i + j) >>> 1;if (target < arr[mid]) {j = mid;} else {// 这里不能 + 1了,如果目标值就在中间,就找不到了i = mid;}}if (arr[i] == target) {return i;}return -1;}
    

    利用 api 写个二分查找,找到目标值就返回索引 + 数组,找不到就插入再返回

        public static Map<Integer, int[]> binarySearch05(int[] arr, int target) {int i = Arrays.binarySearch(arr, target);Map<Integer, int[]> res = new HashMap<>();if (0 < i) {res.put(i, arr);return res;}int targetIndex = abs(i + 1);int[] arr0 = new int[arr.length + 1];System.arraycopy(arr, 0, arr0, 0, targetIndex);arr0[targetIndex] = target;System.arraycopy(arr, targetIndex, arr0, targetIndex + 1, arr.length - targetIndex);res.put(targetIndex, arr0);return res;}
    
  • Arrays.binarySearch 包含多种类型的数据,这里看 int 的:
    binarySearch0 的返回值 = - 插入点 - 1
    为什么不直接返回 -插入点?答:为了防止插入点为0出现歧义

        public static int binarySearch(int[] a, int key) {return binarySearch0(a, 0, a.length, key);}private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) {int low = fromIndex;int high = toIndex - 1;while (low <= high) {int mid = (low + high) >>> 1;int midVal = a[mid];if (midVal < key)low = mid + 1;else if (midVal > key)high = mid - 1;elsereturn mid; // key found}return -(low + 1);  // key not found.}
    
  • 二分查找返回最左/最右的索引(相同元素情况)

        /*** @Desc falg = -1 返回最左索引,flag = 1 返回最右索引*/public static int binarySearchLeftOrRight(int[] arr, int target, int flag) {int i = 0;int j = arr.length - 1;int index = -1;while (i <= j) {int mid = (i + j) >>> 1;if (target < arr[mid]) {j = mid - 1;} else if (arr[mid] < target) {i = mid + 1;} else {index = mid;if (1 == flag) {i = mid + 1;} else {j = mid - 1;}}}return index;}
    
  • 优化: 二分查找返回最左/最右的索引(相同元素情况)

        /*** @param flag  = -1 返回最左索引, = 1 返回最右索引* @return int 存在,返回对应索引,不存在,返回 - 插入点 - 1*/public static int binarySearchLeftOrRight(int[] arr, int target, int flag) {int i = 0;int j = arr.length - 1;while (i <= j) {int mid = (i + j) >>> 1;if (flag == 1 ? target < arr[mid] : target <= arr[mid]) {j = mid - 1;} else {i = mid + 1;}}int index = flag == 1 ? i - 1 : i;if (target == arr[index]) {return index;} return - index - 1;}
    

2. 补充:二进制运算

2.1 十进制与二进制的相互转换

2.1.1 十进制转二进制

  • 方法:短除法
    • 整数:
      逆序排列
    • 小数:
      顺序排列
      原则: 一直转换到小数部分清零为止

思考: 有些小数部分是无法清零的该怎么处理?
这时候就要引入误差/精度了,比如说:0.33转换成二进制,误差小于1‰,那么我假设转换后的结果为 x,那么要求就是: |x - 0.33| < 1‰,即 0.319 < x < 0.331。
那么如何快速计算出结果呢?
进制转换工具

所以 x 取 0.0101010001 就OK了
验证一下:

没问题。

2.1.2 二进制转十进制

  • 整数:10101101 → 173
    向右减权
    1*2^7 + 0*2^6 + 1*2^5 + 0*2^4 + 1*2^3 + 1*2^2 + 0*2^1 + 1*2^0
    
  • 小数:0.1101 → 0.8125
    向右减权
    1*2^-1 + 1*2^-2 + 0*2^-3 + 1+2^-4
    

2.2 机器数 真值

机器数: 数字在计算机中的二进制表示形式。在计算机中,数字以二进制形式存储和处理。机器数的最高位是符号位,用于表示数字的正负。正数的符号位为0,负数的符号位为1。机器数的大小受到计算机字长的限制,字长决定了机器数的表示范围。

// 不同位的计算机中表示 5
00000101   																# 800000000 00000000 00000000 00000101  									# 3200000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101	# 64

64位计算机能一次读8byte,32位能一次读 4byte,可通过 cmd 输入 systeminfo 查看计算机参数
比如某机器的机器数大小是 2 byte,那么它存储数字 5 的机器数就是:0000 0101,最左边的 0 表示正数,其表示 -5 的机器数就是 1000 0101

真值: 带符号位的机器数对应的真正数值,可以通过将机器数转换为十进制数来获得。

比如:机器数 1000 0101 的真值是 -5,0000 0101 的真值是 5。

2.3 原码 补码 反码

原码: 最高位表示符号位,其余位表示数值的绝对值,和机器数一个意思。

反码: 正数的反码和原码相同,负数的反码是在其原码的基础上,符号位不变,其余各位取反。

比如:正数+5的反码和原码都是00000101,负数-5的反码是11111010 。

补码: 正数的补码和原码相同,负数的补码是在其反码的基础上加1。

比如:正数+5的补码和原码都是00000101,负数-5的补码是11111011。

小结一下:对于正数:原码 = 反码 = 补码;对于负数:反码 = 原码标志位不变,其余取反(1变成0,0变成1),补码 = 反码 + 1。

补充:负数的补码转原码

  • 先取反
  • 然后 + 1

2.4 二进制的加减乘除

加法: 正数 + 负数 我们放到减法里面说

  • 方法:从右往左加,逢二进一

减法:

  • 方法:都换成补码,然后相加,相加的结果就是答案

    因为我们是在 8 位二进制里面做加减,所以超出(溢出)的 1 要舍去

乘法:

  • 正数:与十进制一样,按位相乘,然后相加
  • 负数:摘掉标志位,然后相乘,最后根据两个符号位来判断正负

除法:

  • 正数:正数:与十进制一样,按位相除(如果被除数的最高位小于除数的最高位,则除法运算结束。被除数的剩余位数即为余数)
  • 负数:负数取补码,然后相除
    注意:进制负数除法的结果可能是有限的,也可能是无限循环的。在实际计算中,可能需要根据具体情况进行舍入或截断

2.5 移位运算

二进制移位运算:

  • << 左移:将一个数的二进制表示向左移动指定的位数,右侧用0填充

    可以把 << 看做是 *2 操作
  • >> 右移:将一个数的二进制表示向右移动指定的位数,正数高位用0填充,负数高位用1填充

    可以把 >> 看做是 /2 操作,一直右移,正数会变成 0,负数会变成 -1(为了保证它是负数,所以会补个 1)
  • >>> 无符号右移位,不管正数还是负数,高位都用0补齐

注意事项1: 在做移位运算的时候要考虑当前类型的大小,如下:

声明一个 int 整型 -11、11
-11(int) >>> 2 : 原二进制:11111111111111111111111111110101二进制: 111111111111111111111111111101十进制: 107374182111(int)  << 31 : 原二进制: 1011二进制: 10000000000000000000000000000000十进制: -2147483648
11(int)  << 32 : 原二进制: 1011二进制: 1011十进制: 11

注意事项2: Java 中会将最高位看做符号位!,所以当数值 > 2^31 的时候,最高位会被当做符号位 1,从而编程负数。

   public static void main(String[] args) {int i = (int) pow(2, 31);int j = 1;System.out.println(i);System.out.println(Integer.toBinaryString(i));System.out.println(i + j);System.out.println(Integer.toBinaryString(i + j));System.out.println( (i + j) / 2 );System.out.println( (i + j) >>> 1);}

  

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

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

相关文章

IDEA报Error:java:无效的源发行版13解决方式

出现问题原因&#xff1a;原本项目是spingboot2.0版本开发的&#xff0c;IDEA启动正常&#xff0c;后期新项目使用spingboot3.0&#xff0c;通过原来的IDEA版本及JDK1.8启动报上述错误&#xff0c;以下为版本文件 解决方式&#xff1a; 项目背景&#xff1a;项目已经上线&…

因为写保护,U盘会“假死”。如何在Windows 10上删除写保护

本文介绍如何从USB驱动器、SD卡或单个文件中删除写保护。说明适用于Windows 10、Windows 8和Windows 7。 如何使用锁定开关解除写保护 如果你的计算机告诉你介质受写保护&#xff0c;请在USB或SD卡上查找写保护开关&#xff08;也称为锁定开关&#xff09;。如果介质有此开关…

中断机制-通过volatile实现线程中断停止

4.1.4 大厂面试题中断机制考点 如何停止中断运行中的线程&#xff1f; 通过一个volatile变量实现 package com.nanjing.gulimall.zhouyimo.test;import java.util.concurrent.TimeUnit;/*** author zhou* version 1.0* date 2023/10/15 2:34 下午*/ public class InterruptD…

JS DataTable中导出PDF右侧列被截断的问题解决

JS DataTable中导出PDF右侧列被截断的问题解决 文章目录 JS DataTable中导出PDF右侧列被截断的问题解决一. 问题二. 解决办法三. 代码四. 参考资料 一. 问题 二. 解决办法 设置PDF大小和版型 orientation: landscape, pageSize: LEGAL,上述代码设置打印的PDF尺寸为LEGAL&…

深入理解 JVM(重点:双亲委派模型 + 垃圾回收算法)

一、什么是 JVM&#xff1f; JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。可以认为 JVM 是一台被定制过的现实当中不存在的计算机&#xff0c;Java程序最终是…

Android---Android 是如何通过 Activity 进行交互的

相信对于 Android 工程师来说&#xff0c;startActivity 就像初恋一般。要求低&#xff0c;见效快&#xff0c;是每一个菜鸟 Android 工程师迈向高级 Android 工程师的必经阶段。经过这么多年的发展&#xff0c;startActivity 在 google 的调教下已经变得愈发成熟&#xff0c;对…

【Express】服务端渲染(模板引擎 EJS)

EJS&#xff08;Embedded JavaScript&#xff09;是一款流行的模板引擎&#xff0c;可以用于在Express中创建动态的HTML页面。它允许在HTML模板中嵌入JavaScript代码&#xff0c;并且能够生成基于数据的动态内容。 下面是一个详细的讲解和示例&#xff0c;演示如何在Express中…

Mac安装Kali保姆级教程

Mac安装Kali保姆级教程 其他安装教程&#xff1a;使用VMware安装系统Window、Linux&#xff08;kali&#xff09;、Mac操作系统 1 虚拟机安装VM Fusion 去官网下载VM Fusion 地址&#xff1a;https://customerconnect.vmware.com/en/evalcenter?pfusion-player-personal-13 …

网工记背配置命令(3)----POE配置示例

POE 供电就是通过以太网供电&#xff0c;这种方式仅凭借那根连接通信终端的网线就可完成为它们供电。POE提供的是-53V~0v 的直流电&#xff0c;供电距离最长可达 100m。PoE 款型的交换机的软件大包天然支持 POE&#xff0c;无需 license&#xff0c;通过执行 poe-enable 命令使…

Android 10.0 禁止弹出系统simlock的锁卡弹窗功能实现

1.前言 在10.0的系统开发中,在一款产品中,需要实现simlock锁卡功能,在系统实现锁卡功能以后,在开机的过程中,或者是在插入sim卡 后,当系统检测到是禁用的sim卡后,就会弹出simlock锁卡弹窗,要求输入puk 解锁密码,功能需求禁用这个弹窗,所以就需要看是 哪里弹的,禁用…

记一次生产大对象及GC时长优化经验

最近在做一次系统整体优化,发现系统存在GC时长过长及JVM内存溢出的问题,记录一下优化的过程 面试的时候我们都被问过如何处理生产问题&#xff0c;尤其是线上oom或者GC调优的问题更是必问&#xff0c;所以到底应该如何发现解决这些问题呢&#xff0c;用真实的场景实操&#xff…

计算机毕业设计 基于协同过滤算法的白酒销售系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

Apache Log4j Server (CVE-2017-5645) 反序列化命令执行漏洞

文章目录 Apache Log4j Server 反序列化命令执行漏洞&#xff08;CVE-2017-5645&#xff09;1.1 漏洞描述1.2 漏洞复现1.2.1 环境启动1.2.2 漏洞验证1.2.3 漏洞利用 1.3 加固建议 Apache Log4j Server 反序列化命令执行漏洞&#xff08;CVE-2017-5645&#xff09; 1.1 漏洞描述…

【大数据】Hive SQL语言(学习笔记)

一、DDL数据定义语言 1、建库 1&#xff09;数据库结构 默认的数据库叫做default&#xff0c;存储于HDFS的&#xff1a;/user/hive/warehouse 用户自己创建的数据库存储位置&#xff1a;/user/hive/warehouse/database_name.db 2&#xff09;创建数据库 create (database|…

allegro中shape的一些基本操作(一)——添加和修改shape

添加shape 简单添加shape的方式有3种&#xff0c;如下图所示 点击选择相应的shape模式后可以在option面板中设置相应的shape参数&#xff08;这里不做过多介绍&#xff0c;里面可以设置shape的大小、静态或动态shape等参数&#xff09;&#xff0c;然后再用鼠标在相应的层上添…

计算机网络 | 网络层

计算机网络 | 网络层 计算机网络 | 网络层功能概述SDN&#xff08;Software-Defined Networking&#xff09;路由算法IPv4IPv4 分组IPv4 分组的格式IPv4 数据报分片 IPv4 地址与 NATIPv4 地址网络地址转换&#xff08;NAT&#xff09; 子网划分和子网掩码子网划分子网掩码 无分…

Hadoop3教程(十二):MapReduce中Shuffle机制的概述

文章目录 &#xff08;95&#xff09; Shuffle机制什么是shuffle&#xff1f;Map阶段Reduce阶段 参考文献 &#xff08;95&#xff09; Shuffle机制 面试的重点 什么是shuffle&#xff1f; Map方法之后&#xff0c;Reduce方法之前的这段数据处理过程&#xff0c;就叫做shuff…

【opencv】windows10下opencv4.8.0-cuda Python版本源码编译教程

【opencv】windows10下opencv4.8.0-cuda Python版本源码编译教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【opencv】windows10下opencv4.8.0-cuda Python版本源码编译教程前言准备工具anaconda/cuda/cudnnanaconda创建环境(选做)安装原…

Opencv——颜色模型+通道分离与合并

视频加载/摄像头调用 VideoCapture允许一开始定义一个空的对象 VideoCapture video VideoCapture(const String &filename,int apiPreferenceCAP_ANY) filename:读取的视频文件或者图像序列名称 apiPreference:读取数据时设置的属性&#xff0c;例如编码格式、是否调用Op…

LabVIEW为什么不能在RT机箱内看到NI-IMAQ设备

LabVIEW为什么不能在RT机箱内看到NI-IMAQ设备 最近把NI-IMAQ更新到最新的1394版本。这个新驱动工作良好。但是&#xff0c;当打开MAX&#xff0c;NII MAQ设备却在RT PXI机箱里找不到。 问题最有可能是NIIMAQ服务器的版本跟主机PC和RT目标设备是不同的。为保证通信正常NII MAQ服…