点击“小詹学Python”,选择“置顶”公众号

重磅干货,第一时间送达

本文转载自一个不甘平凡的码农

这篇文章主要深入数据结构与算法在解决实际问题怎么运用和分析的,对于 IP 对属地查找本身有 API 接口,那这篇文章主要对原理内部查询过程实现做详细解析,体会怎么将数据结构和算法解决实际的问题。

今天主要模拟一下怎么在 20 万数据中定位一个 IP 地址的归属地,不知道大家有没有用过百度搜索过 IP 地址的归属地。当我们在百度输入 IP 地址时,就会出现这个 IP 地址的归属地。

或者有一些 IP 归属地的查询工具也可以迅速的查找到 IP 归属地。

IP 地址数据那么庞大,它是怎么在短短不到一秒时间查找出 IP 地址的归属地呢?随后我带着疑问模拟了在 20 万条数据中快速查找一个 IP 地址的归属地。

问题分析

---------------------------------------

我们知道每个 IP 由两部分组成的,分别是网络地址和主机地址。而且每个 IP 地址是随机动态分配的,所以说,每个地区的 IP 地址的前多少位代表哪个地区,后多少位代表地区中的局域网。每个所以划定了 IP 范围,每个代表不同的归属地。

1[112.222.133.0, 112.222.133.255]  山东潍坊市2[112.222.135.0, 112.222.136.255]  山东烟台市 3[112.222.156.34, 112.222.157.255] 山东青岛市4[112.222.48.0, 112.222.48.255]  北京朝阳区5[112.222.49.15, 112.222.51.251] 福建省福州6[112.222.56.0, 112.222.56.255]  广东省深圳市

我们逐渐的将问题转化为了数据分析问题,也就是说,我们怎么查找一个 IP 地址所属的范围从而得出 IP 归属地呢?我们可能会想到用快速增删改查的数据结构和算法,平衡树、散列表、跳表、基于数组的二分查找等。

IP 地址的区间是连续的,可能先考虑到用一下二分查找,但是二分查找是有前提条件的:

1、二分查找是基于顺序数组的,运用的数组在时间复杂度为 (1) 的时间内随机快速访问数据的特性。

2、二分查找它必须是有序数据,而且不能频繁的进行动态插入和删除数据,适合一次排序,多次查找的情况回到我们问题符合要求。

通过两个二分查找的条件继续进行问题的分析,那么问题又来了,二分查找是快速的查找一个数据是否存在一组数据中,而且效率极高,1000亿查找一个数据只需 36 次查找。但是我们的要解决的问题是在区间查找。

二分查找的扩展

---------------------------------------

别着急,二分查找还可能有重复的数据,怎么解决?所以二分查找会延伸到查找重复数据的第一个数据或最后一个数据,都可以通过二分查找的算法进行改进的。

如果我们想要查找的 IP 地址在某一区间内,我们能不能转化为查找最后一个小于等于某一个区间的起始值。举个简单例子:有一下区间[1,5]、[6,10]、[11,15]、[16、20],比如 IP 为 9 ,每个区间的起始值分别为 1、6、11、16,也就是说 9 在这组区间起始值中,最后一个小于等于 9 的值,也就是 6 ,然后我们拿 9 去区间[ 6,10] 去查找是否存在该 IP ,如果存在,我们就输出该区间对应的 IP 归属地。

解决方案

---------------------------------------

问题已经分析完成了,下一步开始将问题转换为数据结构与算法的形式来解决。如果你真认为问题分析完成只剩下写代码了,你会接连的遇到棘手的问题。为了能够让大家更能体会到实际问题的复杂性,我会采用分步式递进最终的解决方法。

问题一:当下手开始写代码时,你会发现 IP 地址并不是像上述我们用到的整数,那我们怎么办呢?

※ 解决:你会想能不能将 IP 转化为整数来计算,这里我用 js 来转化。

1    //将 IP 地址转化为整数2    const ipInt = (ip) =>{3        //IP转成整型  4        var num = 0;  5        ip = ip.split(".");  6        num = Number(ip[0]) * 256 * 256 * 256 + Number(ip[1]) * 256 * 256 + Number(ip[2]) * 256 + Number(ip[3]);  7        num = num >>> 0;  8        return num;  9    }

问题二:IP 地址实际上是动态生成的,怎么来进行模拟那么多随机的 IP 地址呢?

※ 解决:最大的 IP 是 255.255.255.255 转化成整数为 4294967295。也就是 40 亿,那我们用随机函数在 40 亿的范围内随机生成 20 万个的 IP 地址。

1    let i = 0;2    const arrIp = [];3    //随机生成 200000 条 IP 数据4    while(i < 10000){5        const number = Math.floor(Math.random()*10000000);6        arrIp.push(number);7        i++;8    }

问题三:随机生成的 IP 地址是无序的,我们要进行排序,那么排序的方式有很多,冒泡、归并、快排、堆排序等,选择哪一种呢?

