壹 ❀ 引

在最近的工作中,有一个任务是需要修复富文本编辑器字号显示的BUG。大概情况就是,从WPS中复制不同样式的标题、正文到到项目编辑器中,发现没办法设置选中的文本为正文;而且字体字号都显示为默认的情况下,这些字体大小还表现不同。因为该富文本编辑器是基于ckeditor二次开发,所以也是看了一天的源码才成功定位到问题,最后发现WPS的字体单位使用的是印刷行业的单位,也就是pt,而不是我们熟知的px,这就导致了富文本编辑器无法识别该pt单位,从而产生了后续一系列的bug。

经过与程序原作者沟通讨论,最终确定的修复方案是将WPS粘贴过来的文本原数据进行单位换算加工。其次还有一个问题,编辑器的字号大小选择范围一般是间断的而非连续的,比如下图:

如果编辑器能展示的字号范围未能匹配到我们最终拿到的字号大小,那么编辑器就会展示字号为默认,因为匹配不到,那这样就还是会造成13px15px都显示为默认的情况,从而给了用户一种字号都是默认,但字体大小显示不同的疑问。所以我们在换算之后还多了一步操作,我们需要在编辑器的字号范围中找到与换算后的字号最接近的数字,作为最终展示字体大小,那么这就衍生出了一个问题,怎么在数组中找到与目标数字最为接近的数字。

贰 ❀ 尝试解决

归纳上面的问题,其实就是在一个数组中找到与目标数字最近接的数字,比如:

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对比,如果targetarr[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 数组中找到与目标值最接近的数字,记一次工作中关于二分查找的算法优化的更多相关文章

  1. Python实现在给定整数序列中找到和为100的所有数字组合

    摘要:  使用Python在给定整数序列中找到和为100的所有数字组合.可以学习贪婪算法及递归技巧. 难度:  初级 问题 给定一个整数序列,要求将这些整数的和尽可能拼成 100. 比如 [17, 1 ...

  2. 原生js(form)验证,可以借鉴下思路,应用到工作中

    我在工作中时常使用form验证,在目前的公司做的表单验证用的angular的form组件,对于一个有追求的前端,或者应用在移动端写个form验证,引入angular或者jquery组件等验证,难免显得 ...

  3. 旋转数组中的最小数字,剑指offer,P70 二分查找来实现O(logn)的查找

    public class MinNumberInRotatedArray { public int getMinNumInRotatedArray(int[] array) { if(array == ...

  4. 对于一个有序数组,我们通常采用二分查找的方式来定位某一元素,请编写二分查找的算法,在数组中查找指定元素。 给定一个整数数组A及它的大小n,同时给定要查找的元素val,请返回它在数组中的位置(从0开始),若不存在该元素,返回-1。若该元素出现多次,请返回第一次出现的位置。

    // ConsoleApplication10.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream& ...

  5. 记一次工作中的小BUG

    今天在调试代码的时候总是遇到一个bug,百思不得其解!先上bug图 我用的webapi 集成的swagger,错误提示是路由名称冲突,可我仔细检查了下并没有冲突的路由地址啊!于是上网查找资料,有位网友 ...

  6. leetcode:Search for a Range(数组,二分查找)

    Given a sorted array of integers, find the starting and ending position of a given target value. You ...

  7. c:找到出现次数最多的递增数字串

    如题,如何在一亿位整数组成的字符串中找到出现次数最多的递增数字串? 答案: #include <stdio.h> #include <string.h> #define MAX ...

  8. 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 ...

  9. Git-【技术干货】工作中Git的使用实践

    Git-[技术干货]工作中Git的使用实践 置顶 2019-09-17 21:02:16 web洋仔 阅读数 11444更多 分类专栏: Git   版权声明:本文为博主原创文章,遵循CC 4.0 B ...

  10. LeetCode第十六题-找出数组中三数之和最接近目标值的答案

    3Sum Closest 问题简介: 给定n个整数的数组nums和整数目标,在nums中找到三个整数,使得总和最接近目标,返回三个整数的总和,可以假设每个输入都只有一个解决方案 举例: 给定数组:nu ...

随机推荐

  1. RL 的探索策略 | Exploration for RL

    最近在草率地调研 RL 的 exploration. 这篇文章也比较草率,仅能起到辅助作用,不能代替读 review 或更精细的读 paper. 目录 0 总结写在最前面 1 主要参考资料 2 RL ...

  2. Jinfo 查看 jvm 配置及使用 Jstat 查看堆内存使用与垃圾回收

    本文为博主远传,未经允许不得转载: 1. Jinfo 查看正在运行的Java应用程序的扩展参数: 包含 JVM 参数与 java 系统参数 命令:  jinfo pid 2. 使用 jstat 查看堆 ...

  3. 问题--在C++使用strcpy等函数时发生C4996报错

    1.问题如下: C4996:'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To ...

  4. 在Windows 版的Chrome中切换标签页

    按住Ctrl+tab键之后使用page up/ page down键左右移动!

  5. SQLServer 执行计划的简单学习和与类型转换的影响

    SQLServer 执行计划的简单学习和与类型转换的影响 背景 最近一直在看SQLServer数据库 索引.存储.还有profiler的使用 并且用到了 deadlock graph 但是感觉还是不太 ...

  6. 【转帖】nginx变量使用方法详解-3

    https://www.diewufeiyang.com/post/577.html 也有一些内建变量是支持改写的,其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 ...

  7. 多模态 GPT-V 出世!36 种场景分析 ChatGPT Vision 能力,LMM 将全面替代大语言模型?

    LMM将会全面替代大语言模型?人工智能新里程碑GPT-V美国预先公测,医疗领域/OCR实践+166页GPT-V试用报告首发解读 ChatGPT Vision,亦被广泛称为GPT-V或GPT-4V,代表 ...

  8. Spring Boot集成Actuator

    一.Spring-Boot-Actuator简介 官网:https://docs.spring.io/spring-boot/docs/2.3.4.BUILD-SNAPSHOT/reference/h ...

  9. js中forEach的用法、forEach如何跳出循环、forEach与for之间的区别

    定义和用法 forEach() 调用数组的每个元素,并将元素传递给回调函数. 注意: forEach() 对于空数组是不会执行回调函数的. 用法: array.forEach(function(cur ...

  10. VictoriaMetrics 1.80版本中值得关注的新特性

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 change log请看:https://github.c ...