Javascript算法——二分查找

1.数组

1.1二分查找

1.搜索索引

开闭matters!!![left,right]与[left,right)

/*** @param {number[]} nums* @param {number} target* @return {number}*/
var search = function(nums, target) {let left=0;let right=nums.length-1;//[left,right],相等时能取到,有意义while(left<=right){let mid =Math.floor((left+right)/2);if(target===nums[mid]){return mid;}else if (target>nums[mid]) {left=mid+1;}else{right=mid-1;}}return -1;};
console.log(search([-1,0,3,5,9,12],2))//-1
console.log(search([-1,0,3,5,9,12],2))//4

                                                                       VS 

/*** @param {number[]} nums* @param {number} target* @return {number}*/
var search = function(nums, target) {// right是数组最后一个数的下标+1,nums[right]不在查找范围内,是左闭右开区间let mid, left = 0, right = nums.length;    // 当left=right时,由于nums[right]不在查找范围,所以不必包括此情况while (left < right) {// 位运算 + 防止大数溢出mid = left + ((right - left) >> 1);// 如果中间值大于目标值,中间值不应在下次查找的范围内,但中间值的前一个值应在;// 由于right本来就不在查找范围内,所以将右边界更新为中间值,如果更新右边界为mid-1则将中间值的前一个值也踢出了下次寻找范围if (nums[mid] > target) {right = mid;  // 去左区间寻找} else if (nums[mid] < target) {left = mid + 1;   // 去右区间寻找} else {return mid;}}return -1;
};
 2.搜索插入位置

/*** @param {number[]} nums* @param {number} target* @return {number}*/
var searchInsert = function(nums, target) {let left=0;let right=nums.length-1;//[left,right],相等时能取到,有意义while(left<=right){let mid =Math.floor((left+right)/2);if(target===nums[mid]){return mid;}else if (target>nums[mid]) {left=mid+1;}else{right=mid-1;}}// 分别处理如下四种情况// 目标值在数组所有元素之前  [0, -1]// 目标值等于数组中某一个元素  return middle;// 目标值插入数组中的位置 [left, right],return  right + 1// 目标值在数组所有元素之后的情况 [left, right],这是右闭区间,所以  return right + 1return right+1;};
console.log(search([1,3,5,6],0))//0
console.log(search([1,3,5,6],3))//1
console.log(search([1,3,5,6],4))//2
console.log(search([1,3,5,6],7))//4

其余三种都可以归纳为right+1 

3.在排序数组中查找元素的第一个和最后一个位置

  • 找左边界时,需将right赋给左边界,所以在target<=num[mid]时更新right并更新左边界
  • 找右边界时,需将left赋给右边界,所以在target>=num[mid]时更新left并更新右边界
  • 情况二,通过rightBorder-leftBorder>1条件判断
var searchRange = function(nums, target) {const getLeftBorder = (nums, target) => {let left = 0, right = nums.length - 1;let leftBorder = -2;// 记录一下leftBorder没有被赋值的情况while(left <= right){let middle = left + ((right - left) >> 1);if(nums[middle] >= target){ // 寻找左边界,nums[middle] == target的时候更新rightright = middle - 1;leftBorder = right;} else {left = middle + 1;}}return leftBorder;}const getRightBorder = (nums, target) => {let left = 0, right = nums.length - 1;let rightBorder = -2; // 记录一下rightBorder没有被赋值的情况while (left <= right) {let middle = left + ((right - left) >> 1);if (nums[middle] > target) {right = middle - 1;} else { // 寻找右边界,nums[middle] == target的时候更新leftleft = middle + 1;rightBorder = left;}}return rightBorder;}let leftBorder = getLeftBorder(nums, target);let rightBorder = getRightBorder(nums, target);// 情况一if(leftBorder === -2 || rightBorder === -2) return [-1,-1];// 情况三if (rightBorder - leftBorder > 1) return [leftBorder + 1, rightBorder - 1];// 情况二return [-1, -1];
};
4.X的平方根
function mySqrt(x) {  if (x === 0) return 0; // 特殊情况处理:0的平方根是0  let left = 1; // 搜索范围的左边界  let right = Math.floor(x / 2) + 1; // 搜索范围的右边界,x/2是一个合理的上限,因为平方根不会超过x/2(对于非负整数x)  while (left <= right) {  let mid = Math.floor((left + right) / 2); // 计算中间值  let square = mid * mid; // 计算中间值的平方  if (square === x) {  return mid; // 如果平方正好等于x,直接返回  } else if (square < x) {  left = mid + 1; // 如果平方小于x,说明平方根在mid的右侧,移动左边界  } else {  right = mid - 1; // 如果平方大于x,说明平方根在mid的左侧或正好是mid(但我们需要整数部分,所以向左移动)  }  }  // 循环结束时,left会指向比实际平方根大的最小整数,而right会指向比实际平方根小的最大整数  // 因为我们需要整数部分,所以返回right(它是最后一个使得mid*mid <= x的mid值)  return right;  
}  // 测试  
console.log(mySqrt(4));  // 输出: 2  
console.log(mySqrt(8));  // 输出: 2 (8的平方根约为2.8284,取整数部分2)  
console.log(mySqrt(15)); // 输出: 3 (15的平方根约为3.8729,取整数部分3)

解释

  1. 边界条件
    • 如果 x 为0,则直接返回0。
  2. 搜索范围
    • 左边界 left 初始化为1,因为0的平方根是0(已经特殊处理),而任何正数的平方根至少为1。
    • 右边界 right 初始化为 Math.floor(x/2)+1,因为平方根不会超过 x/2(对于非负整数 x)。加1是为了确保在 x 为完全平方数时能够包含这个平方根。
  3. 二分查找
    • 在每次迭代中,计算中间值 mid 及其平方 square
    • 根据 square 与 x 的比较结果,移动左边界或右边界。
  4. 返回结果
    • 循环结束时,返回 right,它是最后一个使得 mid * mid <= x 的 mid 值,也就是我们要找的平方根的整数部分。

这种方法的时间复杂度是 O(logn),其中 n 是 x 的值,因为每次迭代都会将搜索范围减半。

更精确 (待进一步补充)

function mySqrt(x) {  if (x === 0) return 0; // 特殊情况处理:0的平方根是0  let guess = x; // 初始猜测值设为x本身(对于非负整数,平方根不会超过x本身)  let epsilon = 1; // 精度控制,用于判断迭代是否结束  while (Math.abs(guess * guess - x) >= epsilon) {  // 牛顿迭代公式:guess = (guess + x / guess) / 2  guess = Math.floor((guess + Math.floor(x / guess)) / 2);  // 为了确保精度,逐步减小epsilon  epsilon /= 10;  }  return guess;  
}  // 测试  
console.log(mySqrt(4));  // 输出: 2  
console.log(mySqrt(8));  // 输出: 2 (8的平方根约为2.8284,取整数部分2)  
console.log(mySqrt(15)); // 输出: 3 (15的平方根约为3.8729,取整数部分3)
  1. 初始猜测值
    • 对于非负整数 x,初始猜测值设为 x 本身,因为平方根不会超过 x 本身。
  2. 牛顿迭代公式
    • 牛顿迭代法的公式为:new_guess=(old_guess+x/old_guess)/2​​
    • 这个公式通过不断迭代来逼近平方根的值。
  3. 精度控制
    • 使用 epsilon 来控制精度,初始设为 1。
    • 每次迭代后,将 epsilon 除以 10,逐步减小精度要求,确保最终结果的准确性。
  4. 取整
    • 使用 Math.floor 函数来确保结果只保留整数部分。
 5.有效的完全平方数(与上类似)
/*** @param {number} num* @return {boolean}*/
var isPerfectSquare = function(num) {if(num===1)return truelet left=1;let right=Math.floor(num/2)+1;//天天天,你条件写错了!!!!while(left<=right){let mid = Math.floor((left+right)/2);let square=mid*mid;if(square===num){return true;}else if(square>num){right=mid-1;}else{left=mid+1;}}return false;
};
  1. 题目:给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1。
    • 解析:这是二分查找算法的最基本应用。通过设定左右指针,不断缩小搜索范围,直到找到目标值或确定目标值不存在。
  2. 题目:给定一个按照非递减顺序排列的整数数组nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target,返回[-1, -1]。
    • 解析:这个问题可以先用二分查找找到目标值的一个位置,然后通过双指针从中间向两边扩散,找到目标值的开始位置和结束位置。这种方法的时间复杂度为O(log n + k),其中n是数组的长度,k是目标值在数组中出现的次数。
  3. 题目:在旋转排序数组中查找目标值(假设数组中不存在重复元素)。
    • 解析:旋转排序数组是指一个递增排序数组经过一次旋转得到的数组。这个问题可以通过修改二分查找算法来解决。首先,找到数组中的“旋转点”(即数组从递增变为递减的点),然后根据目标值与旋转点的大小关系,在数组的左侧或右侧进行二分查找。
  4. 题目:在有序数组中查找第一个大于给定值的元素。
    • 解析:这个问题可以通过二分查找算法来解决。在每次迭代中,根据中间元素与目标值的大小关系,更新搜索范围,直到找到第一个大于目标值的元素或确定不存在这样的元素。

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

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

相关文章

波浪理论(Elliott Wave Theory)

拉尔夫纳尔逊艾略特 拉尔夫纳尔逊艾略特&#xff08;1871年07月28日-1948年01月15日&#xff09;&#xff0c;1871年7月28日出生在美国堪萨斯州的玛丽斯维利镇&#xff0c;是一名杰出的会计师&#xff0c;作家及金融市场分析大师&#xff0c;以其著名的波浪理论而留名于世。 波…

ubuntu 安装 MySql5.7(基于ARM架构 源码安装)

1 系统需求 目标安装MySql5.7版本。 系统环境&#xff1a; oracle云主机,arm架构 确认主机架构如下图&#xff1a; 查看是否有5.7版本的源 apt-cache search mysql | grep mysql-server 执行后发现只有8.0版本的&#xff0c;5.7版本只能通过源码安装了。 2 下载MySql源码…

MATLAB边缘检测

一、目的&#xff1a; 熟悉边缘检测原理&#xff0c;并运用matlab软件实现图像的canny边缘检测&#xff0c;体会canny边缘检测的优缺点。 二、内容&#xff1a; 编写matlab程序&#xff0c;实现对lena图像的边缘检测&#xff0c;输出程序运行结果。 三、原理或步骤&#x…

多线程(七):单例模式指令重排序

目录 1. 单例模式 1.1 饿汉模式 1.2 懒汉模式 2. 懒汉模式下的问题 2.1 线程安全问题 2.2 如何解决 --- 加锁 2.3 加锁引入的新问题 --- 性能问题 2.4 指令重排序问题 2.4.1 指令重排序 2.4.2 指令重排序引发的问题 1. 单例模式 单例模式, 是设计模式中最典型的一种模…

VMware:Windows主机与CentOS虚拟机文件互传文件共享

注意&#xff1a;本文使用Win10与VMware17pro互传 1. 本地创建文件夹 如下图创建一个文件夹&#xff0c;名字任意 2. 设置本地文件夹权限 右键文件夹 - - 属性 - - 共享 - - 高级共享 - - 权限 - - 如下图全部勾选 - - 应用 - - 确认 3. VMware中设置共享文件夹路径 第一步…

使用Three.js和Force-Directed Graph实现3D知识图谱可视化

先看样式&#xff1a; 在当今信息爆炸的时代&#xff0c;如何有效地组织和展示复杂的知识结构成为一个重要的挑战。3D知识图谱可视化是一种直观、交互性强的方式来呈现知识之间的关系。本文将详细介绍如何使用HTML、JavaScript、Three.js和Force-Directed Graph库来实现一个交互…

【动态规划】【路径问题】下降路经最小和、最小路径和、地下城游戏

4. 下降路径最小和 931. 下降路径最小和 算法原理 确定状态表示 dp[i][j] 表示&#xff1a;到达 [i, j] 位置&#xff0c;最小的下降路径 状态转移方程 dp[i][j] 从 [i-1, j-1] 到达 [i, j] > dp[i-1][j-1] m[i][j]从 [i-1, j] 到达 [i, j] > dp[i-1][j] m[i][j]从 …

已解决:ModuleNotFoundError: No module named ‘pip‘

[已解决] ModuleNotFoundError: No module named ‘pip‘ 文章目录 写在前面问题描述报错原因分析 解决思路解决办法1. 手动安装或升级 pip2. 使用 get-pip.py 脚本3. 检查环境变量配置4. 重新安装 Python 并确保添加到 PATH5. 在虚拟环境中安装 pip6. 使用 conda 安装 pip&…

智简魔方业务管理系统v10 好用的IDC业务管理软件

智简魔方业务管理系统v10&#xff0c;您一直在寻找的IDC业务管理软件&#xff0c;基于PHPMYSQL开发的一套小型易于部署的业务管理核心&#xff0c;具有极强的扩展能力&#xff0c;非常方便的安装方式&#xff0c;用户可在5分钟内部署属于自己的业务管理系统&#xff0c;ZJMF-CB…

路由表来源(基于华为模拟器eNSP)

概叙 在交换网络中&#xff0c;若要实现不同网段之间的通信&#xff0c;需要依靠三层设备&#xff08;路由器、三层交换机等&#xff09;&#xff0c;而路由器只知道其直连网段的路由条目&#xff0c;对于非直连的网段&#xff0c;在默认情况下&#xff0c;路由器是不可达的&a…

Goland 搭建Gin脚手架

一、使用编辑器goland 搭建gin 打开编辑器 新建项目后 点击 create 二、获得Gin框架的代码 命令行安装 go get -u github.com/gin-gonic/gin 如果安装不上&#xff0c;配置一下环境 下载完成 官网git上下载 这样就下载完成了。、 不过这种方法需要设置一下GOPATH 然后再执…

【An】Animate 2024 for【Mac】 An动画设计制作软件 安装教程——保姆级教程

Mac分享吧 文章目录 【An】Animate 2024 Mac版 An动画设计制作软件 安装完成&#xff0c;打开效果Mac电脑【An】Animate 2024 动画设计制作软件——v24.0.4⚠️注意事项&#xff1a;1️⃣&#xff1a;下载软件2️⃣&#xff1a;安装AntiCC组件&#xff0c;步骤见文章或下图3️…

springboot+uinapp基于Android的固定资产借用管理平台

文章目录 前言项目介绍技术介绍功能介绍核心代码数据库参考 系统效果图论文效果图 前言 文章底部名片&#xff0c;获取项目的完整演示视频&#xff0c;免费解答技术疑问 项目介绍 固定资产借用管理平台设计的目的是为用户提供使用申请、故障报修、设备归还、意见反馈等管理方…

嘉立创EDA个人学习笔记2(绘制51单片机核心板)

前言 本篇文章属于嘉立创EDA的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 【教程】零基础入门PCB设计-国一学长带你学立创EDA专业版 全程保姆…

新手入门之Spring Bean

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、初识SpringBootSpringBoot 的主要特点1、自动配置&#xff1a;2、外部化配置&#xff1a;3、嵌入式服务器支持&#xff1a;4、启动器依赖&#xff08;Start…

大数据新视界 --大数据大厂之数据脱敏技术在大数据中的应用与挑战

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

R语言机器学习算法实战系列(十)自适应提升分类算法 (Adaptive Boosting)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍原理步骤教程下载数据加载R包导入数据数据预处理数据描述数据切割调节参数构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性保存模型总…

【图解版】力扣第162题:寻找峰值

注意 题目只要求找到一个峰值就可以了。nums[-1]和nums[n]这两个位置是负无穷&#xff0c;也就是说&#xff0c;除了数组的位置之外&#xff0c;其它地方都是负无穷。对于所有有效的 i 都有 nums[i] ! nums[i 1] 方法一 遍历整个数组&#xff0c;找到最高的那个点。时间复杂…

大数据治理:数据时代的挑战与应对

目录 大数据治理&#xff1a;数据时代的挑战与应对 一、大数据治理的概念与内涵 二、大数据治理的重要性 1. 提高数据质量与可用性 2. 确保数据安全与合规 3. 支持数据驱动的决策 4. 提高业务效率与竞争力 三、大数据治理的实施策略 1. 建立健全的数据治理框架 2. 数…

C++STL--------list

文章目录 一、list链表的使用1、迭代器2、头插、头删3、insert任意位置插入4、erase任意位置删除5、push_back 和 pop_back()6、emplace_back尾插7、swap交换链表8、reverse逆置9、merge归并10、unique去重11、remove删除指定的值12、splice把一个链表的结点转移个另一个链表13…