二分查找的原理及实现

简介: 在开发中我们经常会听到"分治法"、“二分思想”的理念,而二分查找就是该理念的经典实现。经典面试题:在一个有序数组中如何快速的查找目标为n的元素索引位置,不存在则返回-1。

前言


在开发中我们经常会听到"分治法"、“二分思想”的理念,而二分查找就是该理念的经典实现。


经典面试题:在一个有序数组中如何快速的查找目标为n的元素索引位置,不存在则返回-1。


小白式算法实现


尤其是面试时,当面试官问到这样一个问题,小白式的同学感觉会特别简单,遍历一遍进行比对不就OK了,今天真是中奖了,这么简单~~


No Use Think! Up Code! (不用想!上代码!)


/**
 * @method forSearch
 * @description 使用for循环查询
 * @param nums number[]
 * @param target number
 * @returns number
 */
function forSearch(nums: number[], target: number): number {
  // 数组长度
  const len = nums.length;
  // 临界值判断
  if (len === 0) {
    return -1;
  }
  for (let i = 0; i < len; i++) {
    if (nums[i] === target) {
      return i;
    }
  }
  return -1;
}


以上实现算法复杂度:


  • 空间复杂度 O(n)O(n)O(n)


  • 时间复杂度 O(n)O(n)O(n)


一般当你写出这个答案的时候,面试官内心肯定会嘿嘿一笑,这不又入坑一个。接下来就会“贱贱”的提出下一个问题,有没有更优的算法实现,复杂度降低到

O(logn)O(logn)O(logn)


低于O(n)O(n)O(n)时间复杂度的实现有没有?


一般当你看到复杂度O(logn)O(logn)O(logn)的时候,一定要有算法的敏感性,这个就是要本着二分法去的~


二分法有个必然的前提是数组必须是有序的!升序和降序无所谓。


二分法是什么原理呢,假定当前nums是一个包含数字的升序数组。每次取数组中的12{\frac 1 2}21位置的值midValue,与目标值target进行比对:


  1. midValue > target 值,说明与target匹配的值是在左边,接下来再取左侧的12{\frac 1 2}21位置的数据进行比对;


  1. midValue < target 值,说明与target匹配的值是在右边,接下来再取右侧的12{\frac 1 2}21位置的数据进行比对;


  1. midValue === target值,返回对应位置索引;


  1. 若最终比对没有符合目标值的,返回 -1


根据这个逻辑,上代码看一看~


/**
 * @method binarySearch
 * @description 基于二分法实现查询 - 函数递归
 * @param nums number[]
 * @param target number
 * @param startIndex number 表示开始比对的左侧索引开始位置
 * @param endIndex number 表示开始比对的右侧索引结束位置
 * @returns
 */
function binarySearch(
  nums: number[],
  target: number,
  startIndex?: number,
  endIndex?: number
): number {
  // 获取数组
  const len = nums.length;
  // 临街值判断
  if (len === 0) {
    return -1;
  }
  // 在初始值情况下,对startIndex和endIndex进行初始化赋值
  if (startIndex === undefined) {
    // 索引0开始
    startIndex = 0;
  }
  if (endIndex === undefined) {
    // 最后一个索引位置结束
    endIndex = len - 1;
  }
  // 判断startIndex和endIndex是否已经交叉,若交叉说明没有匹配的结果
  if (startIndex > endIndex) return -1;
  // 获取当前startIndex和endIndex的二分之一位置
  const midIndex = Math.floor((startIndex + endIndex) / 2); // 向下取整,避免奇偶数长度问题
  // 二分之一位置的值
  const midValue = nums[midIndex];
  if (midValue > target) {
    // 在左侧,将结束索引位置,移动到 midIndex - 1
    return binarySearch2(nums, target, startIndex, midIndex - 1);
  } else if (midValue < target) {
    // 在右侧,将开始索引位置,移动到 midIndex + 1
    return binarySearch2(nums, target, midIndex + 1, endIndex);
  } else {
    // 匹配返回索引
    return midIndex;
  }
}


二分法查找,每次都在进行二分之一处理的时候,都是在逼近最终的结果,其时间复杂度为O(logn)O(logn)O(logn)


对比下两个方案的性能:选取一个1W条数据的数组,查询999,每个查询都执行了10W次。


// 声明数组nums
const nums = [];
for (let i = 0; i < 10000; i++) {
  nums.push(i);
}
// 设置target
const target = 999;
// for循环的实现
console.time('forSearch');
for (let i = 0; i < 10 * 10000; i++) {
  forSearch(nums, target);
}
console.timeEnd('forSearch');
// 二分查找实现
console.time('binarySearch');
for (let i = 0; i < 10 * 10000; i++) {
  binarySearch(nums, target);
}
console.timeEnd('binarySearch');


