算法---二分算法

一:二分法算法分析
1、二分查找算法定义
二分查找又称折半查找,它是一种效率较高的查找方法。

二分查找要求:线性表是有序表,即表中结点按关键字有序,并且要用向量作为表的存储结构。

2、基本思想
(1)首先确定该区间的中点位置

(2)将待查的K值与R[mid]比较:若相等,则查找成功并返回此位置,否则须确定新的查找区间,继续二分查找

(3) ① 若R[mid].key>K,将查找区间变为**[left,mid-1]**

​ ②若R[mid].key<K,将查找区间变为**[mid+1,right]**

3、优缺点
③ 二分查找的优点

折半查找的时间复杂度为O(logn),远远好于顺序查找的O(n)。

④ 二分查找的缺点

虽然二分查找的效率高,但是要将表按关键字排序。而排序本身是一种很费时的运算。既使采用高效率的排序方法也要花费O(nlgn)的时间。

4、二分查找常用场景
寻找一个数、寻找左侧边界、寻找右侧边界。

细节:

​ while循环中的不等号是否应该带等号,mid 是否应该加一等等。

5、二分查找常用框架
int binarySearch(int[] nums, int target) {
int left = 0, right = …;

while(...) {int mid = left + (right - left) / 2;if (nums[mid] == target) {...} else if (nums[mid] < target) {left = ...} else if (nums[mid] > target) {right = ...}
}
return ...;

}

分析二分查找的一个技巧是:不要出现 else,而是把所有情况用 else if 写清楚,这样可以清楚地展现所有细节

计算 mid 时需要技巧防止溢出,建议写成: mid = left + (right - left) / 2

二.基本寻找与左右边界
1.寻找一个数(最基本)
搜索一个数,如果存在,返回其索引,否则返回 -1。

int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 注意

while(left <= right) { // 注意int mid = (right + left) / 2;if(nums[mid] == target)return mid; else if (nums[mid] < target)left = mid + 1; // 注意else if (nums[mid] > target)right = mid - 1; // 注意}
return -1;

}
注意点(细节)分析

  1. 为什么 while 循环的条件中是 <=,而不是 < ?

答:因为初始化 right 的赋值是 nums.length - 1,即最后一个元素的索引,而不是 nums.length。

这二者可能出现在不同功能的二分查找中,区别是:前者相当于两端都闭区间 [left, right],后者相当于左闭右开区间 [left, right),因为索引大小为 nums.length 是越界的。

我们这个算法中使用的是 [left, right] 两端都闭的区间。这个区间就是每次进行搜索的区间,我们不妨称为「搜索区间」(earch space)。

什么时候应该停止搜索呢?当然,找到了目标值的时候可以终止:

if(nums[mid] == target)return mid; 

但如果没找到,就需要 while 循环终止,然后返回 -1。那 while 循环什么时候应该终止?搜索区间为空的时候应该终止,意味着你没得找了,就等于没找到嘛。

while(left <= right)的终止条件是 left == right + 1,写成区间的形式就是 [right + 1, right],或者带个具体的数字进去 [3, 2],可见这时候搜索区间为空,因为没有数字既大于等于 3 又小于等于 2 的吧。所以这时候 while 循环终止是正确的,直接返回 -1 即可。

while(left < right)的终止条件是 left == right,写成区间的形式就是 [right, right],或者带个具体的数字进去 [2, 2],这时候搜索区间非空,还有一个数 2,但此时 while 循环终止了。也就是说这区间 [2, 2] 被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就可能出现错误。

当然,如果你非要用 while(left < right) 也可以,我们已经知道了出错的原因,就打个补丁好了:

//…
while(left < right) {
// …
}
return nums[left] == target ? left : -1;

  1. 为什么 left = mid + 1,right = mid - 1?我看有的代码是 right = mid 或者 left = mid,没有这些加加减减,到底怎么回事,怎么判断?

答:刚才明确了「搜索区间」这个概念,而且本算法的搜索区间是两端都闭的,即 [left, right]。那么当我们发现索引 mid 不是要找的 target 时,如何确定下一步的搜索区间呢?

当然是去搜索 [left, mid - 1] 或者 [mid + 1, right] 对不对?因为 mid 已经搜索过,应该从搜索区间中去除。

  1. 此算法有什么缺陷?

答:至此,你应该已经掌握了该算法的所有细节,以及这样处理的原因。但是,这个算法存在局限性。

比如说给你有序数组 nums = [1,2,2,2,3],target = 2,此算法返回的索引是 2,没错。但是如果我想得到 target 的左侧边界,即索引 1,或者我想得到 target 的右侧边界,即索引 3,这样的话此算法是无法处理的。

