前言:粗略的思考一下,用户在登录的时候会将自己的位置信息告诉服务器,服务器会记录一份用户的位置信息列表。假设服务器里只有10个人,那么要找附近的人就很简单,只需写一个算距离的函数,然后依次遍历长度是10的位置信息列表,距离从近到远排序,返回排序后的列表即可。那么如果服务器里有1千万人呢,或者几亿人呢,比如微信。这时再从头到尾遍历(复杂度O(n))就不适合了。通常情况下会使用“四叉树”来构建这些大量的位置信息。

一、四叉树

顾名思义,四叉树是叶子节点最多为4的树结构。由于采用的树结构,复杂度则变成了树的深度 O(logn)了。

二、地理位置和四叉树

把全球的位置定义为一个矩形,用经度和纬度的二维数据来定位全球范围内的点。

纬度范围[-90,90],经度范围[-180,180]。

当登录服务器的人数越来越多时,超过一个约定的值,就将这个矩形分割成4个更小的矩形,将这些位置信息分发到更小的矩形里(比如分成太平洋,大西洋等)更小的矩形存储的信息又要超过约定的值了,就再分割。

三、查找附近的点

假设我在广州天河的华农的图书馆,想要找到附近的10个人的位置。

那么首先需要从根节点(全球)遍历到天河区的节点,搜寻这个节点的列表里有没有10个人。

如果不够10个人,则向上一层的广州节点请求,可能会在海珠区找到剩余的人。

四、C++描述算法

以上就是四叉树搜索位置的关键点,那么就简单用C++来编写代码描述这个算法。

我采用的是位置全部信息存储在叶子的四叉树。

首先是核心的数据结构树的节点:

struct QuadTreeNode

{

QuadTreeNode(){}

Rect rect; //节点代表的矩形区域 std::vector pos_array; //节点中的位置信息 int child_num; //节点的孩子节点数量 QuadTreeNode *child[CHILD_NUM]; //指向孩子节点的指针 int depth; //节点的深度 };

创建一个节点:

void QuadTree::CreateQuadTreeNode(int depth, Rect rect, QuadTreeNode *p_node)

{

p_node->depth = depth;

p_node->child_num = 0;

p_node->pos_array.clear();

p_node->rect = rect;

for (int i = 0; i < CHILD_NUM; ++i)

{

p_node->child[i] = NULL;

}

}

分割一个节点:

这里采用均分法,即取x坐标和y坐标的一半划分。new四个节点,并且作为当前结点的4个孩子节点。

void QuadTree::Split(QuadTreeNode *pNode)

{

int start_x = pNode->rect.lb_x;

int start_y = pNode->rect.lb_y;

int sub_width = (pNode->rect.rt_x - pNode->rect.lb_x) / 2;

int sub_height = (pNode->rect.rt_y - pNode->rect.lb_y) / 2;

int end_x = pNode->rect.rt_x;

int end_y = pNode->rect.rt_y;

QuadTreeNode *p_node0 = new QuadTreeNode;

QuadTreeNode *p_node1 = new QuadTreeNode;

QuadTreeNode *p_node2 = new QuadTreeNode;

QuadTreeNode *p_node3 = new QuadTreeNode;

CreateQuadTreeNode(pNode->depth + 1, Rect(start_x + sub_width, start_y + sub_height, end_x, end_y), p_node0);

CreateQuadTreeNode(pNode->depth + 1, Rect(start_x, start_y + sub_height, start_x + sub_width, end_y), p_node1);

CreateQuadTreeNode(pNode->depth + 1, Rect(start_x, start_y, start_x + sub_width, start_y + sub_height), p_node2);

CreateQuadTreeNode(pNode->depth + 1, Rect(start_x + sub_width, start_y, end_x, start_y + sub_height), p_node3);

pNode->child[0] = p_node0;

pNode->child[1] = p_node1;

pNode->child[2] = p_node2;

pNode->child[3] = p_node3;

pNode->child_num = 4;

}

一个辅助函数,给一个指定节点,和目标位置,返回所在的象限。

象限划分:

int QuadTree::GetIndex(PosInfo pos, QuadTreeNode *pNode)

{

int start_x = pNode->rect.lb_x;

int start_y = pNode->rect.lb_y;

int sub_width = (pNode->rect.rt_x - pNode->rect.lb_x) / 2;

int sub_height = (pNode->rect.rt_y - pNode->rect.lb_y) / 2;

int end_x = pNode->rect.rt_x;

int end_y = pNode->rect.rt_y;

start_x + sub_width, start_y + sub_height, end_x, end_y;

//0象限 if ((pos.latitude >= start_x + sub_width)

&& (pos.latitude <= end_x)

&& (pos.longitude >= start_y + sub_height)

&& (pos.longitude <= end_y))

{

return UR;

}

//1象限 else if ((pos.latitude >= start_x)

&& (pos.latitude < start_x + sub_width)

&& (pos.longitude >= start_y + sub_height)

&& (pos.longitude <= end_y))

