messenger支持查找附近的人功能吗_现在很多的APP都有“附近的人”功能,这是哪个知识实现的呢!...
前言:粗略的思考一下,用户在登录的时候会将自己的位置信息告诉服务器,服务器会记录一份用户的位置信息列表。假设服务器里只有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都有“附近的人”功能,这是哪个知识实现的呢!...相关推荐
- 现在很多的APP都有“附近的人“功能,这是哪个知识实现的呢!
前言:粗略的思考一下,用户在登录的时候会将自己的位置信息告诉服务器,服务器会记录一份用户的位置信息列表.假设服务器里只有10个人,那么要找附近的人就很简单,只需写一个算距离的函数,然后依次遍历长度是1 ...
- 小程序商店刷榜_机刷8毛,人刷2块2,好评app都是刷出来的?苹果:刷榜app将从应用商店移除...
(原标题:机刷8毛,人刷2块2,好评app都是刷出来的?苹果:刷榜app将从应用商店移除) 随着智能手机的普及,手机上的应用软件与我们的生活越来越密不可分.当你在手机上下载应用软件的时候,你会去看这个 ...
- messenger支持查找附近的人功能吗_最新的 macOS Catalina 正式版,值得更新吗?
随着夏天的结束,Apple陆续发布了正式版的iOS 13.iPadOS等系统,早前广受瞩目的macOS Catalina正式版也终于上线.无论是从细节操作上的体验提升,还是Mac打通自家产品生态边界的 ...
- messenger支持查找附近的人功能吗_可以查找附近的人那个软件叫什么?
2018-05-29 怎样查找刚删除的软件 你查找他的QQ号,再加他为好友就可以了;可以去QQ好友(群)恢复系统恢复好友.huifu.qq.com/系统可供用户恢复好友的范围和类型如何?1. 时间范围 ...
- 华为p4支持鸿蒙功能吗_吹过的牛都一步一步给实现了!明年华为手机支持升级鸿蒙系统!...
12月16日,华为举行 HarmonyOS 2.0 手机开发者 Beta 活动,现场正式发布了 HarmonyOS 2.0 手机开发者 Beta 版本.据悉,本次 HarmonyOS 2.0 公测设备 ...
- 会python的人多吗_为什么很多人喜欢 Python?
对于这个问题,汇智妹为大家请来了我们汇智动力的技术大咖--贵哥,贵哥从事近20年的软件开发.测试工作,今天就让他来为我们谈谈Python这门计算机语言. 福贵哥作为一个多年在IT界摸爬滚打的老兵,因项 ...
- android搜索框功能实现_巧用 Trie 树,实现搜索引擎关键词提示功能
来源 | 码海责编 | Carol封图 | CSDN 付费下载于视觉中国我们几乎每天都在用搜索引擎搜索信息,相信大家肯定有注意过这样一个细节:当输入某个字符的时候,搜索引框底下会出现多个推荐词,如下, ...
- 华为nova5iotg功能使用_原来华为EMUI10输入法这么强大!使用这个功能,一分钟能打300字...
没想打华为EMUI10自带输入法这么好用!开启这个功能,一分钟能打300字 手机输入法目前以搜狗.百度使用的比较广泛,但要说实用,还是华为手机自带的输入法比较好用.开启这个功能之后,一分钟能打300字 ...
- mysql查询每个人的总分_可不可以用一条SQL语句查询多人各自成绩的总和?
展开全部 查询多人各自成绩的总和sql语句如下:62616964757a686964616fe78988e69d8331333433626462 select name,SUM(score) from ...
最新文章
- 炸了!一口气问了我18个JVM问题!
- Keil5.15使用GCC编译器链接.a库文件
- HTML行间距的设置方法
- Java API —— Map接口
- 2种方法帮你恢复Cisco路由器的密码
- JAVA进阶教学之(StringBuider进行字符串拼接)
- 出门问问CEO李志飞:当语音成为基石技术,消费场景如何进一步落地?
- Qt 小技术总结-持续更新
- linux之sed使用技巧
- php 禁止浏览器直接访问网页_.php后缀的url地址在浏览器打开怎么不让他下载,而是访问...
- Oracle join
- C# Winform 开发框架
- 石头机器人拖地水量调节_拖地组件再评测:正式版 家有 石头科技T4 米家一代 水箱拖地组件...
- LPDDR4X与LPDDR4 区别 <转>
- 服务器内存只支持双路主板,壕到没朋友,支持双路18核CPU、16条内存、3路显卡的主板来了...
- janusgraph环境搭建、janusgraph python导入csv顶点、边
- 明日之后怎么在电脑上玩 明日之后电脑版图文攻略
- Servlet实现 教师管理系统
- 新款 Mac mini(2018) 性能及接口分析
- 个人记录—— The bean ‘xxx.FeignClientSpecification‘ could not be registered ...
热门文章
- 求伪逆的五种方法比较
- CF1174F Ehab and the Big Finale 树分治
- 高铁、动车到底啥区别?看完彻底懂了(组图)
- 西门子博途1500SCL程序和梯形图两者结合编程,包括西门子v90伺服profinet通讯控制
- TP开发优秀开源的付费内容视频课程教学管理系统源码
- Linux-Memcache分布式部署方案(magent代理解决单点故障)
- 异常:java.lang.NoClassDefFoundError: net/sf/ezmorph/Morpher 解决办法
- 以计算机通信技术为核心的,浙江混合型IPPBX通信系统,战场上是以计算机通信技术为核心。...
- 软件测试修炼之道(摘自网络用以自勉)
- 联想笔记本e480恢复出厂设置_ThinkPad笔记本一键恢复键是哪个|Thinkpad按什么键进一键还原...