JS 数组中找到与目标值最接近的数字,记一次工作中关于二分查找的算法优化

壹 ❀ 引
在最近的工作中,有一个任务是需要修复富文本编辑器字号显示的BUG。大概情况就是,从WPS中复制不同样式的标题、正文到到项目编辑器中,发现没办法设置选中的文本为正文;而且字体字号都显示为默认的情况下,这些字体大小还表现不同。因为该富文本编辑器是基于ckeditor二次开发,所以也是看了一天的源码才成功定位到问题,最后发现WPS的字体单位使用的是印刷行业的单位,也就是pt,而不是我们熟知的px,这就导致了富文本编辑器无法识别该pt单位,从而产生了后续一系列的bug。
经过与程序原作者沟通讨论,最终确定的修复方案是将WPS粘贴过来的文本原数据进行单位换算加工。其次还有一个问题,编辑器的字号大小选择范围一般是间断的而非连续的,比如下图:

如果编辑器能展示的字号范围未能匹配到我们最终拿到的字号大小,那么编辑器就会展示字号为默认,因为匹配不到,那这样就还是会造成13px与15px都显示为默认的情况,从而给了用户一种字号都是默认,但字体大小显示不同的疑问。所以我们在换算之后还多了一步操作,我们需要在编辑器的字号范围中找到与换算后的字号最接近的数字,作为最终展示字体大小,那么这就衍生出了一个问题,怎么在数组中找到与目标数字最为接近的数字。
贰 ❀ 尝试解决
归纳上面的问题,其实就是在一个数组中找到与目标数字最近接的数字,比如:
const arr = [1,3,5,6,10];
const target = 7;
// 最终找到6,因为7和6的差最小
const arr = [1,3,5,6,10];
const target = 3;
// 最终找到3,还有比相等的数字更为接近的?
只要简单分析上述两个例子,我们要找的值其实就是与目标值target的差最小的一个数,考虑到存在差为负数的情况,所以这个差应该是一个绝对值,可用abs()转换。那么我们来尝试实现它:
const arr = [1, 2, 6, 10, 9];
const target = 3;
const findNearestNumber = (arr, target) => {
// 我们假设最近接的就是数组第第一个数字
let result = arr[0];
for (let i = 1, len = arr.length; i < len; i++) {
if (Math.abs(arr[i] - target) < Math.abs(result - target)) {
result = arr[i];
};
};
return result;
};
console.log(findNearestNumber(arr, target));//2
让我们回顾上述代码,一开始,我们需要提供一个初始值,并拿下一个值参与比较之后决定返回两者中的某一个值,然后继续参与后续遍历,仔细一想,这不就是在做reduce的操作吗,所以我们简化下代码,变成了这样:
const findNearestNumber = (arr, target) => {
return arr.reduce((pre, curr) => {
return Math.abs(pre - target) > Math.abs(curr - target) ? curr : pre;
})
};
以上的实现适用于有序以及无序数组,考虑无序的情况,我们最差的情况就是完整遍历一遍,最终发现最后一个数字才是我们想要的,所以站在时间复杂度上来说就是O(n)。
不过我们现在的需求有点不同,很明显字号大小是从小到大排列的,比如这样一个数组:
const fontSize = [8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72];
这是一个有序数组,就像肌肉记忆一样,看到有序数组,谈到遍历我们首先想到的就应该是二分查找,其实上篇文章我也提过公司code review会比较严格,对于性能要求比较高。发版那天本来就够忙了,我可不想在提测通过后,因为review没过,又得修改代码再次走测试流程,非常费时,所以保险起见,这里就直接考虑二分查找来做了。
我们声明左右两个指针,分别指向数组的第0位索引和最后一位索引,然后求出中间索引,拿中间索引所对应的数字与target对比,如果target比arr[mid]大,那说明target肯定在右边范围,这时候只要修改左指针为mid即可,反之修改右指针。

