二分查找:如何快速定位IP对应的省份地址?

文章来源于极客时间前google工程师−王争专栏。

通过IP地址查找IP归属地功能:
image
这个功能是通过维护一个很大的IP地址库来实现。地址库中包含IP地址范围和归属地的对应关系。

当我们查询202.201.133.13这个IP地址归属地时,在地址库中搜索,这个IP落在[202.102.133.0,202.102.133.255]这个地址范围内,就可以显示“山东东营市”给用户了。

问题是:在庞大的地址库中逐一比对IP地址所在的区间是非常耗时的。假设我们有12万条这样的IP区间与归属地的对应关系,如何快速定位出一个IP地址的归属地呢?

主要学习二分查找的变形问题。

“十个二分九个错”,二分查找虽然简单,但是想写出没有Bug的二分查找并不容易。

常见的二分查找变形问题:
image

变体一:查找第一个值等于给定值的元素

简单二分法适用于有序数据集合中不存在重复的数据,我们在其中查找值等于某个给定值的数据。

如下图所示,如果查找第一个等于8的数据?
image
上图所示,如果用简单二分法的代码实现,首先拿8与区间的中间值a[4]比较,8比6大,于是在下标5到9之间继续查找,下标5和9的中间位置是下标7,刚好等于8,所以代码就返回了。

针对这种情况,可以稍微改造下简单二分法实现。

public int bsearch(int[] a, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {int mid = low + ((high - low) >> 1);if (a[mid] >= value) {high = mid - 1;} else {low = mid + 1;}}if (low < n && a[low]==value) return low;else return -1;
}

以上代码实现简洁,比较难理解

比较容易理解的实现如下:

public int bsearch(int[] a, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {int mid =  low + ((high - low) >> 1);if (a[mid] > value) {high = mid - 1;} else if (a[mid] < value) {low = mid + 1;} else {if ((mid == 0) || (a[mid - 1] != value)) return mid;else high = mid - 1;}}return -1;
}

a[mid]跟要查找的value的大小关系有三种情况:大于、小于、等于。对于a[mid]>value的情况,只需要更新high=mid-1;对于a[mid]<value的情况,只需要更新low=mid+1。当a[mid]=value的时候该如何处理呢?

如果mid=0,数组中的第一个元素,那这个肯定就是我们要找的。如果mid不等于0,a[mid]的前一个元素a[mid-1]不等于value,那么a[mid]也是我们要找的第一个给定值的元素。如果等于value,继续更新high=mid-1,因为我们要找的元素肯定在[low,mid-1]之间。

代码易读懂,没Bug,其实更重要,所以第二种写法更好。

变体二:查找最后一个值等于给定值的元素

public int bsearch(int[] a, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {int mid =  low + ((high - low) >> 1);if (a[mid] > value) {high = mid - 1;} else if (a[mid] < value) {low = mid + 1;} else {if ((mid == n - 1) || (a[mid + 1] != value)) return mid;else low = mid + 1;}}return -1;
}

参照变体一

变体三:查找第一个大于等于给定值的元素

比如,数组中存储的这样一个序列:3,4,6,7,10。如果查找第一个大于等于5的元素,那就是6。

代码实现如下:

public int bsearch(int[] a, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {int mid =  low + ((high - low) >> 1);if (a[mid] >= value) {if ((mid == 0) || (a[mid - 1] < value)) return mid;else high = mid - 1;} else {low = mid + 1;}}return -1;
}

查找最后一个小于等于给定值的元素

public int bsearch7(int[] a, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {int mid =  low + ((high - low) >> 1);if (a[mid] > value) {high = mid - 1;} else {if ((mid == n - 1) || (a[mid + 1] > value)) return mid;else low = mid + 1;}}return -1;
}

解答开篇

如何快速定位出一个IP地址的归属地?

首先预处理这12万条数据,让其按照起始IP从小到大排序。IP可以转化为32位的整型数。

按照起始IP从小到大排序。问题就转化为在有序数组中,查找最后一个小于等于某个给定值的元素了。

