一、应用场景
项目场景描述:数据库中共有45万条记录,保存了起始IP和终止IP,以及在此IP段对应的地理位置信息,如 114.104.085.000—114.104.085.255 中国 安徽省 六安市。
项目需求:
- 输入一条IP地址,可以获得该IP所对应的地理位置信息;
- 空间复杂度暂时不考虑,要求查找时间小于2ms。
二、算法设计
在海量数据检索处理中,哈希算法是常用的检索算法,由于数据库中存储的是一个IP的范围,因此不能将整个数据集存储到一个HashMap中,所以取IP的前三位作为key值。将IP由字符串转化为Long型,如114.104.085.000转换为114104085000。整个数据集做Hash以后,再将其保存到一个按照起始IP排好序的TreeMap中,其Key值为起始IP,value则保存了地理位置信息。同时将TreeMap中的KeySet转化为List,该List是排好序的,保存该List是因为要利用二分查找来寻找TreeMap中对应的起始IP。二分查找算法,在此处是一个变体,用来寻找小于等于起始IP的最大值。
三、二分查找算法——查找小于key值的最大值与大于key值得最小值
先将二分查找和二分查找算法变体的代码贴出:
public class BinarySearch {
//基本二分查找
public static int binarySearch(int[] array, int key){
int low = 0;
int high = array.length - 1;
while(low <= high){
int mid = (low + high) >> 1;
if(array[mid] == key)
return mid;
else if(array[mid] > key){
high = mid - 1;
}
else{
low = mid + 1;
}
}
return -1;
}
//查找小于key的最大值
public static int binaryUpBound(int[] array, int key){
int low = 0;
int high = array.length - 1;
while(low < high){
int mid = (low + high + 1) >> 1;
if(array[mid] > key){
high = mid - 1;
}
else{
low = mid;
}
}
if(array[low] <= key)
return low;
else
return -1;
}
//查找大于key的最小值
public static int binaryLowBound(int[] array, int key){
int low = 0;
int high = array.length - 1;
while(low < high){
int mid = (low + high) >> 1;
if(array[mid] < key){
low = mid + 1;
}
else{
high = mid;
}
}
if(array[high] >= key)
return high;
else
return -1;
}
}
其中需要注意的地方有一下几个方面:
- 二分查找算法的变体中,循环退出条件为low<high,如果依旧写为low<=high,则会导致循环无法退出;
- 查找小于key的最大值,取mid值时采用(low+high+1)/2,因为如果low和high值相差1时,如果仍写为(low+high)/2则会导致死循环,循环退出后,下限可能是最终结果;
- 查找大于key的最小值,取mid值使用(low+high)/2,这与low=mid+1相对应,不会导致死循环,循环退出后,上限可能是结果。
四、结论
项目中使用了Hash和二分查找的思想,并且做了缓存,经测试,最终查找一条IP的耗时为0.35ms,满足了需求。