二分查找

使用二分查找的前提

模板

常见的二分查找应用比如猜数字游戏。

// 二分查找适用于有序的数组
// 这个一个最简单的二分查找算法,前提是数组中不存在重复元素
function binarySearch(arr, target) {if (arr && Array.isArray(arr)) {if (!arr.length) return -1let left = 0let right = arr.length - 1while (left <= right) {// let mid = Math.floor((left + right) / 2)let mid = Math.floor(left + (right - left)/2) // 防止left, right过大相加导致数值溢出if (arr[mid] === target) {return mid}if (arr[mid] > target) {right = mid - 1} else {left = mid + 1}}return -1}
}// 二分查找除了使用循环实现以外还可以使用递归实现
function bsearch(arr, target) {if (arr && Array.isArray(arr)) {return bsearchInternally(arr, 0, arr.length - 1, target)}
}function bsearchInternally(arr, left, right, target) {if (left > right) return -1let mid = Math.floor(left + (right - left) / 2)if (arr[mid] === target) {return mid}if (arr[mid] > target) {return bsearchInternally(arr, left, mid - 1, target)} else {return bsearchInternally(arr, mid + 1, right, target)}
}console.log(binarySearch([8, 11, 19, 23, 27, 33, 45, 55, 67, 98], 19)) // 2
console.log(binarySearch([8, 11, 19, 23, 27, 33, 45, 55, 67, 98], 20)) // -1console.log(binarySearch([8, 11, 19, 23, 27, 33, 45, 55, 67, 98], 33)) // 5
console.log(bsearch([8, 11, 19, 23, 27, 33, 45, 55, 67, 98], 33)) // 5

这里关于二分查找算法的时间复杂度分析也很好理解:

我们假设数据规模为n,每一次查找数据规模会缩减1/2,最坏情况下即缩减空间为0才停止。

可以看出来,这是一个等比数列。其中 n/2 =1 时,k 的值就是总共缩小的次数。而每一次 缩小操作只涉及两个数据的大小比较,所以,经过了 k 次区间缩小操作,时间复杂度就是 O(k)。通过 n/2 =1,我们可以求得 k=log n,所以时间复杂度就是 O(logn)。

总结: 二分查找算法针对的是一个有序的数据集合,查找思想类似分治思想,每次都通过跟中间数进行对比来缩小数据规模,将待查找的数据规模缩微之前的一半,直到找到目标元素,或者查找区间被缩减为0。

二分查找的应用场景

  • 首先二分查找依赖的是顺序表结构,简单说就是数组,因为二分查找需要按照下标索引随机访问元素,数组随机访问元素时间复杂度是O(1)。
  • 其次二分查找针对的是有序数据,如果数据不是有序的,那么我们首先要先排序,我们知道排序最好情况时间复杂度是O(nlogn)。如果我们针对的是一组静态数据,不需要频繁的删除,插入等操作,那么我们只需要进行一次排序,多次二分查找即可,这样排序的效率可以被均摊,性能尚能接受。如果我们的数据集合需要频繁的插入,删除操作,要想用二分查找,那么我们需要保证每次删除,插入操作后数据有序,要么在每次二分查找之前先排序,针对这样的动态数据,无论哪种方式维护成本都太高。

所以二分查找更适合于静态数据场景或者少插入,删除操作,一次排序多次查找的场景。

  • 数据量太小也不适合二分查找,如果数据量太少其实用顺序查找与二分查找效率相差无几,不过有一种例外的情况,如果数据之间的比较操作非常耗时,不管数据量大小都推荐使用二分查找。因为我们需要尽可能的减少比较次数,这样可以提高性能。
  • 数据量太多也不适合二分查找算法,因为二分查找底层依赖于数组这样的数据结构,而数组在内存中是连续存储的,比较耗内存。

变形的二分查找

我们前面学习的都是非常简单的二分查找,在不存在重复元素的有序数组中,查找值等于给定值的元素。

- 查找第一个值等于给定值的元素

const binaryFirst = (arr, target) => {if (Array.isArray(arr)) {const len = arr.lengthif (!len) return -1let low = 0let high = len - 1while (low <= high) {const mid = Math.floor((low + high) / 2)if (arr[mid] < target) {low = mid + 1} else if (arr[mid] > target) {high = mid - 1} else {// 需要判断当前值是否是第一个相等的值 if (mid === 0 || arr[mid - 1] < target) return midhigh = mid - 1}}}return -1
};// test case
console.log(binaryFirst([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 8)) // 5
console.log(binaryFirst([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 11)) // 8
console.log(binaryFirst([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 19)) // -1
console.log(binaryFirst([1, 4, 4, 5, 6, 8, 8, 8, 11, 18], 4)) // 1
console.log(binaryFirst([1], 1)) // 0
console.log(binaryFirst([1, 1], 1)) // 0
console.log(binaryFirst([1], 0)) // -1

这里主要难点在于判断当前值是否是第一个相等的值。

1. 如果mid === 0 说明这个元素是数组第一个元素 那么肯定是第一个

2. 如果 mid 不等于 0,但 arr[mid] 的前一个元素 arr[mid-1] 不等于value,那也说明 arr[mid] 就是我们要找的第一个值等于给定值的元素。

3. 如果经过检查之后发现 a[mid] 前面的一个元素 a[mid-1] 也等于 value,那说明此时的a[mid] 肯定不是我们要查找的第一个值等于给定值的元素。那我们就更新 high=mid-1,因为要找的元素肯定出现在 [low, mid-1] 之间。

  • 查找最后一个值等于给定值的元素
const binaryLast = (arr, target) => {if (Array.isArray(arr)) {const len = arr.lengthif (!len) return -1let low = 0let high = len - 1while (low <= high) {const mid = Math.floor((low + high) / 2)if (arr[mid] < target) {low = mid + 1} else if (arr[mid] > target) {high = mid - 1} else {if ((mid === len - 1) || (arr[mid + 1] > target)) return midlow = mid + 1}}}return -1
};// test case
console.log(binaryLast([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 8)) // 7
console.log(binaryLast([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 11)) // 8
console.log(binaryLast([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 19)) // -1
console.log(binaryLast([1, 4, 4, 5, 6, 8, 8, 8, 11, 18], 4)) // 2
console.log(binaryLast([1], 1)) // 0
console.log(binaryLast([1, 1], 1)) // 0
console.log(binaryLast([1], 0)) // -1
console.log(binaryLast([1, 3, 4, 5, 6, 8, 8, 8, 11, 18, 18], 18)) // 10

  1. 如果mid === len - 1,说明已经到了元素已经到了数组末尾,肯定是最后一个相同元素了。
  2. 如果arr[mid +1]> target,说明已经是最后一个相同元素了。
  3. 如果还不是最后一个元素,那么继续往后扫描,最后一个相同元素范围肯定是[mid + 1, high]。
  • 查找第一个大于等于给定值的元素
const binaryFindFistBig = (arr, target) => {if (Array.isArray(arr)) {const len = arr.lengthif (!len) return -1let low = 0let high = len - 1while (low <= high) {const mid = Math.floor((low + high) / 2)if (arr[mid] >= target) {// 如果mid === 0 说明数组第一个元素满足情况// 如果arr[mid - 1] < target 说明如果当前元素前一个元素比目标值小那么当前元素肯定第一个满足条件的if ((mid === 0) || arr[mid - 1] < target) return midhigh = mid - 1} else {low = mid + 1}}}return -1
}// test case
console.log(binaryFindFistBig([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 7)) // 5
console.log(binaryFindFistBig([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 8)) // 5
console.log(binaryFindFistBig([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 19)) // -1
console.log(binaryFindFistBig([1, 4, 4, 5, 6, 8, 8, 8, 11, 18], 4)) // 1
console.log(binaryFindFistBig([1], 1)) // 0
console.log(binaryFindFistBig([1, 1], 1)) // 0
console.log(binaryFindFistBig([1], 0)) // 0
console.log(binaryFindFistBig([1, 3, 4, 5, 6, 8, 8, 8, 11, 18, 18], 18)) // 9

  • 查找最后一个小于等于给定值的元素
const binaryFindLastBig = (arr, target) => {if (Array.isArray(arr)) {const len = arr.lengthif (!len) return -1let low = 0let high = len - 1while (low <= high) {const mid = Math.floor((low + high) / 2)if (arr[mid] > target) {high = mid - 1} else {if ((mid === len - 1) || arr[mid + 1] > target) return midlow = mid + 1}}}return -1
}// test case
console.log(binaryFindLastBig([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 7)) // 4
console.log(binaryFindLastBig([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 8)) // 7
console.log(binaryFindLastBig([1, 3, 4, 5, 6, 8, 8, 8, 11, 18], 19)) // 9
console.log(binaryFindLastBig([1, 4, 4, 5, 6, 8, 8, 8, 11, 18], 4)) // 2
console.log(binaryFindLastBig([1], 1)) // 0
console.log(binaryFindLastBig([1, 1], 1)) // 1
console.log(binaryFindLastBig([1], 0)) // -1
console.log(binaryFindLastBig([1, 3, 4, 5, 6, 8, 8, 8, 11, 18, 18], 18)) // 10

java查找一个数等于一组数中哪些数字相加的和_快速入门二分查找相关推荐

  1. 查找一个数等于一组数中某些数相加的和

    实现需求: 查找一个数等于一组数中哪些数相加的和 例如: 从1, 2, 3, 4, 5, 6, 7 中找出和为5的数, 即2, 3 public class AccumulationUtil {pri ...

  2. 一组数中寻找加和最接近某个值的组合 JAVA实现

    一组数中寻找加和最接近某个值的组合 JAVA实现 最近帮人做了一个题,就是在一组数中寻找一个子集合,使得这个子集合的数的加和最接近给定的某个值,此外,这些数的类型是浮点数.我第一想到是用01背包问题去 ...

  3. c语言中求大于的函数,c语言编写函数,求一组数中大于平均值的数的个数.

    C语言编写程序 给定一组数,求大于0,等于0,小于0的数据个数 #define N 10 main(){int num1=0,num2=0,num3=0,i;for(i=0;i 一道C语言题目:求一组 ...

  4. C语言实现查找一组数中的最大和最小值

    查找一组数中的最大.最小值 /*** 查找一组数中的最大数* @param nums 数组指针* @param step sizeof(type)* @param n 该组数中有几个数* @retur ...

  5. matlab产生一组均为一的矩阵,在matlab中如何从一组数中得到随机数组成一个n*n的矩阵...

    导航:网站首页 > 在matlab中如何从一组数中得到随机数组成一个n*n的矩阵 时间:2019-3-15 在matlab中如何从一组数中得到随机数组成一个n*n的矩阵 从1,2,3,4,5,6 ...

  6. Java判断一个数是不是快乐数

    Java判断一个数是不是快乐数 快乐数的定义: 快乐数(happy number)有以下的特性: 在给定的进位制下,该数字所有数位(digits)的平方和,得到的新数再次求所有数位的平方和,如此重复进 ...

  7. 在一组数中寻找加和最接近某个值的数组合

    在一组数中寻找加和最接近某个值的数组合 今天碰到个小问题,就是需要在一组数中,找到加和数最接近某个值的一系列数. 比如: [8.05, 6.98, 6.19, 5, 22.96,4.71,4.74,4 ...

  8. 求一组数中的最大值和最小值

    Description 给定一组数,求它们中的最大值和最小值. Input 首先是一个整数n,表示这组数据的个数. 然后是n个整数,中间用空格分隔. Output 输出最大值和最小值,中间用" ...

  9. C# 利用linq获取一组数中几个连续数中最大的一个数字

    提供一个数组,其中部分数据是连续的,比如0~20的一组数据,其中少了一个数,假设少了11,那么连续中最大的数字就是10, 假如数组不是从0开始,假设是3~20,中间少了1个数,假如少了8,那么连续中最 ...

最新文章

  1. js中继承的几种用法总结(apply,call,prototype)
  2. Spring + JDBC + Struts联合开发(实现单表的CRUD)
  3. MySQL索引类型 btree索引和hash索引的区别
  4. 父盒子高度为子盒子总高度自动撑满 height: fit-content; //设置内容高度
  5. OpenCV-数字图像处理之直方图均衡化
  6. 解决迭代器调用next方法时报错:StopIteration
  7. 宾大最新《图神经网络》课程,附视频与课件
  8. Java程序员必看的 14 本 Java 书籍!
  9. [转载] 怎样彻底卸载anaconda?
  10. 核桃编程Delta Lake实时数仓应用实践
  11. 计算机应用职业生涯规划,计算机应用技术职业规划书|计算机应用专业个人职业规划...
  12. Android怎么导入夜神模拟器,电脑文件怎么传到夜神android模拟器 文件上传夜神模拟器...
  13. msi2lmp不能用,mpi不能连接主机,解决办法看这里
  14. hibernate 二级缓存 @cache注解
  15. 用Python玩转数据数据处理相关小例编程题
  16. 工业机器人协作控制研究
  17. 华为S5系列交换机配置
  18. Discus论坛System Error界面修改标语
  19. 胶囊网络中挤压函数Squash的实现(tensorflow和pytorch)
  20. mysql path妙用

热门文章

  1. 通过浏览器启动php cli,Cron作业PHP脚本失败但脚本将通过CLI或浏览器运行
  2. php+oracle新增数据类型,Oracle 修改某个字段的数据类型三种方式
  3. [转]MySQL索引背后的数据结构及算法原理
  4. 【NOI2019模拟2019.7.4】朝夕相处 (动态规划+BM)
  5. Redis管理各类型存储数据命令
  6. python中生成器的两段代码
  7. error_reporting()函数
  8. haproxy 配置
  9. 『转』图解硬件特性!
  10. php slug,PHP函数使slug(URL字符串)