【问题标题】:Is there a data structure for storing 2D points of natural numbers, which allows checking for existence in O(1)?是否有用于存储 2D 自然数点的数据结构,允许在 O(1) 中检查是否存在?
【发布时间】:2019-11-20 07:34:13
【问题描述】:

我想要一个数据结构,它可以存储自然数的 n 个 2D 点 (x,y) 的列表,然后允许在 O(1) 复杂度时间内检查给定点是否存在于原始点列表中,并且这个时间复杂度应该是确定性的(意味着哈希表不是一个选项)。该数据结构的创建也应该尽可能高效。我不能使用需要在时间复杂度上进行一些初始化的数据结构,这比 O(n * sqrt(n)) 更糟。

这样的数据结构存在吗?

【问题讨论】:

  • 按索引查找的数组是O(1),因此在现有点设置一个逻辑数组true,其他位置false。我认为这也满足了您对初始化复杂性的第二个要求。
  • @HighPerformanceMark 不幸的是,他需要一个大小为n*n的二维数组
  • 感谢您接受我的回答。您现在也可以对答案进行投票,这样他们的排名就会更高,并且作者会获得额外的声誉。
  • @MattTimmermans 这是一个不错的方法,但它无助于数据结构初始化时间的约束。您只能创建O(nsqrt(n)) 存储桶,这会导致O(sqrt(n)) 的访问时间。

标签: algorithm data-structures time-complexity


【解决方案1】:

是的,确实存在。

首先,让我们考虑O(1) 包含操作的第一个候选对象。第一个自然候选者是二维数组。问题是另一个约束,它说初始化时间复杂度必须是O(n*sqrt(n))

如果我们不初始化值怎么办?我们可能会对这些内存单元中已经存在的随机垃圾感到满意,并通过将arr[x][y] 设置为 1 或任何其他值来将一个点标记为数组中。

不幸的是,这可能会以非常小的概率导致误报,但我们不能将 arr 初始化为零,因为它需要 O(n^2)

幸运的是,存在用于常量数组初始化的算法。我稍后会在这里描述它,但现在,你可以阅读它here

算法说明

让我们有一个大小为MAX_INT x MAX_INT 的数组point_filter 和两个同样大小的数组。我们称他们为FROMTO。我们还需要一个指标 TOP 初始化为 0。

point_filter: array MAX_INT x MAX_INT
FROM:         array MAX_INT x MAX_INT
TO:           array MAX_INT x MAX_INT
TOP:          0

我们假设数组中的所有值都是无符号整数。

我们会说,point_filter 的元素 i 已经被访问当且仅当 FROM[i] < TOP and TO[FROM[i]] = i

如果满足上述条件,则返回point_filter[i]的值,否则返回初始值(假设为0)。

第一次设置point_filter

FROM[i] = TOP
TO[TOP] = i
point_filter[i] = 1 //visited, point in filter
TOP++

这样,point_filter 的初始化采用O(1)。要说那个点i在集合中,按照上面的步骤,检查点之前是否插入过,检查FROM[i] < TOP and TO[FROM[i]] = i是否。

现在您需要调整二维数组的算法,这非常简单。只需将[i] 替换为[i][j],算法仍然有效。

【讨论】:

  • 据我了解,您的解决方案的数组大小不是n*n,而是(max(x)-min(x)) * (max(y) - min(y))。如果二维点数组是[(1,2), (3000000, 40000000)] 会发生什么?
  • 一个小问题 - n 指定原始列表中给出的点数。根据您的解决方案,如果我理解正确,我们应该分配的数组大小为 MAX_X * MAX_Y,它可能非常大(我现在只是对于每个点,x 和 y 是自然数,但没有界限)。我理解正确吗?如果是这样,那么不幸的是,这个解决方案可能不适合我。
【解决方案2】:

我认为在这种情况下,您必须选择希望 100% 的结果准确度或 100% 的 O(1) 保证。正如您所说,您希望绝对保证 O(1) 解决方案,您可以拥有一个准确度非常接近 100% 的算法。数据结构如下:

class ConstantLookup:
    class Hashers:
        def __init__(self, seed, len):
            self.seed = seed
            self.len = len
        def hash(self, x, y):
            ... perform your hash function in respect to seed here ...
            return hash_result % len
    def __init__(self, n_hashers, len):
             self.hashers = [Hashers(random.randint(0,1000, len) for i in range(n_hashers)]
             self.lists = [[False] * len for i in range(n_hashers)]

    def insert(self, x, y):
             for i, h in enumerate(self.hashers):
                 hash = h.hash(x, y)
                 self.list[i][hash] = True
    def contains(self, x, y):
             for i, h in enumerate(self.hashers):
                 hash = h.hash(x, y)
                 if not self.list[i][hash]:
                     return False
             return True

使用足够大的 lenn_hashers,您将获得接近 100% 的准确率,而插入的运行复杂度为 O(len * n_hashers * n) 并且查找复杂度为 O(len * n_hasher) ,可以认为是常数。

假设哈希器设计得很好,给出误报的机会是 (n/len) ^ n_hashers

这种方法的明显缺点是不允许删除。

【讨论】:

  • 请看看我的解决方案。它符合所有限制条件。
  • 您的解决方案需要最小的 O(m^2) 空间复杂度,这在现实世界的情况下可能是不切实际的。此外,甚至可以通过将多个 point_filter 的真值设置为不同的幻数来改进您的解决方案。您不应该仅仅因为它是一个相互竞争的答案而对与您的角度不同的答案投反对票。
  • 这个解决方案的实用性超出了这个问题的范围。就实用性而言,哈希表是最好的。
  • you have to choose either you want 100% accuracy in result or 100% guarantee on O(1). 不正确
  • 好的。我想我误读了你的算法。谢谢指出
猜你喜欢
  • 2010-10-02
  • 2012-05-05
  • 1970-01-01
  • 1970-01-01
  • 2021-09-14
  • 2017-09-25
  • 1970-01-01
  • 2017-03-06
  • 2013-08-31
相关资源
最近更新 更多