这样的数据结构是否存在?

时间:2016-07-27 20:34:21

标签: sorting data-structures functional-programming

我正在搜索一个数据结构,该结构可以像普通列表一样快速排序,并且应该允许以下列方式删除元素。假设我们有一个这样的列表:

[{2,[1]},
 {6,[2,1]},
 {-4,[3,2,1]},
 {-2,[4,3,2,1]},
 {-4,[5,4,3,2,1]},
 {4,[2]},
 {-6,[3,2]},
 {-4,[4,3,2]},
 {-6,[5,4,3,2]},
 {-10,[3]},
 {18,[4,3]},
 {-10,[5,4,3]},
 {2,[4]},
 {0,[5,4]},
 {-2,[5]}]

即。包含元组的列表(这是Erlang语法)。每个元组包含数字,以及列表,其中包含用于计算上一个数字的列表成员。我想对列表做的是以下内容。首先,排序,然后采取列表,最后 清理 列表。使用 clean 我的意思是从尾部删除包含头部元素的所有元素,或者换句话说,从尾部的所有元素中删除所有元素头不是空的。例如,排序后头部为{18,[4,3]}。下一步是删除包含43的列表中的所有元素,即结果列表应该是这个:

[{6,[2,1]},
 {4,[2]},
 {2,[1]},
 {-2,[5]}]

接下来是采用新的头部并再次清洁直到整个清单被消耗。请注意,如果清理过程保留了顺序,则无需在每次迭代时使用列表。

这里的瓶颈是清洁过程。我需要一些能让我以比现在更快的方式进行清洁的结构。

是否有人知道某种结构允许以有效的方式执行此操作而不会丢失订单或至少允许快速排序?

2 个答案:

答案 0 :(得分:1)

是的,你可以比这更快。您的问题是您将第二个元组成员表示为列表。搜索它们很麻烦,而且非常不必要。它们都是5..1的连续子串。你可以简单地将它们表示为索引元组!

事实上,你甚至不需要带有这些索引元组的列表。将它们放在一个二维数组中,就在相应元组给出的位置,你会得到一个triangular array

h\l|  1   2   3   4   5
---+----------------------
 1 |  2
 2 |  6   2
 3 | -4  -6  -10
 4 | -2  -4   18  2
 5 | -4  -10 -10  0  -2

不是将数据存储在二维数组中,您可能希望将它们存储在一个带有一些索引魔法的简单数组中以解释三角形形状(如果您的编程语言只允许使用矩形二维数组),但这不会影响复杂性。

这是通过简单查看来快速过滤“列表”所需的所有结构。

我们只需在整个结构中迭代一次,找到最大值及其索引,而不是先排序并获取头部:

max_val = 18
max = (4, 3) // the two indices

过滤器非常简单。如果我们不使用列表(not (any (substring `contains`) selection))或集合(isEmpty (intersect substring selection))但是使用元组,那么它只是sel.high < substring.low || sel.low > substring.high。而且我们甚至不需要迭代整个三角形数组,我们可以简单地迭代高阶和低阶三角形:

result = []
for (i from 1 until max[1])
    for (j from i until max[1])
        result.push({array[j][i], (j,i)})
for (i from max[0] until 5)
    for (j from i until 5)
        result.push({array[j+1][i+1], (j+1,i+1)})

你已经拥有了所需的元素:

[{ 2, (1,1)},
 { 6, (2,1)},
 { 4, (2,2)},
 {-2, (5,5)}]

现在你只需要对它进行排序,你就得到了结果。

实际上,三角形阵列的整体复杂性并没有变得更好。您仍然需要O(n)来构建列表并找到最大值。是否通过针对每个子字符串索引元组进行测试来过滤O(n),或者通过智能选择过滤O(|result|)无关紧要,但您特别询问了快速清理步骤。如果数据很大,或者您需要进行多次清洁,这在现实中仍然可能是有益的 影响整体复杂性的唯一因素是仅对结果进行排序,而不是整个输入。

答案 1 :(得分:0)

我想知道您的原始数据结构是否可以被视为有向图的邻接列表? E.g;

{2,[1]},
{6,[2,1]}

表示您拥有这些节点和边缘;

node 2 => node 1
node 6 => node 2
node 6 => node 1

所以你的问题可以改写为;

  

如果我找到一个链接到节点4和3的节点,如果删除节点4和3,图表会发生什么?

一种方法是建立邻接矩阵;一个NxN位矩阵,其中每个边沿都是1位。你的问题现在变成了;

  

将4行中的每一位和4列中的每一位设置为零。

也就是说,没有任何链接进入或删除此已删除的节点。

作为优化,保留长度为N的位数组。如果节点未被删除,则设置该位。因此,如果节点1,2,4和5是“直播”的话。 “3”和“3”被删除&#39;,数组看起来像

[1,1,0,1,1,0]

现在要删除&#39; 4&#39;,您只需清除该位;

[1,1,0,0,1,0]

当您完成删除操作后,请浏览邻接矩阵,但忽略在设置0的行或列中编码的任何边缘。

完整的例子。让我们说你有

[ {2, [1,3]},
  {3, [1]},
  {4, [2,3]} ]

那是邻接矩阵

  1 2 3 4
1 0 0 0 0  # no entry for 1
2 1 0 1 0  # 2, [1,3]
3 1 0 0 0  # 3, [1]
4 0 1 1 0  # 4, [2,3]

和面具

  [1 1 1 1]

要删除节点2,只需更改掩码;

  [1 0 1 1] 

现在,要弄清楚结构,伪代码如:

rows = []
for r in 1..4:
  if mask[r] == false:
    # this row was deleted
    continue;

  targets = []
  for c in 1..4:
    if mask[c] == true && matrix[r,c]:
      # this node wasn't deleted and was there before
      targets.add(c)

  if (!targets.empty):
    rows.add({ r, targets})

邻接矩阵可以变得很大 - 毕竟它是NxN位 - 所以这只会在小而密集的矩阵上更好,而不是大的稀疏矩阵。

如果这不是很好,您可能会发现谷歌更容易使用图表算法而不是自己发明它们:)