这样的需求很常见。你也许会说,找到一个 target 索引,然后向左或向右线性搜索不行吗?可以,但是不好,因为这样难以保证二分查找对数级的时间复杂度了。

我们后续的算法就来讨论这两种二分查找的算法。

2、寻找左侧边界的二分搜索
int left_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0;
int right = nums.length; // 注意

while (left < right) { // 注意int mid = (left + right) / 2;if (nums[mid] == target) {right = mid;} else if (nums[mid] < target) {left = mid + 1;} else if (nums[mid] > target) {right = mid; // 注意}
}
return left;

}

注意点(细节)分析

  1. 为什么 while(left < right) 而不是 <= ?**

答:用相同的方法分析,因为初始化 right = nums.length 而不是 nums.length - 1 。因此每次循环的「搜索区间」是 [left, right) 左闭右开。

while(left < right) 终止的条件是 left == right,此时搜索区间 [left, left) 恰巧为空,所以可以正确终止。

  1. 为什么没有返回 -1 的操作࿱

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

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

相关文章

机器学习实验------AGNES层次聚类方法

机器学习 — AGNES层次聚类方法 第1关:距离的计算 任务描述 本关任务:根据本关所学知识,完成calc_min_dist函数,calc_max_dist函数以及calc_avg_dist函数分别实现计算两个簇之间的最短距离、最远距离和平均距离。 import numpy as np def calc_min_dist(cluster1, clus…

【测试工具】JMeter接口测试的简单使用

事先声明&#xff1a;博主的JMeter是3.3版本的&#xff0c;可能和最新版本的操作有些许差别 测试前的准备工作 1、先添加一个线程组&#xff1a;右击“测试计划”&#xff0c;点击“添加”—》“Threads(Users)”—》“线程组” 2、再添加一个HTTP请求&#xff0c;右击“线程…

基于Hive大数据分析springboot为后端以及vue为前端的的民宿系

标题基于Hive大数据分析springboot为后端以及vue为前端的的民宿系 本文介绍了如何利用Hive进行大数据分析,并结合Spring Boot和Vue构建了一个民宿管理系统。该民民宿管理系统包含用户和管理员登陆注册的功能,发布下架酒店信息,模糊搜索,酒店详情信息展示,收藏以及对收藏的…

Pillow教程03:图像处理的基本步骤+分离split+合并merge+混合blend+composite遮罩

--------------Pillow教程集合--------------- Python项目18&#xff1a;使用Pillow模块&#xff0c;随机生成4位数的图片验证码 Python教程93&#xff1a;初识Pillow模块&#xff08;创建Image对象查看属性图片的保存与缩放&#xff09; Pillow教程02&#xff1a;图片的裁剪…

烫烫烫手的结构体大小计算来咯,很烫哦,慢慢消化。自定义类型(一)

emmm&#xff0c;在这炎热的夏天在宿舍吹着空调写着博客也是一件不错的事呢&#xff0c;今天就来来好好盘一下C语言中的自定义类型。 常常会回顾努力的自己&#xff0c;所以要给自己的努力留下足迹。 为今天努力的自己打个卡&#xff0c;留个痕迹吧 2024.03.29 小闭 目录 …

C++项目——集群聊天服务器项目(九)客户端异常退出业务

服务器端应检测到客户端是否异常退出&#xff0c;因此本节来实现客户端异常退出&#xff0c;项目流程见后文 一、客户端异常退出业务流程 &#xff08;1&#xff09;在业务模块定义处理客户端异常退出的函数 &#xff08;2&#xff09;集群聊天服务器项目(八&#xff09;提到…

为什么在Python中总是使用【字典】这种类型呢?

你好&#xff0c;我是安然无虞。 文章目录 创建字典新增字典元素update 方法 删除字典元素pop 方法popitem 方法 查找字典元素in 和 in not 操作符get 方法thisdict[key] 修改字典元素遍历字典元素for循环遍历keys方法values方法items方法 合并字典字典中的key 字典常用接口汇…

网际协议 - IP

文章目录 目录 文章目录 前言 1 . 网际协议IP 1.1 网络层和数据链路层的关系 2. IP基础知识 2.1 什么是IP地址? 2.2 路由控制 3. IP地址基础知识 3.1 IP地址定义 3.2 IP地址组成 3.3 IP地址分类 3.4 子网掩码 IP地址分类导致浪费? 子网与子网掩码 3.5 CIDR与…

常见缓存数据库

Redis Redis 是完全开源的&#xff0c;遵守 BSD 协议&#xff0c;是一个高性能的 key-value 数据库。 Redis 与其他 key - value 缓存产品有以下三个特点&#xff1a; Redis支持数据的持久化&#xff0c;可以将内存中的数据保存在磁盘中&#xff0c;重启的时候可以再次加载进行…

自己动手用ESP32手搓一个智能机器人:ESP32-CAM AI Robot

目录 介绍 硬件需求 软件需求 步骤 总结 源码下载 介绍 ESP32-CAM是一款集成了Wi-Fi和蓝牙功能的微控制器模块&#xff0c;同时还集成了摄像头接口&#xff0c;使其成为一个非常适合构建智能机器人的选择。在本项目中&#xff0c;我将向您展示如何使用ESP32-CAM模块构建…

数据运营分析-详解

一、指标与指标体系 指标体系就是业务逻辑的框架,也是思考业务逻辑的第一步 案例: 老板,我负责的用户活跃,主要考察每天启动产品的注册用户数量,整体来看,每月活跃保持7.3%的增长,是因为渠道团队的拉新活动带来很多新增注册用户,占每月活跃用户的40%,新一年会继续沿…

消息队列的七种经典应用场景

在笔者心中&#xff0c;消息队列&#xff0c;缓存&#xff0c;分库分表是高并发解决方案三剑客。 在职业生涯中&#xff0c;笔者曾经使用过 ActiveMQ 、RabbitMQ 、Kafka 、RocketMQ 这些知名的消息队列 。 这篇文章&#xff0c;笔者结合自己的真实经历&#xff0c;和大家分享…

Linux项目自动化构建工具-make/ makefile及其应用:多文件编写第一个linux程序:进度条(懒人学习必备博文!!!)

目录 1.前言--make/makefile的引入 2.快速上手make/makefile---自动化构建 3.关于依赖关系和依赖方法 4.自动化清理 为什么我们执行编译的时候&#xff0c;make一下就好&#xff0c;清理却要使用make clean? 5. make/makefile是如何知道当前目录下可执行文件是否为最新 6.文件…

在虚拟机ubuntu中端里输入vim filename.不显示vim界面,而是vim可以在以下的 package 找到

1。打开终端 2.输入以下命令来更新软件包列表&#xff1a; sudo apt update 3&#xff0c;输入以下命令来安装vim编辑器&#xff1a; sudo apt install vim 4等待安装完成后&#xff0c;再次输入"vim filename"命令&#xff0c;应该就能正常显示vim界面了。

express实现用户登录和注册接口

目录 1 创建数据库2 连接数据库3 集成ORM库4 创建业务逻辑5 创建路由7 测试接口总结 我们在编写后端接口的时候操作数据库是一种常见的功能需求&#xff0c;express本身并不提供直接操作数据库的能力&#xff0c;需要借助第三方库来操作数据库&#xff0c;本篇讲解一下软件开发…

JavaScript 之 var、let 和 const

JavaScript 之 var、let 和 const 1.作用域2.重新赋值3.重复声明总结 var、let 和 const 都是用来声明变量的关键字&#xff0c;但它们之间有一些重要的区别 1.作用域 var&#xff1a;声明的变量具有函数作用域或全局作用域。 在函数内部声明的变量只能在该函数内部访问&…

【二叉树】Leetcode 543. 二叉树的直径【简单】

二叉树的直径 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例1&#xff1a; 输入&#xff1a;root [1,2…

基于SpringBoot + Vue实现的在线装修管理系统设计与实现+毕业论文

介绍 系统包含用户、装修队、管理员三个角色 管理员&#xff1a; 管理员管理&#xff1a;管理其他管理员的账号和权限&#xff0c;确保系统管理的层次化和安全性。 装修队管理&#xff1a;审核装修队的资质&#xff0c;管理装修队的人员信息&#xff0c;监控工程进度&#xff…

可视化学习:实现Canvas图片局部放大镜

前言 最近我在可视化课程中学习了如何在Canvas中利用像素处理来实现滤镜效果&#xff0c;在这节课程的结尾留了一道局部放大镜的题目&#xff0c;提示我们用像素处理的方式去实现这个效果&#xff0c;最终实现随着鼠标移动将图片局部放大&#xff0c;本着把学到的内容落地实践的…

elementUI this.$msgbox msgBox自定义 样式自定义 富文本

看这个效果是不是很炫?突出重点提示内容,对于用户交互相当的棒! 下来说说具体实现: let self = this const h = self.$createElement; this.$msgbox({title: null,message: h("p", {style: "margin-top:10px"}, [h("i", {class: "el-i…