【问题标题】:Grid data structure网格数据结构
【发布时间】:2011-04-02 10:35:27
【问题描述】:

通常“可扩展”网格表示为列表列表(行列表,每行都有单元格列表),这些列表是某种链表。

在此数据结构中操作(删除、插入)行既简单又便宜,只需重新链接以前的节点即可,但是当涉及列时,例如删除一列,这将成为一个很长的操作,我需要“循环”所有行以删除该索引单元格。显然这不是好的行为,至少对我来说是这样。

我在这里不是在谈论数据库;我发现的一个很好的例子是一些文本文件到文本编辑器中,(据我所知)文本编辑器主要将行分成行并且很容易删除行。我希望删除一列与删除某些行一样便宜且高效。

最后,我需要一些多维网格,但我认为任何二维简单网格都适用于 MD,对吗?

【问题讨论】:

  • 所以你想要 O(1) 删除行和列?
  • 是的,这就是我要问的问题。
  • 谢谢大家,我在以下所有答案中都发现了非常有用的想法。并且不能真正推荐其中之一。都是很好的答案,需要阅读此类问题。

标签: algorithm data-structures grid multidimensional-array linked-list


【解决方案1】:

你可以有一个二维的“链接矩阵”(我忘记了正确的术语):

... Col 3 ... Col 4 ...
      |         |
... --X-- ... --Y-- ...
      |         |
...  ...  ...  ...  ...

如图所示,每个单元格有四个邻居。此外,您需要可能指示行/列位置的行和列标题,以及指向每行或列中的第一个单元格。这些最容易表示为没有向上邻居的特殊单元格(用于列标题)。

在 3 和 4 之间插入一个新列意味着向下迭代第 3 列中的单元格 X,并插入一个新的右邻居 Z。这个新的单元格 Z 向左链接到 X,向右链接到 Y。您还需要添加一个新列标题,并垂直链接新单元格。然后可以重新编号4之后所有列的位置(第4列变为第5列)。

... Col 3 Col 4 Col 5 ...
      |     |     |
... --X-----Z-----Y-- ...
      |     |     |
...  ...   ...   ...  ...

插入一列的成本是 O(n) 用于插入和链接新单元格,再次是 O(m) 用于更新列标题。删除过程类似。

因为每个单元格只有四个链接,所以行插入/删除使用相同的算法。

【讨论】:

  • 感谢您的回复,但您似乎没有理解我的问题,通过您的回答,您使行操作像列一样昂贵!这与我需要的相反。简而言之,列删除仍然很慢,因为我仍然必须遍历所有这些单元格并再次重新链接。它不像删除一行那么容易和便宜。在列插入中,该列将“已经”生成为列(考虑替换列)。但是,为了简单起见,我使用了“删除”。
  • 在我的方案中删除一列与删除一行具有相同的算法成本。如果行数多于列数,则绝对成本可能会有所不同——但每个单元格的成本仍然是线性的。我认为你不能做得比这更好,除非推迟一些工作(例如,只需将列标记为已删除,然后在保存时批量删除所有标记的列)。
  • 不是谈论导航,我需要更好的Manipulation结构而不是导航。实际上,“列表列表”结构比在每个单元格中添加更多指针更适合导航。同样,我正在寻找的是更好的有效数据操作解决方案。谢谢。
  • 这个结构不仅仅是列表的列表——它是一个二维链接的矩阵。也许您可以在原始问题中澄清问题:您说“我需要‘循环’所有行以删除索引单元格”。如果每一行都是一个普通的列表,你需要遍历每一行才能找到要从该行中删除的单元格;然后对所有其他行重复。你是这个意思吗?这不是我所描述的结构的问题,因为您可以快速遍历列中的所有单元格,而无需遍历每一行的列表。
  • 对不起,如果我的问题不清楚,也许我应该说“我想要 O(1) 删除/插入行和列”---行,但为什么不列?
【解决方案2】:

保持现有数据结构不变。另外,在每列被创建时给它一个唯一的 id。删除列时,只需将其 id 添加到所有已删除列 id 的哈希表中。每次遍历一行时,根据哈希表检查每个元素的列 ID(需要与元素的所有其他数据一起存储),如果已删除,则将其从行中拼接出来。

如果您有一个每个网格元素都可以指向的每列数据结构,则不需要哈希表和 ID。那么你只需要在那个数据结构中删除一个位。

顺便说一句,Edmund 的计划也适合你。尽管删除长度为 n 的行或列需要 O(n) 时间,但您大概可以将该成本与创建这 n 个元素的成本进行摊销,从而使删除的摊销时间为 O(1)。

【讨论】:

    【解决方案3】:

    我知道“链接列表”通常从理论角度受到赞赏,但在实践中它们通常效率低下。

    我建议转向随机访问容器以获得一些速度。最简单的是数组,但双端队列或索引跳过列表/B* 树可能更好,具体取决于我们所讨论的数据大小。

    从概念上讲,它并没有太大变化(还),但是您可以移动到 O(1)(数组,双端队列)/O(log N)(跳过列表/B* 树)中的给定索引操作,而不是使用简单链表的 O(N)。

    然后是魔术的时候了。

    Keith 已经公开了基本思想:与其实际删除该列,您只需将其标记为已删除,然后在您走过您的结构时“跳”到它上面。然而,哈希表需要线性遍历才能到达第 N 列。使用 Fenwick 树将产生一种计算实际索引的有效方法,然后您可以直接跳转到那里。

    请注意,将行标记为已删除的主要好处是undo 操作的明显可能性。

    还请注意,您可能需要构建一个压缩功能,以消除不时删除的列,而不是让它们累积。

    【讨论】:

      猜你喜欢
      • 2019-01-05
      • 2014-12-23
      • 1970-01-01
      • 2011-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-16
      • 2021-12-02
      相关资源
      最近更新 更多