Nodejs 求js最高效算法:范围查找
Nodejs 求js最高效算法:范围查找
有这么个需求: 比如,一条长街,门牌号从0 - 1000 0-100,卖鱼 101-203,卖菜 204-500,卖肉 501-1000,卖鸡蛋
各个分段之间是连续的: [0-100],[101-203],[204-500],[501-1000]
然后随便给出一个0-1000的门牌号,如何最快速的知道这个号是卖什么的? 这个算法是瓶颈,要求速度一定要极致,求各位大神指点。
Node.js 中高效的范围查找算法
需求描述
假设有一条长街,门牌号从0到1000,每个区间售卖不同的商品:
- 0-100:卖鱼
- 101-203:卖菜
- 204-500:卖肉
- 501-1000:卖鸡蛋
我们需要根据给定的门牌号(例如:75),找出该门牌号对应的售卖商品。
解决方案
为了实现高效的范围查找,我们可以使用二分查找算法。二分查找适用于有序数组,能够在对数时间内找到目标值的位置。在这个场景中,我们首先需要将区间转换为一个有序数组。
const intervals = [
{ start: 0, end: 100, product: '鱼' },
{ start: 101, end: 203, product: '菜' },
{ start: 204, end: 500, product: '肉' },
{ start: 501, end: 1000, product: '鸡蛋' }
];
function findProductByHouseNumber(houseNumber) {
let left = 0;
let right = intervals.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (intervals[mid].start <= houseNumber && houseNumber <= intervals[mid].end) {
return intervals[mid].product;
}
if (houseNumber < intervals[mid].start) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return '未找到';
}
// 示例调用
console.log(findProductByHouseNumber(75)); // 输出: 鱼
解释
- 数据结构:我们将每个区间的起始和结束位置以及对应的售卖商品存储在一个对象数组中。
- 二分查找:我们定义了
findProductByHouseNumber
函数来实现二分查找。函数接收一个门牌号作为参数。 - 查找过程:
- 初始化
left
和right
指针分别指向数组的首尾。 - 在
left
小于等于right
的情况下进行循环。 - 计算中间索引
mid
。 - 如果门牌号位于当前区间的范围内,则返回对应的售卖商品。
- 如果门牌号小于当前区间的起始位置,则调整
right
指针。 - 否则,调整
left
指针。
- 初始化
- 返回结果:如果在所有区间中都没有找到匹配项,则返回 “未找到”。
这种方法的时间复杂度为 O(log n),非常适合大规模数据的查找。
实际需求里面,“长街”总长度在40亿左右,大约分为50万组。
每组不定长是吧?
50万组么二分法么。查一个门牌最多19次就能查出来了么。
以门牌号首为界限存入数组也就50w的数组。然后对这个数组进行二分即可。
嗯,而且长度还不太均匀,有的很大,有的很小。。。 …
谢谢指点!不知道数据库里面,类似的算法是不是也是这么实现的?
如果是数据库,建个索引就可以了
准备直接读内存里面处理,数据库太重了,呵呵~~
算法什麼的google一下就好了 之後寫c++ addons 如果性能覺得還不夠,就寫c++ addons的時候搜索算法用匯編(彙編)寫
没必要汇编吧,这个简单的需求时间复杂度是o(logn)的😂
50W个都放到内存里也没多大吧
基础运算,1+1啊 a>b啊,node和c++差多少?
//门牌号 var numbers = [0,1,2,3,4,5,6…]; //卖什么的 var types = [ { begin : 0, end : 120, type : ‘卖鱼的’ }, { begin : 121, end : 680, type : ‘卖肉的’ }, { begin : 681, end : 990, type : ‘卖X的’ }, { begin : 991, end : 1300, type : ‘卖Y的’ }, … ] //桥梁 var bridges = [ [0, 1, 2, 3]//这里的意思是0-1000这个段中包含了types中的0,1,2,3这四个段 [3…] … ];
比如这个时候给一个数x, 那么 就可以这样了 var idx = Math.floor(x / 1000); bridges[idx].forEach(function(el){ if (x >= el.begin && x<= el.end) { console.log(‘x=’ + el.type); return false; } });
分段倒也可以,而且可以分段+二分。
hash table
直接二分好了,没必要预处理bridges数组,极端情况下每次forEach也要查1000次,没二分效率高。
用线段树,见百度百科。比二分好
我提个醒,v8内存限制只能用1.4G左右
不是可以设置吗?
线段树是不错 -。 - 对于批量更改是个不错的选择。
感谢楼上各位,受益匪浅。等最后对比一下,算法确定了,再和大家汇报。
最好是建索引,索引的算法就是二分法
40亿数据量的原始表不适合用于查询待查门牌号是卖什么的。 因为号段连续,就可以另建门牌号段表,50w记录那个。就是 xujun52011说的var types = [{begin:x,end:y,type:z},…] 你不是放在数据库里面吗?那如果要查门牌号为xxx的是卖什么,就对types的begin建立索引, 然后选择begin小于等于xxx的号段记录中最大的那条。 select top 1 type from types where begin<=xxx order by begin desc 50万记录加单键有序索引,速度应该没问题吧
要解决这个问题,可以使用二分查找算法来提高查找效率。因为区间是有序且不重叠的,二分查找能够在对数时间内找到目标区间。
以下是一个简单的实现示例:
示例代码
const streetRanges = [
{ start: 0, end: 100, product: '鱼' },
{ start: 101, end: 203, product: '菜' },
{ start: 204, end: 500, product: '肉' },
{ start: 501, end: 1000, product: '鸡蛋' }
];
function findProductByHouseNumber(houseNumber) {
let left = 0;
let right = streetRanges.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (streetRanges[mid].start <= houseNumber && houseNumber <= streetRanges[mid].end) {
return streetRanges[mid].product;
} else if (houseNumber < streetRanges[mid].start) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return '未知';
}
// 测试示例
console.log(findProductByHouseNumber(50)); // 输出: 鱼
console.log(findProductByHouseNumber(150)); // 输出: 菜
console.log(findProductByHouseNumber(600)); // 输出: 肉
console.log(findProductByHouseNumber(900)); // 输出: 鸡蛋
解释
- 数据结构:我们用一个数组
streetRanges
存储每个区间的起点、终点和对应的售卖商品。 - 二分查找函数:
findProductByHouseNumber
函数实现了二分查找算法。通过比较门牌号与区间的起点和终点,逐步缩小查找范围,直到找到对应的区间或确认不在任何区间内。 - 时间复杂度:二分查找的时间复杂度为 O(log n),其中 n 是区间的数量(在这个例子中为 4)。这比线性搜索(O(n))快得多。
通过这种方式,你可以高效地找到给定门牌号对应的售卖商品。