{

return UL;

}

//2象限 else if ((pos.latitude >= start_x)

&& (pos.latitude < start_x + sub_width)

&& (pos.longitude >= start_y)

&& (pos.longitude < start_y + sub_height))

{

return LL;

}

//3象限 else if ((pos.latitude >= start_x + sub_width)

&& (pos.latitude <= end_x)

&& (pos.longitude >= start_y)

&& (pos.longitude < start_y + sub_height))

{

return LR;

}

else

{

return INVALID;

}

}

接下来是关键点,插入函数,当数据一组一组出现的时候,如何构建一棵四叉树:

可以从根节点开始,如果该节点有孩子,则插入对应象限的孩子节点。

否则的话,判断当前结点的位置信息容量和深度。

如果位置信息还没满,深度在预先设定的范围内,则将位置信息插入当前结点。

否则需要将当前结点分割,并且把位置信息分发到各个新的孩子节点,删除自身的位置信息。然后重新对当前结点做一次插入操作。

void QuadTree::Insert(PosInfo pos, QuadTreeNode *p_node)

{

if (p_node == NULL)

{

return;

}

if (p_node->child_num != 0)

{

int index = GetIndex(pos, p_node);

if (index >= UR && index <= LR)

{

Insert(pos, p_node->child[index]);

}

}

else

{

if ((int)p_node->pos_array.size() >= m_maxobjects && p_node->depth < m_depth)

{

Split(p_node);

for (std::vector::iterator it = p_node->pos_array.begin(); it != p_node->pos_array.end(); ++it)

{

Insert(*it, p_node);

}

p_node->pos_array.clear();

Insert(pos, p_node);

}

else

{

p_node->pos_array.push_back(pos);

}

}

}

搜索附近的点:

从根节点遍历到目标节点所在节点,遍历该节点所有的位置信息点。

假如数量不够,则向上到父节点,搜寻兄弟节点,直到获得足够数量的位置信息点。

void QuadTree::Search(int num, PosInfo pos_source, std::vector &pos_list, QuadTreeNode *p_node)

{

if (p_node == NULL)

{

return;

}

if ((int)pos_list.size() >= num)

{

return;

}

if (p_node->child_num == 0)

{

for (std::vector::iterator it = p_node->pos_array.begin(); it != p_node->pos_array.end(); ++it)

{

if ((int)pos_list.size() >= num)

{

return;

}

pos_list.push_back(*it);

}

return;

}

//目标节点 int index = GetIndex(pos_source, p_node);

if (index >= UR && index <= LR)

{

if (p_node->child[index] != NULL)

{

Search(num, pos_source, pos_list, p_node->child[index]);

}

}

//目标节点的兄弟节点 for (int i = 0; i < CHILD_NUM; ++i)

{

if (index != i && p_node->child[i] != NULL)

{

Search(num, pos_source, pos_list, p_node->child[i]);

}

}

}

如果算法没错的||—_—||数据测试,随机1千万组数据,输入一个目标位置,搜索15个附近点的耗时是2毫秒。

随机1亿组数据,耗时是37毫秒。(插入数据用了一个小时,打印了一个1G的TXT)。

为了答谢大家关注和支持,这次给大家准备了限时领取福利:阿里面试题、百度面试题、滴滴面试题、华为面试题、京东面试题、美团面试题、腾讯面试题、头条面试题、中兴面试题。

还等什么小编推荐自己的linuxC/C++语言交流群:【前100名进群领取,额外赠送一份价值199的C/C++、linux资料包含(视频教程、电子书、实战项目及代码),下面部分展示。

