本文最后更新于 2024-03-23T16:23:39+00:00
思考:如何快速定位 IP 对应的省份地址?
举例:查询 202.102.133.13 这个 IP 地址的归属地时,在地址库中发现这个 IP 地址落在[202.102.133.0, 202.102.133.255]这个地址范围内,就可以将这个 IP 地址范围对应的归属地显示给用户了。
现在我的问题是,在庞大的地址库中逐一比对 IP 地址所在的区间,是非常耗时的。假设我们有 12 万条这样的 IP 区间与归属地的对应关系,如何快速定位出一个 IP 地址的归属地呢?
主要内容
主要将二分的变形查找
都以数据从小到大排序为前提。
变体一:查找第一个值等于给定值的元素
当数据中存在重复的值时,如何找到呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function bsearch(list, targetValue) { const len = list.length - 1 let low = 0 let high = len
while(low <= high) { const mid = Math.floor((low + high) / 2) const midValue = list[mid]
if(midValue > targetValue) { high = mid - 1 } else if(midValue < targetValue) { low = mid + 1 } else {
if(mid === 0 || list[mid - 1] !== targetValue) return mid else high = mid - 1 } }
return -1 }
|
变体二:查找最后一个值等于给定值的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function bsearch(list, targetValue) { const len = list.length - 1 let low = 0 let high = len
while(low <= high) { const mid = Math.floor((low + high) / 2) const midValue = list[mid]
if(midValue > targetValue) { high = mid - 1 } else if(midValue < targetValue) { low = mid + 1 } else {
if(mid === len || list[mid + 1] !== targetValue) return mid else low = mid + 1 } }
return -1 }
|
变体三:查找第一个大于等于给定值的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function bsearch(list, targetValue) { const len = list.length - 1 let low = 0 let high = len
while(low <= high) { const mid = Math.floor((low + high) / 2) const midValue = list[mid]
if(midValue < targetValue) { low = mid + 1 } else {
if(mid === 0 || list[mid - 1] < targetValue) return mid else high = mid - 1 } }
return -1 }
|
变体四:查找最后一个小于等于给定值的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function bsearch(list, targetValue) { const len = list.length - 1 let low = 0 let high = len
while(low <= high) { const mid = Math.floor((low + high) / 2) const midValue = list[mid]
if(midValue > targetValue) { high = mid - 1 } else {
if(mid === len || list[mid + 1] > targetValue) return mid else low = mid + 1 } }
return -1 }
|
解答思考
思考:如何快速定位 IP 对应的省份地址?
举例:查询 202.102.133.13 这个 IP 地址的归属地时,在地址库中发现这个 IP 地址落在[202.102.133.0, 202.102.133.255]这个地址范围内,就可以将这个 IP 地址范围对应的归属地显示给用户了。
现在我的问题是,在庞大的地址库中逐一比对 IP 地址所在的区间,是非常耗时的。假设我们有 12 万条这样的 IP 区间与归属地的对应关系,如何快速定位出一个 IP 地址的归属地呢?
解答:现将区间从小到大排序,可将 IP 转为 32 位整数进行排序。然后用【变体四】找到最后一个起始 IP 小于等于该 IP 的 IP 区间数据。然后检查 IP 是否在该区间内,在的话返回归属地,不在就返回未找到。
内容总结
变体的二分查找比较常用,运用的场景也多。
所以要掌握手写代码的能力
新的思考
如果有序数组是一个循环有序数组,比如 4,5,6,1,2,3。针对这种情况,如何实现一个求“值等于给定值”的二分查找算法呢?
循环有序数组简单理解为环形数组,结构为 ……3,4,5,6,1,2,3,4,5……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function circularBinarySearch(list, tValue) { const len = list.length
if(!len) return -1
let start = 0 let end = len - 1
while(start <= end) { const mid = (start + end) % len
const midValue = list[mid]
if(midValue > tValue) { end = mid - 1 } else if(midValue < tValue) { start = mid + 1 } else { return mid }
if(start > end) { start = start % len
for(let i = start; i < start + end + 1; i++) { if(list[i % len] === tValue) return i % len }
reutrn -1 }
reutrn -1 } }
|