因为目标值不存在于数组中,所以最终我们得保证左右指针指到相邻的两个数字上,大致实现如下:
const findNearesttargetber = (arr, target) => {
let mid;
let l = 0;
let r = arr.length - 1;
// 保证指针最终停留在相邻的两个数,所以这里是判断是否大于1
while (r - l > 1) {
mid = Math.floor((l + r) / 2);
// 如果目标数比中间小,所以范围在左边
if (target < arr[mid]) {
r = mid;
} else {
l = mid;
};
};
// 最后比较这两个数字的绝对差大小即可。
return Math.abs(target - arr[l]) <= Math.abs(target - arr[r]) ? arr[l] : arr[r];
}
由于二分查找每次都能排除掉一半的可能,所以时间复杂度为O(logn),当然由于当前数组并不是很庞大,执行上其实也并不太大区别,只是尽可能去这么写了。那么关于本文就到这里了。
JS 数组中找到与目标值最接近的数字,记一次工作中关于二分查找的算法优化的更多相关文章
- Python实现在给定整数序列中找到和为100的所有数字组合
摘要: 使用Python在给定整数序列中找到和为100的所有数字组合.可以学习贪婪算法及递归技巧. 难度: 初级 问题 给定一个整数序列,要求将这些整数的和尽可能拼成 100. 比如 [17, 1 ...
- 原生js(form)验证,可以借鉴下思路,应用到工作中
我在工作中时常使用form验证,在目前的公司做的表单验证用的angular的form组件,对于一个有追求的前端,或者应用在移动端写个form验证,引入angular或者jquery组件等验证,难免显得 ...
- 旋转数组中的最小数字,剑指offer,P70 二分查找来实现O(logn)的查找
public class MinNumberInRotatedArray { public int getMinNumInRotatedArray(int[] array) { if(array == ...
- 对于一个有序数组,我们通常采用二分查找的方式来定位某一元素,请编写二分查找的算法,在数组中查找指定元素。 给定一个整数数组A及它的大小n,同时给定要查找的元素val,请返回它在数组中的位置(从0开始),若不存在该元素,返回-1。若该元素出现多次,请返回第一次出现的位置。
// ConsoleApplication10.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream& ...
- 记一次工作中的小BUG
今天在调试代码的时候总是遇到一个bug,百思不得其解!先上bug图 我用的webapi 集成的swagger,错误提示是路由名称冲突,可我仔细检查了下并没有冲突的路由地址啊!于是上网查找资料,有位网友 ...
- leetcode:Search for a Range(数组,二分查找)
Given a sorted array of integers, find the starting and ending position of a given target value. You ...
- c:找到出现次数最多的递增数字串
如题,如何在一亿位整数组成的字符串中找到出现次数最多的递增数字串? 答案: #include <stdio.h> #include <string.h> #define MAX ...
- linq介绍及工作中应用两例——左联与内联,linq循环方法
目录 1 linq介绍 1.1 linq产生背景 1.2 linq使用范围 1.3 linq核心程序集 1.4 linq架构图 1.5 linq使用形式对比 1.5.1 linq To Objects ...
- Git-【技术干货】工作中Git的使用实践
Git-[技术干货]工作中Git的使用实践 置顶 2019-09-17 21:02:16 web洋仔 阅读数 11444更多 分类专栏: Git 版权声明:本文为博主原创文章,遵循CC 4.0 B ...
- LeetCode第十六题-找出数组中三数之和最接近目标值的答案
3Sum Closest 问题简介: 给定n个整数的数组nums和整数目标,在nums中找到三个整数,使得总和最接近目标,返回三个整数的总和,可以假设每个输入都只有一个解决方案 举例: 给定数组:nu ...
随机推荐
- java进阶(28)--Map集合
一.Map简介: 1.Map与collection没有继承关系 2.Map集合以key与value的方式存储数据 二.常用方法: 1.void clear():清空Map集合
- 聊一聊数字孪生与3D可视化
前言 在当代科技发展的背景下,数字孪生和3D可视化技术逐渐成为各行业的关键工具和解决方案.数字孪生是一种将实物事物与数字模型相结合的概念,通过将物理世界和数字世界实时连接,创造出一个对实体进行虚拟建模 ...
- WPF|黑暗模式的钱包支付仪表盘界面设计
阅读目录 效果展示 准备 简单说明 + 源码 结尾(视频及源码仓库) 1. 效果展示 欣赏效果: 2. 准备 创建一个WPF工程,比如站长使用 .NET 7 创建名为 WalletPayment 的W ...
- 例2.9 建立一个带头结点的线性链表,用以存放输人的二进制数,链表中每个结点的data域存放一个二进制位。并在此链表上实现对二进制数加1的运算。
1.题目 例2.9建立一个带头结点的线性链表,用以存放输人的二进制数,链表中每个结点的data域存放一个二进制位.并在此链表上实现对二进制数加1的运算. 2.算法分析 3.代码 /* 二进制加1 */ ...
- [SpringMVC] - 解决Jackson中文乱码 : springmvc-servlet.xml
<!-- 指定响应体返回类型和编码 , 解决乱码????的问题 --> <mvc:annotation-driven> <mvc:message-converters r ...
- Advanced .Net Debugging 1:你必须知道的调试工具
一.简介 我曾看到过许多开发人员使用错误的工具来分析问题,更有甚者,有些人连任何工具都没有使用.他们采取的分析方法通常包括:输出更多的调试信息,或者做一些临时性的代码审查.这里的临时性是指,通过 ...
- [转帖]Nginx - 根据IP分配不同的访问后端
https://www.cnblogs.com/hukey/p/11868017.html 1. 需求分析 为了在线上环境提供测试分支,规定将某IP转发到测试程序地址.如果是 ngx 直接对外,采用 ...
- [转帖]Linux系统管理-crond、chkconfig、systemd、unit、target
https://cloud.tencent.com/developer/article/1409845 10.23 linux任务计划cron crontab命令被用来提交和管理用户的需要周期性执行的 ...
- [转帖]12.24.2 DECIMAL Data Type Characteristics
https://dev.mysql.com/doc/refman/8.0/en/fixed-point-types.html This section discusses the characteri ...
- [转帖]使用GCC编译器实测兆芯KX-U6780A的SPEC CPU2006成绩
https://baijiahao.baidu.com/s?id=1722775453962904303 兆芯KX-U6780A是一款8核2.7GHz的使用x86/AMD64指令集(架构)的国产C ...