messenger支持查找附近的人功能吗_现在很多的APP都有“附近的人”功能,这是哪个知识实现的呢!...相关推荐

  1. 现在很多的APP都有“附近的人“功能,这是哪个知识实现的呢!

    前言:粗略的思考一下,用户在登录的时候会将自己的位置信息告诉服务器,服务器会记录一份用户的位置信息列表.假设服务器里只有10个人,那么要找附近的人就很简单,只需写一个算距离的函数,然后依次遍历长度是1 ...

  2. 小程序商店刷榜_机刷8毛,人刷2块2,好评app都是刷出来的?苹果:刷榜app将从应用商店移除...

    (原标题:机刷8毛,人刷2块2,好评app都是刷出来的?苹果:刷榜app将从应用商店移除) 随着智能手机的普及,手机上的应用软件与我们的生活越来越密不可分.当你在手机上下载应用软件的时候,你会去看这个 ...

  3. messenger支持查找附近的人功能吗_最新的 macOS Catalina 正式版,值得更新吗?

    随着夏天的结束,Apple陆续发布了正式版的iOS 13.iPadOS等系统,早前广受瞩目的macOS Catalina正式版也终于上线.无论是从细节操作上的体验提升,还是Mac打通自家产品生态边界的 ...

  4. messenger支持查找附近的人功能吗_可以查找附近的人那个软件叫什么?

    2018-05-29 怎样查找刚删除的软件 你查找他的QQ号,再加他为好友就可以了;可以去QQ好友(群)恢复系统恢复好友.huifu.qq.com/系统可供用户恢复好友的范围和类型如何?1. 时间范围 ...

  5. 华为p4支持鸿蒙功能吗_吹过的牛都一步一步给实现了!明年华为手机支持升级鸿蒙系统!...

    12月16日,华为举行 HarmonyOS 2.0 手机开发者 Beta 活动,现场正式发布了 HarmonyOS 2.0 手机开发者 Beta 版本.据悉,本次 HarmonyOS 2.0 公测设备 ...

  6. 会python的人多吗_为什么很多人喜欢 Python?

    对于这个问题,汇智妹为大家请来了我们汇智动力的技术大咖--贵哥,贵哥从事近20年的软件开发.测试工作,今天就让他来为我们谈谈Python这门计算机语言. 福贵哥作为一个多年在IT界摸爬滚打的老兵,因项 ...

  7. android搜索框功能实现_巧用 Trie 树,实现搜索引擎关键词提示功能

    来源 | 码海责编 | Carol封图 | CSDN 付费下载于视觉中国我们几乎每天都在用搜索引擎搜索信息,相信大家肯定有注意过这样一个细节:当输入某个字符的时候,搜索引框底下会出现多个推荐词,如下, ...

  8. 华为nova5iotg功能使用_原来华为EMUI10输入法这么强大!使用这个功能,一分钟能打300字...

    没想打华为EMUI10自带输入法这么好用!开启这个功能,一分钟能打300字 手机输入法目前以搜狗.百度使用的比较广泛,但要说实用,还是华为手机自带的输入法比较好用.开启这个功能之后,一分钟能打300字 ...

  9. mysql查询每个人的总分_可不可以用一条SQL语句查询多人各自成绩的总和?

    展开全部 查询多人各自成绩的总和sql语句如下:62616964757a686964616fe78988e69d8331333433626462 select name,SUM(score) from ...

最新文章

  1. 炸了!一口气问了我18个JVM问题!
  2. Keil5.15使用GCC编译器链接.a库文件
  3. HTML行间距的设置方法
  4. Java API —— Map接口
  5. 2种方法帮你恢复Cisco路由器的密码
  6. JAVA进阶教学之(StringBuider进行字符串拼接)
  7. 出门问问CEO李志飞:当语音成为基石技术,消费场景如何进一步落地?
  8. Qt 小技术总结-持续更新
  9. linux之sed使用技巧
  10. php 禁止浏览器直接访问网页_.php后缀的url地址在浏览器打开怎么不让他下载,而是访问...
  11. Oracle join
  12. C# Winform 开发框架
  13. 石头机器人拖地水量调节_拖地组件再评测:正式版 家有 石头科技T4 米家一代 水箱拖地组件...
  14. LPDDR4X与LPDDR4 区别 <转>
  15. 服务器内存只支持双路主板,壕到没朋友,支持双路18核CPU、16条内存、3路显卡的主板来了...
  16. janusgraph环境搭建、janusgraph python导入csv顶点、边
  17. 明日之后怎么在电脑上玩 明日之后电脑版图文攻略
  18. Servlet实现 教师管理系统
  19. 新款 Mac mini(2018) 性能及接口分析
  20. 个人记录—— The bean ‘xxx.FeignClientSpecification‘ could not be registered ...

热门文章

  1. 求伪逆的五种方法比较
  2. CF1174F Ehab and the Big Finale 树分治
  3. 高铁、动车到底啥区别?看完彻底懂了(组图)
  4. 西门子博途1500SCL程序和梯形图两者结合编程,包括西门子v90伺服profinet通讯控制
  5. TP开发优秀开源的付费内容视频课程教学管理系统源码
  6. Linux-Memcache分布式部署方案(magent代理解决单点故障)
  7. 异常:java.lang.NoClassDefFoundError: net/sf/ezmorph/Morpher 解决办法
  8. 以计算机通信技术为核心的,浙江混合型IPPBX通信系统,战场上是以计算机通信技术为核心。...
  9. 软件测试修炼之道(摘自网络用以自勉)
  10. 联想笔记本e480恢复出厂设置_ThinkPad笔记本一键恢复键是哪个|Thinkpad按什么键进一键还原...