※ 解决:对于在 20 万的 IP 查询一个 IP 的归属地,我用 js 在浏览器中实现的,想到存储空间有限,所以排序空间复杂度不能太高,查询效率又不能太慢。快排的可以实现空间复杂度为 O(1) 排序,而且排序效率复杂度为 O(nlog2n) 。

 1    //对 20 万条数据进行快速排序 2    // 参数一(arrIP):要排序的数组IP 参数二(start):指向起始指针 参数三(end):指向末尾指针 3    const quickSort = (arr,startIndex,endIndex) =>{ 4        //递归终止条件 5        if(startIndex < endIndex){ 6            //一般选择最后一个元素为区分点(下标索引) 7            let pivot =  endIndex; 8            //获取一组数据区分后的大于 pivot 点最后元素的索引 9            let partitionIndex = partition(arr,pivot,startIndex,endIndex);10            //进行递归11            quickSort(arr,startIndex,partitionIndex-1);12            quickSort(arr,partitionIndex+1,endIndex);13        }14    }1516    // 获取排好序的区分点 Index17    const partition = (arr,pivot,startIndex,endIndex) =>{18        //获取到该区分点的值19        let pivotVal = arr[pivot];20        //永远指向第一个大于 pivot 的值21        let swapIndex = startIndex;22        //进行筛选23        // i 为遍历数据指针24        for(let i = startIndex; i < endIndex; i++){25            if(arr[i] < pivotVal){26                swap(arr,i,swapIndex);27                swapIndex++;28            }29        }30        //将大于 pivot 的值和小于 pivot 的值中间点和 pivot 的值交换31        swap(arr,swapIndex,pivot)32        //返回区分点的索引33        return swapIndex;34    }3536    //交换37    const swap = (arr,i,j) =>{38        let temp = arr[i];39        arr[i] = arr[j];40        arr[j] = temp;41    }

问题四: 因为我们要做的是查询某 IP 在哪一区间,而不是查找该 IP 地址,所以要对二分查找代码进行改进,让其转化为小于等于某区间的起始位置。

※ 解决:

 1   //对 20 万数据匹配IP对属地(二分查找) 2    const findIpAddress = (arr,value) =>{ 3        //声明两个指针 4        let low = 0; 5        let high = arr.length - 1; 6 7        while(low <= high){ 8            //取中间值 9            let mid = Math.floor((low + (high - low))/2);10            //判断中间值11            if(arr[mid] <= value){12                //进一步判断是否是小于 IP 区间的终点值[改进]13                if(mid == arr.length - 1 || arr[mid + 1] > value){14                    return mid;15                }else{16                    low = mid + 1;17                }18            }else{19                high = mid - 1;20            }21        }22        return false;23    }

IP 区间归属地我们自己设置几个简单的区间模拟一下,但是实际中很多的 IP 地址归属地划分的很精细的,所以我们在这不多陈述。

代码我们都做好了,我在这用前端做了一的简单的交互页面,我们来模拟一下,你会发现,当我们划分区间后,数据并没有 20 万,因为我们只记录区间的起始值查找就可以了,20 万数据实际大约也就是十几万甚至小于这个值。

我们可以设想一下如果把全球的数据存储到浏览器中会发生什么,所以小鹿随机生成了 50 亿的数据,来进行排序二分查找,你猜发生了什么情况?

浏览器只在呼呼的转圈,并不显示什么,好吧,作为一个前端开发者,存储那么多的数据来进行操作内存溢出了。如果你是一名后台开发者,可以尝试着用后台语言实现一下,看看能不能数据量大时,能不能再进行查找了?

通过上边的测试,小鹿从中又得出两个二分查找的适用条件:

1数据量不能太大,数组在内存中需要连续的内存空间,像 java 语言,在内存空间紧张的情况下,二分查找就不适用了。但是 js 中的数组并不是连续的,而是以哈希映射的方式存在的。 

2、数据量不能太小,如果数据量太小,我们直接遍历就可以了,无序写复杂的二分查找来进行查询。


二分查找的三点重点:

1、循环退出条件

注意是 low <= height,而不是 low < heigh。如果是后者,会造成循环指向一个数据。

2mid 的取值

因为如果 low 比和 height 大的话,两者之和可能会溢出。应写成 low+(high-low)/2 ,如果优化到极致的话,改进为位运算符 low+((high-low)>>1)。

3、low 和 high 的更新

如果不进行 +1 和 -1 ,就有可能会发生死循环。

总结

---------------------------------------

自从学习数据结构与算法以来,发现它确实能解决很多我们身边实际的问题,而不仅仅停留到刷各种各样的算法题上。我们刷算法题的主要目的呢,是提高逻辑思维能力分析能力。还有一种能力也是需要提高的就是一个实际问题怎么才能转化为数据结构和算法问题,再考虑用什么样的数据结构和算法去解决?怎么找到一个最优的解决方案?

它对我们的理解、分析、转化实际问题到数据结构与算法提出了一个更高的要求,从之前写了两篇用数据结构与算法解决实际问题总结来看,我个人觉得不仅仅需要分析问题的能力,还考验一个人对所有数据结构与算法的灵活运用、优化、以及思想有很大的挑战性,因为不局限于一个算法题,还要考虑到实际的很多考虑不到的因素。


推荐阅读:

OpenCV4最全系统化学习路线图与教程!

【实战篇】| 模拟 20 万数据快速查询 IP 归属地相关推荐

  1. 任何快速查询IP归属地

    最近公司项目需要做一个IP归属地查询的功能,想着如果用现成的API就可以大大提高开发效率,所以在网上的API商店搜索了一番,发现了 APISpace,它里面的IP归属地API非常符合我的开发需求. I ...

  2. python读取20万数据Excel文件+拆分数据

    python读取20万数据Excel文件 使用普通的pandas读取Excel,再结合xlrd读取,可能会读取的Excel数据会不全,最多只能读取到65535+行的数,如果读取超大excel数据时就读 ...

  3. .NET Core 开源工具 IPTools - 快速查询 IP 地理位置、经纬度信息

    快速查询IP信息,支持国内和国外IP信息查询,支持查询经纬度,地理位置最高支持到城市. 1. IPTools.China 快速查询中国IP地址信息,包含国家.省份.城市.和网络运营商.非中国IP只支持 ...

  4. 利用IP地址查询接口来查询IP归属地

    如果我们在项目中需要获得用户的地址,而不仅仅是获得用户的IP,为了避免在自己的数据库里添加IP库,可以直接调用网上的第3方IP地址查询接口来查询IP归属地.今儿个在网上了解了这些接口,要么返回XML, ...

  5. shell 抓取页面信息 ip168查询 IP归属地

    通过ip168批量查询ip归属地#!/bin/bash while read line do echo $line ip=$(echo -en $line | awk '{print $3}' | a ...

  6. linux 查询ip归属地的工具,Linux 通过shell查询ip归属地(curl请求转码)

    root@kickseed:~# ping www.baidu.com                                                   #用百度进行测试获取百度IP ...

  7. python生成一个20万数据_用Python分析了20万场吃鸡数据,有不少有趣的发现

    首先,神枪镇楼 背景 最近老板爱上了吃鸡(手游:全军出击),经常拉着我们开黑,只能放弃午休的时间,陪老板在沙漠里奔波. 上周在在微信游戏频道看战绩的时候突发奇想,是不是可以通过这个方式抓取到很多战斗数 ...

  8. PHPEXCEL 20万数据导入导出(一)

    本片博客记录了一次实际开发中的需要使用PHPExcel导入导出大量数据(20万)的解决过程. 复盘优化自己的项目,好处在于,一旦已找到好的方法,好的代码,你就不可能再使用差的代码 场景描述:开发环境: ...

  9. php查询ip归属地api接口_【php】利用新浪api接口与php获取远程数据的方法,获取IP地址,并获取相应的IP归属地...

    本文与<[Servlet]Javaweb中,利用新浪api接口,获取IP地址,并获取相应的IP归属地>(点击打开链接)为姊妹篇,只是后端编程语言换成了php. 做出同样的效果,打开页面,得 ...

最新文章

  1. c++引用另一个类的方法_VlookUp函数使用方法,一张表引用另一张表的数据。
  2. java多线程查询_利用Java函数式接口处理多线程查询
  3. [JavaScript] FireBug 调试
  4. 中小企业SaaS型软件BI的发展前景
  5. Leetcode--8
  6. 高校云计算机中心建设方案,最新某大学云数据中心建设方案.pdf
  7. python详细安装教程-python详细安装pip教程
  8. 手机自动化测试的原理
  9. 利用NXlog采集Windows系统日志
  10. freeswitch与eyebeam
  11. 7-6 古风排版 (20分)
  12. logo设计的方法和技巧
  13. 最近用到的shell命令
  14. 甲骨文数据库购买的价格(二)
  15. 网络技术基础概念总结
  16. (PTA)基础编程题目集
  17. Java—计算长方形的周长和面积(类和对象)
  18. 2.1.2 Capturing HDR Videos(Advanced High Dynamic Range Imaging )
  19. 网易游戏面试题刷刷刷(1)
  20. Oracle Net Service:监听器与服务名解析方法

热门文章

  1. promise使用promise进行封装http请求接口
  2. MySQL的distinct:去重
  3. JQUERY输入改变事件change
  4. 古月居ros课件_【古月居】ROS2探索总结系列
  5. Apache ECharts各种图表页面展示
  6. PHP底层运行原理初探
  7. 世界公认最好的记忆方法_全球公认最好的12个教育孩子的方法,值得每个家长收藏学习!...
  8. 麻瓜编程python爬虫微专业_麻瓜编程 - 主页
  9. linux终端xwindow,如何从命令行重新启动X Window Server?
  10. php写简单接口_使用PHP如何编写简单的App接口