在哈希表范围内查找键的最佳算法

Posted

技术标签:

【中文标题】在哈希表范围内查找键的最佳算法【英文标题】:Best algorithm to find a key within a range from hashtable 【发布时间】:2019-01-07 08:44:07 【问题描述】:

问题

从下面的项目列表中,我需要通过识别具有给定值的键范围来访问该项目,例如让我们说如果值是 2.1.1,它的单位是度、分和秒,我需要找到键 0.0.0- 30.0.0 性能是重中之重。

key: 0.0.0-30.0.0   value: x-y-z
key: 30.0.0-60.0.0  value: y-x-z
key: 60.0.0-90.0.0  value: z-y-z

解决方案 1: 到目前为止我尝试过的以下解决方案

如下重新创建新的键/值(json)文件

key: 0.0.0  value: x-y-z
key: 0.0.1  value: x-y-z
.
key: 0.0.59 value: x-y-z
.
key: 0.1.0 value x-y-z
key: 0.1.1 value x-y-z

key: 30.0.0  value: y-x-z
.
.
key: 30.0.59 value: y-x-z
.
key: 30.1.0 value: y-x-z
key: 30.1.1 value: y-x-z
key: 30.1.2 value: y-x-z
.
.
key: 30.1.59 value: y-x-z
key: 30.2.0 value: y-x-z
key: 30.2.1 value: y-x-z
key: 30-2.3 value: y-x-z
.
.
key: 60.0.0 value: z-y-x
key: 60.0.1 value: z-y-x
key: 60.0.2 value: z-y-x
.
.
key: 60.0.59 value: z-y-x
key: 60.1.0 value: z-y-x
key: 60.1.1 value: z-y-x
.
.

问题 上述解决方案的问题是文件大小将增加,这导致我的紧凑型应用程序中的堆溢出

解决方案2

?

【问题讨论】:

范围是否重叠?是否有未映射的范围?我的第一个想法是将它们放入一个排序数组中,然后在其中进行二进制搜索。 你的问题不够清楚。因此,您要做的是根据示例中从 0.0.0 到 30.0.0 的键范围获取键/值对的集合/列表,对吗? Json 文件包含格式为 degfrom.minfrom.secfrom-degto.minto.secto 的密钥,输入将类似于 deg1.min1.sec1 我想要的是快速找到一个密钥范围其中 deg1.min1.sec1 属于 键范围在排序数组中的二进制搜索可能很昂贵,因为这些不是直接数字,因此解析从 DMS 转换为十进制然后执行比较可能会出现性能问题,此函数每秒触发一次 是否可以在内存中加载json文件内容。 json文件有多大? 【参考方案1】:

使用 KD-tree 或 R-tree 数据结构而不是哈希映射。

【讨论】:

【参考方案2】:

由于您在第一个 json 中的条目是按顺序排序的,因此您可以解析您的输入并使用它来计算位置并生成条目的键。

对于第一个列表,我会将输入值解析为 3 个整数。对于这个,我们只需要输入的第一部分(度)。 然后我们得到这样的位置:

 int position = input_degree / step_size;

step_size 在您的第一个列表中为 30。

如果您需要密钥,您现在可以使用位置生成它:

String key_begin = position * step_size;
String search_key = key_begin + ".0.0-" + key_begin+step_size + ".0.0";

【讨论】:

【参考方案3】:

哈希表不太适合解决这个问题,因为当您可能查找的所有键都存储在表中时,哈希表效果最好:您说过您没有内存。二进制搜索确实是一个很好的方法,但是你提到......

键范围在排序数组二进制搜索中可能很昂贵,因为这些不是直接数字,因此从 DMS 解析转换为十进制然后执行比较可能会出现性能问题,此函数每秒触发一次。

首先,C++ 程序可以在不到一秒的时间内完成大量工作——即使是未经优化的查找也可能足够快地工作,但我们暂时假设你做到了需要更接近最佳速度...

“从 DMS 解析”含糊不清,但我假设您的意思是您的程序中有一个键的文本表示,例如“2.1.1”:解析它几乎肯定比必须做一个更好使用文本比较查找。要以“C++ 风格”解析文本,您可以简单地使用...

std::istringstream iss(the_incoming_key);
int degree, minute, second;
char c1, c2;
if (iss >> degree >> c1 >> minute >> c2 >> seconds &&
    c1 == '.' && c2 == '.')

    // have parsed a valid key...

else
    error_throw_exit_whatever();

如果您准备使用较旧的 C 函数来提高速度,请考虑:

if (sscanf(the_incoming_key.c_str(), "%d.%d.%d", &degree, &minute, &second) == 3)
    // have parsed a valid key...

解析了密钥后,你可以合理地:

1) 使用std::map<tuple<int8_t, int8_t, int8_t>, Value> values; 和使用std::make_tuple(degree, minute, second) 进行二分搜索,或者

2) 使用std::map<int, Value> values; 和使用degree * 3600 + minute * 60 + second 进行二分搜索 - 总秒数 值可能会或可能不会更快地让您的计算机进行比较。

    虽然乘法有点慢,所以可以使用 (degree << 12) + (minute << 6) + second 来避免它,因为 6 位足以存储 0 到 59 之间的值。

当然,在解析 json 文件和填充表时,您为创建密钥所做的任何转换都需要早先使用。

为了进一步优化,您可以拥有一组 360 度地图,并索引到您想要查找分钟和秒组件的特定地图。将搜索空间除以 360 可以预期在每次地图查找时消除 8 或 9 次比较(因为每次比较都会使仍在争用的元素数量减半,而 360 介于 2^8 和 2^9 之间)。

【讨论】:

我想要 javascript 解决方案 thru' 您的解决方案看起来非常适合服务器端,但在我的情况下,每秒访问服务器可能也太贵了,希望这段代码可以移植到 javascript 让我试一试,谢谢反正 @Naga:哦,对不起——我通常专注于 C++ 问题,当我检查哈希表标签时忘记检查你使用的是哪种语言。一些谷歌搜索表明正则表达式通常用于 Javascript 中的解析 - 像 dms_string.match(/\\d+\\.?\\*/g) 这样的东西应该返回一个子字符串数组,您可以调用 Number 来提取整数值?对于您的客户端/服务器情况,我有点迷茫:如果您的搜索键来自客户端,那么您要么需要数据结构来在那里搜索,要么 ping 服务器 - 看不出那是怎么回事避免。

以上是关于在哈希表范围内查找键的最佳算法的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法--------哈希表

算法哈希表 ( 两数之和 )

哈希表

哈希表与哈希(Hash)算法

哈希表及其常用算法(代码实例)

哈希算法(Hash)