Javascript 无处不在的二分搜索

2024-04-19 22:52

本文主要是介绍Javascript 无处不在的二分搜索,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        我们知道二分查找算法。二分查找是最容易正确的算法。我提出了一些我在二分搜索中收集的有趣问题。有一些关于二分搜索的请求。我请求您遵守准则:“我真诚地尝试解决问题并确保不存在极端情况”。阅读完每个问题后,最小化浏览器并尝试解决它。 
        问题陈述:给定一个由 N 个不同元素组成的排序数组,使用最少的比较次数在数组中找到一个键。 (您认为二分搜索是在排序数组中搜索键的最佳选择吗?)无需太多理论,这里是典型的二分搜索算法。

// Javascript code to implement the approach
 
 
// Returns location of key, or -1 if not found
function BinarySearch(A, l, r, key) {
  let m;
 
  while (l < r) {
    m = l + (r - l) / 2;
 
    if (A[m] == key) // first comparison
      return m;
 
    if (A[m] < key) // second comparison
      l = m + 1;
    else
      r = m - 1;
  }
 
  return -1;
}
 
// This code is contributed by gfgking 

        理论上,最坏情况下我们需要进行log N + 1次比较。如果我们观察的话,我们会在每次迭代中使用两次比较,除非最终成功匹配(如果有)。在实践中,比较将是昂贵的操作,它不仅仅是原始类型比较。尽量减少与理论极限的比较更为经济。请参阅下图,了解下一个实现中索引的初始化。 

以下实现使用较少的比较次数。  