有图有真相,二者时间消耗对比还是非常明显的,O(log(n))O(log(n))O(log(n))复杂度要远胜于O(n)O(n)O(n)复杂度的。


网络异常,图片无法展示
|


注:大家在看算法复杂度时,一定要注意有最好情况时间复杂度、最坏情况时间复杂度、平均时间复杂度、均摊时间复杂度。


方案一 - 循环查询,最好的时间复杂度就是O(1)O(1)O(1),第一个找的元素就是。最坏的时间复杂度是O(n)O(n)O(n),最后一个元素才是。


方案二 - 二分查询,最好时间复杂度是O(1)O(1)O(1),恰好查询的元素是二分之一位置的,最坏的是O(logn)O(logn)O(logn),最边上的元素是目标元素。


而且复杂度讨论的是量级问题,而并不是一个时间损耗绝对值的问题。


相关文章
|
Linux 数据安全/隐私保护 虚拟化
Linux技术基础(1)——操作系统的安装
本文是龙蜥操作系统(Anolis OS) 8.4 的安装指南,用户可以从[龙蜥社区下载页面](https://openanolis.cn/download)获取ISO镜像。安装方法包括物理机的光驱和USB闪存方式,以及虚拟机中的VMware Workstation Pro设置。安装过程涉及选择语言、配置安装目标、选择软件集合和内核,设置Root密码及创建新用户。安装完成后,可通过文本模式或图形化界面验证系统版本,如Anolis OS 8.4,标志着安装成功。
|
20天前
|
人工智能 自然语言处理 搜索推荐
2025智能营销产品深度评测,国内主流智能营销厂商推荐
在数字化运营深化的时代,用户资产成为企业核心竞争力。用户智能运营产品已从单一营销工具演变为支撑用户生命周期管理、降本增效、业务增长的关键基础设施。面对AI自动化、全渠道数据整合、私域公域协同等趋势,企业需构建涵盖场景适配性、数据能力、智能化、生态集成等维度的选型体系。本文对比瓴羊Quick Audience、神策数据、致趣百川、Convertlab、HubSpot、Adobe Experience Cloud六大主流产品,揭示其在数据整合、运营自动化、个性化能力等方面差异,为企业提供科学选型参考,助力实现精细化运营转型。
|
7月前
|
存储 人工智能 编解码
吞噬混沌:CodeBuddy与流程利刃,斩破游戏开发的蛮荒时代(一)
本文探讨了《飞机大战游戏开发流程规范》的工程化实践,涵盖版本控制、任务分配与系统设计。通过CodeBuddy智能工具,实现分支管理自动化、环境配置标准化及代码质量提升。在UI开发中,CodeBuddy确保继承规范与Docstring完整性;AI行为树开发中,它检测逻辑死循环与状态处理问题;输入系统设计中,辅助键位绑定一致性与事件处理完整性。CodeBuddy作为腾讯云推出的智能助手,将静态规范转化为动态辅助,助力游戏开发迈向“规范为骨、智能为翼”的新时代。
230 4
|
机器学习/深度学习 并行计算 TensorFlow
揭示 GPU 上的批处理策略
【6月更文挑战第9天】批处理策略是优化GPU效率的关键技术,通过组合处理多个数据样本,减少数据传输、充分利用并行计算,提升GPU计算效率。在TensorFlow示例中,批处理用于神经网络训练,但选择合适的批处理大小需考虑GPU内存、模型复杂度和数据特性,以达到最佳性能。批处理策略将持续发展,支持深度学习的进步。
232 7
|
9月前
|
机器学习/深度学习 算法 API
淘宝图片搜索商品列表API接口全攻略
淘宝图片搜索API(拍立淘)通过上传图片快速检索淘宝/天猫相似商品,支持标题、价格、销量等信息返回。核心功能包括以图搜图、商品筛选和分页查询,具备高效性、准确性和多语言支持。开发者需注册账号、创建应用并申请权限后调用接口,适用于电商平台、比价工具等场景。
|
消息中间件 缓存 Java
RocketMQ - 消费者消费方式
RocketMQ - 消费者消费方式
627 0
|
机器学习/深度学习 自然语言处理 监控
CNN的应用场景
【10月更文挑战第23天】CNN的应用场景
969 3
|
消息中间件 存储 Java
消息队列-死信队列
消息队列-死信队列
552 0
|
JSON Dart Android开发
Flutter系列:Flutter常见问答(可用于面试)
Flutter系列:Flutter常见问答(可用于面试)
871 0