创建数组动态索引的最有效方法是创建一个与要索引的数组所保存的数据类型相同的空数组。
为了简单起见,假设我们使用整数。然后,您可以将该概念扩展到任何其他数据类型。
理想的索引深度将取决于要索引的数据的长度,并且会接近数据的长度。
假设数组中有 100 万个 64 位整数要索引。
首先,您应该对数据进行排序并消除重复项。这很容易通过使用 qsort()(快速排序 C 内置函数)和一些删除重复函数来实现,例如
uint64_t remove_dupes(char *unord_arr, char *ord_arr, uint64_t arr_size)
{
uint64_t i, j=0;
for (i=1;i<arr_size;i++)
{
if ( strcmp(unord_arr[i], unord_arr[i-1]) != 0 ){
strcpy(ord_arr[j],unord_arr[i-1]);
j++;
}
if ( i == arr_size-1 ){
strcpy(ord_arr[j],unord_arr[i]);
j++;
}
}
return j;
}
根据您的需要调整上面的代码,当函数完成对有序数组的排序时,您应该 free() 无序数组。上面的函数非常快,当要排序的数组包含一个元素时,它将返回零条目,但这可能是你可以忍受的。
一旦数据有序且唯一,创建一个长度接近数据长度的索引。它不需要有一个确切的长度,虽然保证 10 的幂会让一切变得更容易,如果是整数。
uint64_t* idx = calloc(pow(10, indexdepth), sizeof(uint64_t));
这将创建一个空索引数组。
然后填充索引。遍历您的数组以索引一次,每次您检测到左侧有效数字的数量(与索引深度相同)发生变化时,添加检测到该新数字的位置。
如果您选择 indexdepth 为 2,您的索引中将有 10² = 100 个可能的值,通常从 0 到 99。
当您检测到某个数字以 10 (103456) 开头时,您向索引添加一个条目,假设在位置 733 检测到 103456,您的索引条目将是:
index[10] = 733;
下一个以 11 开头的条目应该添加到下一个索引槽中,假设第一个以 11 开头的数字在位置 2023 找到
index[11] = 2023;
等等。
当您稍后需要在存储 100 万个条目的原始数组中查找某个数字时,您不必迭代整个数组,您只需检查索引中第一个数字从前两位有效数字开始的位置被储存了。条目索引[10] 告诉您以 10 开头的第一个数字的存储位置。然后,您可以向前迭代,直到找到匹配项。
在我的示例中,我使用了一个小索引,因此您需要执行的平均迭代次数为 1000000/100 = 10000
如果您将索引扩大到接近数据长度的某个位置,则迭代次数将趋于 1,从而使任何搜索速度都非常快。
我喜欢做的是创建一些简单的算法,在知道要索引的数据的类型和长度后,告诉我理想的索引深度是多少。
请注意,在我提出的示例中,64 位数字由它们的第一个索引深度有效数字索引,因此 10 和 100001 将存储在同一个索引段中。这本身不是问题,但是每个大师都有他的小秘密书。将数字视为固定长度的十六进制字符串有助于保持严格的数字顺序。
您不必更改基数,您可以将 10 视为 0000010 以将其保留在 00 索引段中并保持基数为 10 的数字有序,但使用不同的数字基数在 C 中是微不足道的,这很棒帮助完成这项任务。
随着索引深度变大,每个索引段的条目数量会减少
请注意,编程,尤其是像 C 这样的低级编程,在很大程度上在于理解 CPU 周期和内存使用之间的权衡。
创建建议的索引是一种减少定位值所需的 CPU 周期数的方法,代价是随着索引变大而使用更多内存。尽管如此,这仍然是当今要走的路,因为大量的内存很便宜。
随着 SSD 的速度越来越接近 RAM 的速度,需要考虑使用文件存储索引。然而,现代操作系统倾向于尽可能多地加载到 RAM 中,因此从性能角度来看,使用文件最终会产生类似的结果。