// Invariant: A[l] <= key and A[r] > key
// Boundary: |r - l| = 1
// Input: A[l .... r-1]
function BinarySearch(A, l, r, key)
{
    let m;
 
    while( r - l > 1 )
    {
        m = l + (r-l)/2;
 
        if( A[m] <= key )
            l = m;
        else
            r = m;
    }
 
    if( A[l] == key )
        return l;
    if( A[r] == key )
        return r;
    else
        return -1;

        在 while 循环中,我们仅依赖于一次比较。搜索空间收敛到将l和r指向两个不同的连续元素。我们需要再进行一次比较来跟踪搜索状态。您可以查看示例测试用例 http://ideone.com/76bad0。 (C++11 代码)。

        问题陈述:给定一个由 N 个不同整数组成的数组,找到输入“key”的下限值。假设 A = {-1, 2, 3, 5, 6, 8, 9, 10} 且 key = 7,我们应该返回 6 作为结果。我们可以使用上面的优化实现来找到键的下限值。只要不变量成立,我们就不断地将左指针移到最右边。最终左指针指向小于或等于 key 的元素(根据定义下限值)。以下是可能的极端情况, —> 如果数组中的所有元素都小于 key,则左指针移动到最后一个元素。 —> 如果数组中的所有元素都大于 key,则为错误情况。 —> 如果数组中的所有元素都相等且 <= key,则这是我们实现的最坏情况输入。
这是示例: 

// largest value <= key
// Invariant: A[l] <= key and A[r] > key
// Boundary: |r - l| = 1
// Input: A[l .... r-1]
// Precondition: A[l] <= key <= A[r]
function Floor(A, l, r, key){
    let m;
    while(r - l > 1){
        m = l + parseInt((r-l)/2);
        if(A[m] <= key) l = m;
        else r = m;
    }
    return A[l];
}
 
// Initial call
function Floor(A, size, key)
{
    // Add error checking if key < A[0]
    if( key < A[0] )
        return -1;
  
    // Observe boundaries
    return Floor(A, 0, size, key);
}
 
// THIS CODE IS CONTRIBUTED BY YASH AGARWAL(YASHAGAWRAL2852002) 

您可以看到一些测试用例 http://ideone.com/z0Kx4a。 

        问题陈述:给定一个可能有重复元素的排序数组。查找log N时间内输入“key”出现的次数。这里的想法是使用二分搜索查找数组中最左边和最右边出现的键。我们可以修改底函数来跟踪最右边的出现和最左边的出现。 
这是示例: 

// Input: Indices Range [l ... r)
// Invariant: A[l] <= key and A[r] > key
function getRightPosition(A, l, r, key) {
    while (r - l > 1) {
        const m = l + Math.floor((r - l) / 2);
        if (A[m] <= key) {
            l = m;
        } else {
            r = m;
        }
    }
    return l;
}
 
// Input: Indices Range (l ... r]
// Invariant: A[r] >= key and A[l] > key
function getLeftPosition(A, l, r, key) {
    while (r - l > 1) {
        const m = l + Math.floor((r - l) / 2);
        if (A[m] >= key) {
            r = m;
        } else {
            l = m;
        }
    }
    return r;
}
 
function countOccurrences(A, size, key) {
    // Observe boundary conditions
    let left = getLeftPosition(A, -1, size - 1, key);
    let right = getRightPosition(A, 0, size, key);
 
    // What if the element doesn't exist in the array?
    // The checks help to determine whether the element exists
 
    if (A[left] === key && key === A[right]) {
        return right - left + 1;
    }
    return 0;
}
 
// Example usage
const A = [1, 2, 2, 2, 3, 4, 4, 4, 5, 5, 6];
const key = 4;
const size = A.length;
const occurrences = countOccurrences(A, size, key);
console.log(`The number of occurrences of ${key} is: ${occurrences}`); 

示例代码 zn6R6a - Online C++0x Compiler & Debugging Tool - Ideone.com。   

        问题陈述: 给定一个由不同元素组成的排序数组,并且该数组在未知位置旋转。找到数组中的最小元素。我们可以在下图中看到示例输入数组的图示。

        我们收敛搜索空间直到l和r 指向单个元素。如果中间位置落在第一个脉冲中,则不满足条件 A[m] < A[r],我们将搜索空间收敛到 A[m+1 … r]。如果中间位置落在第二个脉冲中,则满足条件 A[m] < A[r],我们将搜索空间收敛到 A[1 … m]。在每次迭代中,我们都会检查搜索空间大小,如果它是 1,我们就完成了。
        下面给出的是算法的实现。 你能想出不同的实施方案吗?   

function BinarySearchIndexOfMinimumRotatedArray(A, l, r){
    // extreme condition, size zero or size two
    let m;
     
    // Precondition: A[l] > A[r]
    if(A[l] <= A[r]) return l;
     
    while(l <= r){
        // Termination condition (l will eventually falls on r, and r always
        // point minimum possible value)
        if(l == r) return l;
        m = l + (r-l)/2;
        if(A[m] < A[r]){
            // min can't be in the range
            // (m < i <= r), we can exclude A[m+1 ... r]
            r = m;
        }else{
            // min must be in the range (m < i <= r),
            // we must search in A[m+1 ... r]
            l = m+1;
        }
    }
    return -1;
}
 
function BinarySearchIndexOfMinimumRotatedArray(A, size){
    return BinarySearchIndexOfMinimumRotatedArray(A, 0, size-1);

 请参阅示例测试用例 KbwDrk - Online C++0x Compiler & Debugging Tool - Ideone.com。 

练习: 
1. 称为signum(x, y)的函数 定义为,

Signum(x, y) = -1 如果 x < y 
             = 0 如果 x = y 
             = 1 如果 x > y

您是否遇到过比较行为类似于符号函数的指令集?它能让二分搜索的第一个实现变得最优吗? 

2. 实现floor函数的ceil函数复制品。 

3. 与你的朋友讨论“二分查找是否是最优的(比较次数最少)?为什么不在排序数组上进行三元搜索或插值搜索?与二分搜索相比,您什么时候更喜欢三元搜索或插值搜索?” 

4. 画出二分搜索的树表示(相信我,这对你理解二分搜索的内部原理有很大帮助)。  

这篇关于Javascript 无处不在的二分搜索的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/918660

相关文章

SpringBoot中四种AOP实战应用场景及代码实现

《SpringBoot中四种AOP实战应用场景及代码实现》面向切面编程(AOP)是Spring框架的核心功能之一,它通过预编译和运行期动态代理实现程序功能的统一维护,在SpringBoot应用中,AO... 目录引言场景一:日志记录与性能监控业务需求实现方案使用示例扩展:MDC实现请求跟踪场景二:权限控制与

Java NoClassDefFoundError运行时错误分析解决

《JavaNoClassDefFoundError运行时错误分析解决》在Java开发中,NoClassDefFoundError是一种常见的运行时错误,它通常表明Java虚拟机在尝试加载一个类时未能... 目录前言一、问题分析二、报错原因三、解决思路检查类路径配置检查依赖库检查类文件调试类加载器问题四、常见

Java注解之超越Javadoc的元数据利器详解

《Java注解之超越Javadoc的元数据利器详解》本文将深入探讨Java注解的定义、类型、内置注解、自定义注解、保留策略、实际应用场景及最佳实践,无论是初学者还是资深开发者,都能通过本文了解如何利用... 目录什么是注解?注解的类型内置注编程解自定义注解注解的保留策略实际用例最佳实践总结在 Java 编程

Java 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows