Algorithm 将多个任意排序的列表合并为一个列表

Algorithm 将多个任意排序的列表合并为一个列表,algorithm,delphi,sorting,Algorithm,Delphi,Sorting,给定3个按相同但未知的排序顺序任意排序的列表。是否有一种算法可以将这些列表合并成一个仍然按相同顺序排序的列表 例如: 清单1: A. B C F h 清单2: B C E h 清单3: C D E f 假设这些列表已排序,但所使用的排序顺序未知。我希望将这些列表合并为不包含重复项但仍保持排序顺序的结果: A. B C D E F h 如上所述:已知给定列表已排序,但不知道按哪个顺序排序,但要求合并列表仍按相同(未知)顺序排序 在上面的示例中,我知道元素“f”位于“e”和“h”之间,因为从列表1中

给定3个按相同但未知的排序顺序任意排序的列表。是否有一种算法可以将这些列表合并成一个仍然按相同顺序排序的列表

例如:

清单1: A. B C F h

清单2: B C E h

清单3: C D E f

假设这些列表已排序,但所使用的排序顺序未知。我希望将这些列表合并为不包含重复项但仍保持排序顺序的结果: A. B C D E F h

如上所述:已知给定列表已排序,但不知道按哪个顺序排序,但要求合并列表仍按相同(未知)顺序排序

在上面的示例中,我知道元素“f”位于“e”和“h”之间,因为从列表1中我知道

“c”<“f”<“h”

从清单2我知道

“c”<“e”<“h”

从清单3我知道了这一点

“e”<“f”和“c”<“e”

其组合为:

“c”<“e”<“f”<“h”

如果任何给定列表都无法确定排序顺序,则允许仅将元素附加到结果列表的末尾。此外,如果无法确定元素序列的排序顺序,则允许将它们以任何顺序插入列表,只要它们位于正确的位置(例如,如果我知道“b”和“c”必须插入“a”和“d”之间,但我不知道它应该是b c d还是c b d,则两者都是允许的。)

当然,这只是一个例子。真正的列表较长(但包含少于100个元素),包含的不是单个字符元素,而是多个字符元素,排序顺序不是字母顺序。此外,我有多达5个列表

我需要在Delphi中实现这个算法(不:这不是家庭作业,而是一个现实生活中的问题),但我采用的是一种语言的算法,前提是它不包含太多的编译器魔法或复杂的库函数


性能不是什么大问题,因为这是一次性的。

图论似乎是一个很好的第一直觉

您可以构建一个有向图,其中列表的元素是顶点,并从每个列表元素向其后续元素插入一条有向边。那么一个节点a小于另一个节点B,当且仅当通过遍历图可以从a到达B时

图中的循环(A小于B且B小于A)表示输入数据损坏或存在两个具有不同名称的等效元素


在没有循环的情况下,在给定的小于关系下进行合并应该很简单:从图中重复删除任何其他节点都无法到达的节点,并将它们添加到输出列表中。

输入列表定义了项目的部分顺序。根据,您需要的是拓扑排序

您可以使用哈希表吗?这里有一个合并两个列表的算法

T = new HashMap
for(i = 1 to length B)
  T.put(B[i],i)
N = array[length A]
for(i = 1 to length A){
  if(T containsKey A[i])
    N[i] = T.get(A[i])
  else
    N[i] = -1
}

R = array[length A + length B]
j = 1
k = 1
for(i = 1 to length A){
  if(N[i] = -1)
    R[j++] = N[i]
  else{
    while(k <= N[i])
      R[j++] = B[k++]
  }
}
while(k <= length B)
  R[j++] = B[k++]
return R[1 ... j-1]
T=newhashmap
对于(i=1至长度B)
T.put(B[i],i)
N=数组[长度A]
对于(i=1至长度A){
如果(T包含一个[i])
N[i]=T.get(A[i])
其他的
N[i]=-1
}
R=数组[长度A+长度B]
j=1
k=1
对于(i=1至长度A){
如果(N[i]=-1)
R[j++]=N[i]
否则{
而(k那么你有

List1: a b c f h
List2: b c e h
List3: c d e f
逐个列表并输入图表。因此,在第一个列表之后,您有:

A -> B -> C -> F -> H
然后从列表2开始。B已经在那里了。然后你看到B连接到你已经知道的C。然后你知道C连接到E,而E还没有在那里,所以你现在有:

A -> B -> C -> F -> H
          |          
          E
          D -> E ---|
          ^         |
          |         |
A -> B -> C -> F -> H
          D -> E
          ^    |     
          |    |     
A -> B -> C -> F -> H
然后您知道E连接到H,因此:

A -> B -> C -> F -> H
          |         ^ 
          E --------|
然后你进入清单3,你知道C在里面,它指向D:

          D
          ^
          |
A -> B -> C -> F -> H
          |         ^ 
          E --------|
然后您知道D指向E。由于C->E与C->D->E有相同的祖先,您可以断开C->E的链接,因此您现在有:

A -> B -> C -> F -> H
          |          
          E
          D -> E ---|
          ^         |
          |         |
A -> B -> C -> F -> H
          D -> E
          ^    |     
          |    |     
A -> B -> C -> F -> H
最后你知道E在F之前。因为你知道E之前直接指向H,现在有另一条从E到H的路径(E->F->H),你知道F必须在E和H之间,你可以从E->H中删除链接。因此你现在有:

A -> B -> C -> F -> H
          |          
          E
          D -> E ---|
          ^         |
          |         |
A -> B -> C -> F -> H
          D -> E
          ^    |     
          |    |     
A -> B -> C -> F -> H
然后你知道可以缩短为

A -> B -> C -> D -> E -> F -> H
现在让我们假设你最终得到了如下结果:

  E -> T
  |    |
  A -> Z
  |    ^
  R -> W
你没有足够的信息来判断E/t是否在R/W之前,但你知道两者都在Z之前和A之后。因此,你只需随机选择其中一条路径,然后选择下一条路径,以此类推,这样你就可以得到A-E-t-R-W-Z或A-R-W-E-t-Z。你甚至可以从每条路径中随机选择一条,这将保证这些路径都是正确的我会被排序,也许你会很幸运,你的合并也被排序了。所以你可以有一个A-R-E-W-T-Z,它的E/T仍然相对排序,R/W仍然相对排序,或者如果你从E段开始,你会很幸运,有一个A-E-R-T-W-Z

很好的问题。虽然可能是最推荐的方法,但你必须解析首先输入以建立依赖项列表。我想到了一种更直接的方法,基于查找出现在多个列表中的项来设置订单定义

我无法预测时间复杂度,但由于您不关心性能,特别是考虑到项目总数最多为500项,我认为该算法应该可以很好地工作

算法
  • 所有列表都放在一个临时列表中,然后自然排序,以识别和筛选所有重复项。这些重复项(命名键)构成最终排序顺序的唯一定义
  • 通过比较每两个项目,按输入排序顺序对键列表进行排序:如果两个键出现在同一个输入列表中,则该列表中的第一个键也位于输出列表中的第二个键之前。如果任何输入列表中没有两个键同时出现,则认为它们相等
  • 随后,循环在键上循环
  • 在每个循环中,在每个输入列表中,上一个键和当前键之间的每个项都添加到