我计划使用数组实现二进制搜索 - 但是我还需要支持动态更新/插入数组。到目前为止我所听到的最佳方法是在O(n)区域,因为插入点右侧的所有元素都需要向右移动一个。
我思考这个想法:我重新定义/拦截(或者#34;重载"取决于你的语言)插入方法 - 如果检测到插入索引10,例如,我"延迟&#34 ;这个插入,我将新元素保留在第二个列表中。在真实数组中没有插入任何内容,但算法会记住插入点。其他插入也是如此。然后,如果我检测到对我也截取的元素15的访问,我重新计算,将索引向右偏移1,并给出元素16,因为插入到i = 15左边的位置。
有一段时间我会通过真正插入真实列表来清空第二个列表,可能是通过插入排序。
这听起来像我可以快速工作。
这可行吗?
谢谢,
答案 0 :(得分:1)
您没有澄清有关您的想法的一些重要信息,例如您的算法如何记住新插入值的插入点,二进制搜索如何处理新的(即延迟的)插入,以及您如何如果有N个这样的插入,则处理。
在任何情况下,您都必须在某些时候将延迟插入与原始数组合并,这将花费 O(N)时间,或者您必须运行二进制搜索同时考虑原始数组和新插入的等待列表。考虑到动态插入可能会导致在两个元素之间插入一个元素,而这两个元素本身就存在于二级数组中,我不知道你怎么能以系统的方式做到这一点。你可能会从A的任何元素的访问成本中妥协,在常规数组中 O(1),反过来可能最终得到的效率低于 O(logN)的即可。因此,对您的想法的不完整定义的高级概述似乎不太可能在复杂性方面达到预期的结果。
根据其定义,二进制搜索在有序容器上工作,并且连续存储每个元素的数组)使任何尝试在任何位置插入然后重新排列顺序在最坏的情况下, O(N)。
虽然这不是一个正式的证据,但直观地说,你可能会想到这种情况。每当你必须向数组A插入一个小于A中所有元素的元素时,为了能够在A上工作的未来二进制搜索操作,你必须在开头插入它,以便保持A订购。当你在A的第一个位置插入这样一个小元素时,你必须将A中所有当前元素向右移动1,如果A的大小为A,则为 O(N)。被认为是N.(参见众所周知的算法insertion sort的插入步骤中的类似程序)
因此,除非您更改使用数组的首选项,否则在插入时会遇到 O(N)时间复杂度限制。但是,如果您考虑使用平衡二叉搜索树(例如Red-black tree,AVL tree),您可以实现更快的平均时间复杂度,并且不会牺牲搜索元素的效率。当然,在这种情况下,您运行以查找元素的算法将更多地是树遍历而不是二进制搜索。但是,如果更有效的搜索和插入(和删除)操作比您使用的特定算法和数据结构更重要,那么在数据结构和算法首选项中进行此更改应该是可以容忍的。
答案 1 :(得分:1)
“这可行吗?”:也许,但不太可能。
对于每次插入,您都必须查找待处理插入列表并确定对插入点的更正。您必须在待处理列表中插入新密钥。您回到了有序数组中插入的原始问题,使用较小的数组。
如果计算插入数,假设最大大小为L的待定列表,则将执行O(N),总成本为O(N.L)。所以你需要L比O(1)更好。
如果算上合并,你将执行N / L,费用为N / L.(N + L)。那么你需要L为Ω(N)。
这两个要求似乎不兼容。
确保在您之前(可能在您出生之前)彻底调查了排序列表中的插入问题,并且没有采用基于待处理列表的合适解决方案。
相反,平衡搜索树通常用于动态排序,并且已知它们是最优的,具有搜索,插入和删除操作的O(Log N)界限。
答案 2 :(得分:1)
我很确定你所看到的方向没有什么好处。我过去曾经做过类似的事情,但只有当有一种已知的访问模式才能使它变得有效时。
如果你想保持阵列存储和二进制搜索的一些效率,同时仍允许动态插入,B +树是一个很好的权衡:
https://en.wikipedia.org/wiki/B%2B_tree
在B +树中,每个节点都有一个键数组。您可以选择最大长度 - 通常在16到4096之间。阵列存储限制了浪费,因为您没有一大堆额外链接,有助于在搜索期间缓存局部性,并提供限制树深度的高扇出。