首页 > 编程语言 >Javascript算法——二分查找

Javascript算法——二分查找

时间:2024-10-16 18:45:58浏览次数:3  
标签:二分 right return target nums Javascript mid 查找 left

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;
};

标签:二分,right,return,target,nums,Javascript,mid,查找,left
From: https://blog.csdn.net/m0_55049655/article/details/142975354

相关文章

  • 前端开发 --JavaScript
    前言html种script主要包括内联script和引用外部JavaScript文件两张方式1.内联script的用法内联script指的是将JavaScript代码直接写在html文档中某个部位<!--内嵌--><script>alert(1)</script><script>windowonload=function(){vara......
  • <Leetcode:算法题及解析>最大子数组和(Javascript版)
    题目描述:给你一个整数数组nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。本题可以使用Kadane's算法实现,这是一种用于解决最大子数组和问题的高效算法。它由JosephBornKadane在1984年提出。这个算法的核......
  • 代码随想录算法训练营第一天|704二分查找、27移除元素、977有序数组的平方
    代码随想录算法训练营第一天|704二分查找、27移除元素、977有序数组的平方1Leetcode704二分查找题目链接:[704.二分查找](704.二分查找-力扣(LeetCode))文章链接:[代码随想录](代码随想录(programmercarl.com))视频链接:[手把手带你撕出正确的二分法|二分查找法|二分搜......
  • 前端新手教程:HTML、CSS 和 JavaScript 全面详解及实用案例
    一、引言在当今数字化的时代,前端开发扮演着至关重要的角色,它决定了用户与网页和应用程序交互的体验。HTML、CSS和JavaScript作为前端开发的核心技术,分别负责网页的结构、样式和交互。本教程将为前端新手全面深入地介绍HTML、CSS和JavaScript的知识点,并通过实用案例帮助......
  • JavaScript 实现购物车数量增加减少功能
    场景描述在实现购物车功能时,需要限制用户加购的数量必须大于0且不超过加购商品的库存量。代码实现下列代码中,<input></input>中使用min属性定义数量的最小值,max属性定义数量的最大值。在实际开发中可以整合springboot和thymeleaf,使用th:max="${商品对象的库存量}"将ma......
  • 二分查找
    [https://leetcode.cn/problems/binary-search/](题目链接)二分查找有前提条件:序列有序。序列中无重复元素不是必要条件。二分查找注意区间:如果是左闭右开,while循环的条件是left<right,每一次查找结束后,left要更新为mid+1或者right更新为mid;如果是左闭右闭,循环条件是left<=r......
  • E Revenge on My Boss CCPC 2023 Harbin Site 贪心,二分
    传送门给出了三个数组\(\{a_i\},\{b_i\},\{c_i\}\)要求给出一个排列\(p\)最小化:任选一个位置\(m\),最大化贡献\(S=(\sum_{i=1}^ma_{p_i}+\sum_{i=m}^nb_{p_i})c_{p_m}\)。标准的最小的最大提示我们考虑二分。这里直接二分答案\(Mid\)。那么就考虑是否存在一个排列使得对于任意\(......
  • C#哈希查找算法
    前言哈希查找算法是一种高效的查找算法,通过将键值映射到哈希表中的位置来实现快速访问。在C#中,哈希查找通常通过哈希表(Hashtable)或字典(Dictionary)来实现。实现原理哈希函数:将键值转换成哈希值,该哈希值决定了键值在哈希表中的位置。哈希表:一种数据结构,用于存储键值对。哈希表中的位......
  • C#哈希查找算法
    前言哈希查找算法是一种高效的查找算法,通过将键值映射到哈希表中的位置来实现快速访问。在C#中,哈希查找通常通过哈希表(Hashtable)或字典(Dictionary)来实现。实现原理哈希函数:将键值转换成哈希值,该哈希值决定了键值在哈希表中的位置。哈希表:一种数据结构,用于存储键值对。哈希表......
  • 前端原型链:探索 JavaScript 中的继承奥秘
    一、引言在前端开发领域,JavaScript是一门广泛应用的编程语言。而原型链作为JavaScript中一个重要的概念,对于理解JavaScript的面向对象特性和实现继承机制起着关键作用。它不仅影响着代码的组织和复用方式,还决定了对象之间的关系和属性访问规则。本文将深入探讨前端原型链......