Javascript算法——二分查找

news2024/10/21 21:28:19

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 + 1

    return 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的时候更新right
                right = 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的时候更新left
                left = 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 true
    let 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.coloradmin.cn/o/2220300.html

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

相关文章

⌈ 传知代码 ⌋ 无监督动画中关节动画的运动表示

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

PP-ChatOCRv3—文档场景信息抽取v3产线使用教程

文档场景信息抽取v3产线使用教程 1. 文档场景信息抽取v3产线介绍 文档场景信息抽取v3&#xff08;PP-ChatOCRv3&#xff09;是飞桨特色的文档和图像智能分析解决方案&#xff0c;结合了 LLM 和 OCR 技术&#xff0c;一站式解决版面分析、生僻字、多页 pdf、表格、印章识别等常…

Spring连接数据库:Mybatis

MyBatis是一款优秀的框架 在数据库中创建表 1.创建项目mybatis 2.在proxml文件中导入必要配置并进行编译 <dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33<…

电能表预付费系统-标准传输规范(STS)(15)

6.3.7 CRC: CyclicRedundancyCheck (循环冗余校验) The CRC is a checksum field used to verify the integrity of the data transferred for all tokens, except for Class 0 with SubClass 4 to 7, which uses CRC_C (see 6.3.22). The checksum is derived using the foll…

网络基础知识:交换机关键知识解析

了解交换机的关键知识对网络工程师至关重要。 以下是交换机的基础知识解析&#xff0c;包括其基本概念、工作原理和关键技术点&#xff1a; 01-交换机的基本概念 交换机是一种网络设备&#xff0c;用于在局域网&#xff08;LAN&#xff09;中连接多个设备&#xff0c;如计算机…

如何恢复U盘里格式化数据?别慌,有带图详细步骤!

U盘&#xff0c;这个小巧的存储神器&#xff0c;我们几乎天天都在用。但有时候&#xff0c;一不小心手滑&#xff0c;U盘就被格式化了&#xff0c;里面的东西好像全没了&#xff0c;别急&#xff0c;其实数据恢复没那么难。这篇文章就来告诉你&#xff0c;怎么把格式化的U盘里的…

NVR录像机汇聚管理EasyNVR多品牌NVR管理工具/设备云台接入及控制详解

在当今快速发展的信息化时代&#xff0c;视频监控系统已成为企业管理和安全防范的重要工具。随着技术的不断进步&#xff0c;多品牌NVR&#xff08;网络视频录像机&#xff09;管理工具如海康NVR管理平台/工具EasyNVR多个NVR同时管理凭借其强大的兼容性和智能化管理功能&#x…

【LInux】Shell脚本编写基本语法

文章目录 一、前期准备1、查看本机bash2、编辑脚本 二、 判断结构1、if结构2、if/else结构3、if/elif/else结构4、case结构 三、循环结构1、for循环2、while循环3、until循环 四、谢谢观看&#xff01; 一、前期准备 1、查看本机bash which bash之后编写脚本时&#xff0c;第…

数据同步工具Sqoop原理及场景优化

目录 0 数据同步策略 1 数据同步工具 ​编辑 2 Sqoop同步数据原理分析 2.1 原理分析 2.2 Sqoop基本使用分析 3 切片逻辑 3.1 MR切片逻辑 3.2 Hive CombineInputformat切片逻辑 3.3 实验1:Map任务并行度分析1 3.4 实验2: Map任务并行度分析2 3.5 实验3:Map任务并行…

C++ 类的基础用法与详细说明:简单易懂的入门指南

什么是类&#xff1f; C类_百度百科 类是C中一种用于封装数据和功能的基本结构。你可以将类视为一种自定义的数据类型&#xff0c;它可以包含数据&#xff08;成员变量&#xff09;和操作这些数据的函数&#xff08;成员函数&#xff09;。 创建一个简单的类 让我们通过一个…

Java爬虫:获取商品评论数据的高效工具

在电子商务的激烈竞争中&#xff0c;商品评论作为消费者购买决策的重要参考&#xff0c;对于商家来说具有极高的价值。它不仅能够帮助商家了解消费者的需求和反馈&#xff0c;还能作为改进产品和服务的依据。Java爬虫技术&#xff0c;以其稳健性和高效性&#xff0c;成为了获取…

Vue2的依赖注入(跨级通信)基本使用

provide(提供) &#xff0c;inject(注入) 祖先级组件用provide传递数据,它的所有后代都可以通过inject取到数据 使用演示&#xff1a; //祖先组件 <template><div>父组件传的值&#xff1a;{{num}} </div> </template><script> //导入子组件 i…

SourceMonitor,免费代码统计工具,覆盖率,圈复杂度,代码行

SourceMonitor是一款免费的代码度量工具&#xff0c;包含代码行&#xff0c;覆盖率&#xff08;无条件覆盖率&#xff09;&#xff0c;圈复杂度&#xff0c;函数调用深度等指标的测量。 用以下软件安装包&#xff0c;低版本的初始化工程会闪退&#xff0c;工具下载路径&#xf…

Python(numpy库)

numpy基础 NumPy 的全称是“ Numeric Python”&#xff0c;它是 Python 的第三方扩展包&#xff0c;主要用来计算、处理一维或多维数组 ndarray NumPy 定义了一个 n 维数组对象&#xff0c;简称 ndarray 对象&#xff0c;它是一个一系列相同类型元素组成的数组集合。数组中的…

h5页面与小程序页面互相跳转

小程序跳转h5页面 一个home页 /pages/home/home 一个含有点击事件的元素&#xff1a;<button type"primary" bind:tap"toWebView">点击跳转h5页面</button>toWebView(){ wx.navigateTo({ url: /pages/webview/webview }) } 一个webView页 /pa…

springboot二手交易平台

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 系统展示 【2024最新】基于JavaSpringBootVueMySQL的&#xff0c;前后端分离。 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;…

电脑上的顽固软件卸载不干净?试试这几款卸载工具,简单几步解决

相信大家日常的工作或学习生活中&#xff0c;都需要使用电脑&#xff0c;而为了完成工作&#xff0c;都会在电脑上安装一些工具。有时候不需要这些工具了&#xff0c;想要卸载时&#xff0c;发现有些软件太顽固了&#xff0c;卸载不掉&#xff0c;或者是卸载不干净。如果您也遇…

uniapp-uniapp + vue3 + pinia 搭建uniapp模板

使用技术 ⚡️uni-app, Vue3, Vite, pnpm &#x1f4e6; 组件自动化引入 &#x1f34d; 使用 Pinia 的状态管理 &#x1f3a8; tailwindcss - 高性能且极具灵活性的即时原子化 CSS 引擎 &#x1f603; 各种图标集为你所用 &#x1f525; 使用 新的 <script setup> …

Excel功能区变灰是什么原因造成?怎么解决?

Microsoft Excel是广泛使用的电子表格软件&#xff0c;但有时用户可能会遇到功能区变灰的问题&#xff0c;这可能导致功能无法使用&#xff0c;影响工作效率和用户体验。本文将深入探讨Excel功能区灰色的原因及解决方案&#xff0c;帮助读者快速解决这一问题。 一、Excel功能区…

Polypyus部署、使用与原理分析

文章目录 前言1、概述2、安装与使用2.1、源码安装2.1.1、部署系统依赖组件2.1.2、使用源码安装系统 2.2、使用方法2.2.1、命令行界面操作方法2.2.2、图形界面操作方法 3、测试用例4、参考文献总结 前言 本博客的主要内容为Polypyus的部署、使用与原理分析。本博文内容较长&…