我们先通过二分查找,找到最后一个起始IP小于等于这个IP的IP区间,然后,检查这个IP是否在这个IP区间内,如果在就取对应的归属地显示;如果不在,就返回未查到。

总结

**凡是用二分查找能解决的,绝大部分我们更倾向于用散列表或者二叉查找树。**即便是二分查找在内存使用上更节省,但是内存紧缺的情况并不多。二分查找真的没什么用处了吗?

“值等于给定值”的二分查找确实不怎么会被用到,二分查找更适合用在“近似”查找问题,在这类问题上,二分查找的优势更加明显。比如这篇讲到的几种变体问题,用其他数据结构,比如散列表、二叉树,就比较难实现了。

变体二分法,容易因为细节处理不好产生Bug,这些容易出错的细节有:终止条件、区间上下界更新方法、返回值选择。

思考

循环有序数组,比如4,5,6,1,2,3。如何实现一个“值等于给定值”的二分算法呢?

对应leetcode 33题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/116080.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT_day6

qt与数据库连接&#xff0c;实现添加、显示、查找、删除功能 weiget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QSqlDatabase> //数据库管理 类 #include <QSqlQuery> //执行sql语句 类 #include <QSqlRecord> //数据…

H3C AC通过Web平台进行AC软件的升级?

软件升级的流程 1、获取软件版本 登录新华三官网&#xff08;首页>产品支持与服务>文档与软件>软件下载&#xff09;&#xff0c;将指定的软件版本下载至本地。 无线路由器-无线接入点-无线控制器-新华三集团-H3C 官网软件下载公共账号密码&#xff1a;账号&#x…

selenium 3种等待方式

三种等待方式 ①强制等待 强制等待是使线程休眠一定时间。强制等待一般在隐式等待和显式等待都不起作用时使用。示例代码如下&#xff1a; time.sleep(3)②隐式等待 隐式等待的作用是全局的&#xff0c;是作用于整个 session 的生命周期&#xff0c;也就是说只要设置一次隐式…

c语言刷题(第8周)

输入整数m,n&#xff08;m&#xff1c;n&#xff09;&#xff0c;输出[m,n]之间所有能被3&#xff0c;不能被4整除的所有数并求和&#xff0c;输出时每行显示5个数。 题干输入整数m,n&#xff08;m&#xff1c;n&#xff09;&#xff0c;输出[m,n]之间所有能被3&#xff0c;不…

C++进阶篇2---多态

1.多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是当不同的对象&#xff0c;去完成某个行为&#xff0c;会产生不同的状态 举个例子&#xff1a;同样是吃饭&#xff0c;狗吃狗粮&#xff0c;猫吃猫粮&#xff0c;不同的对象&#…

nodejs+vue大学生社团管理系统

通过软件的需求分析已经获得了系统的基本功能需求&#xff0c;根据需求&#xff0c;将大学生社团管理系统平台功能模块主要分为管理员模块。管理员添加社团成员管理、社团信息管理&#xff0c;社长管理、用户注册管理等操作。 目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1…

asp.net社区医疗辅助诊断网站系统VS开发sqlserver数据库web结构c#编程

一、源码特点 asp.net社区医疗辅助诊断网站系统 是一套完善的web设计管理系统&#xff0c;系统采用mvc模式&#xff08;BLLDALENTITY&#xff09;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver200…

spring懒加载

简介 Spring默认会在容器初始化的过程中&#xff0c;解析xml或注解&#xff0c;创建配置为单例的bean并保存到一个map中&#xff0c;这样的机制在bean比较少时问题不大&#xff0c;但一旦bean非常多时&#xff0c;spring需要在启动的过程中花费大量的时间来创建bean &#xff0…

Flink学习笔记(四):Flink 四大基石之 Window 和 Time

文章目录 1、 概述2、 Flink 的 Window 和 Time2.1、Window API2.1.1、WindowAssigner2.1.2、Trigger2.1.3、Evictor 2.2、窗口类型2.2.1、Tumbling Windows2.2.2、Sliding Windows2.2.3、Session Windows2.2.4、Global Windows 2.3、Time 时间语义2.4、乱序和延迟数据处理2.5、…

【2024秋招】2023-9-14 最右线下后端开发二面

1 OS 1.1 讲讲什么是虚拟内存&#xff0c;怎么实现的 虚拟内存是一种存储器管理能力&#xff0c;它使得一个应用程序似乎有更多的物理内存&#xff08;RAM&#xff09;可用&#xff0c;而实际上&#xff0c;系统使用了一部分硬盘空间来模拟额外的 RAM。通过使用虚拟内存&…

Git(一)Windows下安装及使用Git Bash

目录 一、简介1.1 什么是Git&#xff1f;1.2 Git 的主要特点1.3 什么是 Git Bash&#xff1f; 二、下载三、安装3.1 同意协议3.2 选择安装位置3.3 其他配置&#xff08;【Next】 即可&#xff09;3.4 安装完毕3.5 打开 Git Bash 官网地址&#xff1a; https://www.git-scm.com/…

视频去噪网络BSVD的实现

前些天写了视频去噪网络BSVD论文的理解&#xff0c;详情请点击这里&#xff0c;这两个星期动手实践了一下&#xff0c;本篇就来记录一下这个模型的实现。 这个网络的独特之处在于&#xff0c;它的训练和推理在实现上有所差别。在训练阶段&#xff0c;其使用了TSM&#xff08;T…

基于斑马优化的BP神经网络(分类应用) - 附代码

基于斑马优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于斑马优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.斑马优化BP神经网络3.1 BP神经网络参数设置3.2 斑马算法应用 4.测试结果&#xff1a;5.M…

【数据仓库-零】数据仓库知识体系 ing

文章目录 一. 数仓基本概念二. 离线数仓建设方法论三. etl流程四. 数仓规范建设指南四. 数据仓库架构五. 数据可视化 通过熟悉构建数仓整体的过程&#xff0c;可以系统的了解 数仓构建理论&#xff1a;能够站在全局角度看数仓的运行架构&#xff0c;数仓执行流程。了解到构建数…

车道线检测laneatt 学习笔记

目录 图片检测可视化 图片检测可视化 import logging import argparse import os import timeimport cv2 import numpy as np import torchfrom lib.config import Config from lib.runner import Runner from lib.experiment import Experimentdef parse_args():parser = ar…

虚拟机安装centos系统后配置桥接网络

一.桥接网络和nat网络的区别 桥接模式 通过使用物理机网卡 具有单独ip,但是需要手动配置。 在bridged模式下&#xff0c;VMWare虚拟出来的操作系统就像是局域网中的一台独立的主机&#xff0c;它可以访问网内任何一台机器。主机网卡和虚拟网卡的IP地址处于同一个网段&#xff…

App爬虫之强大的Airtest的操作总结

App爬虫之强大的Airtest的操作总结 App爬虫之强大的Airtest的操作总结 # Python使用该框架需要安装的依赖库 pip install airtest pip install poco pip install pocouifrom airtest.core.api import * from airtest.cli.parser import cli_setup from poco.drivers.android.…

Mybatis的SqlRunner执行流程

Mybatis的SqlRunner执行流程 SqlRunner exec new SqlRunner(connection); Map<String, Object> row exec.selectOne("SELECT * FROM PRODUCT WHERE PRODUCTID ?", "FI-SW-01");connection.close();assertEquals("FI-SW-01", row.ge…

【QT开发(10)】QT 进程

文章目录 1.1 运行一个新进程1.2 QProcess 还可以对一些信号进行关联2 进程间通信2.1 使用共享内存实现进程通信2.2 演示 代码仓库参考 1.1 运行一个新进程 使用类 QProcess&#xff0c;允许将一个进程堪称一个顺序IO设备。 在Qt中&#xff0c;QProcess类是用于启动外部进程的…

大模型与知识图谱如何相互助力

目前各行各业在数字化、智能化发展的大势所趋下&#xff0c;信息新技术不断涌现&#xff0c;也在加快深入融合到传统实体行业应用中&#xff0c;比如知识图谱、人工智能、数字孪生等等&#xff0c;特别是基于人工智能的大模型在去年底被chatgpt的带领下涌现出一波又一波的浪潮&…