我有一些列表中包含可变数量的元素。每个列表都已排序,但排序算法未知。我想将列表合并到一个大列表中,该列表包含相同顺序的所有列表,没有重复。
示例输入:
预期结果:
通过匹配输入序列获得预期结果,以便以正确的顺序获得包含每个输入序列的元素的合并结果,如下所示:
XS M L XL
S M XXL
XXS XS S L
-------------------
XXS XS S M L XL XXL
如果存在具有不明确位置的元素,则该函数应通知。在这里,它将是XXL(它可以保持在M,L或XL之后)并且我需要在XL之后手动指定其位置(因为在这里我知道排序算法并且可以帮助)。 我想要定义每两个元素的对,每个元素按照原始列表的顺序排列。从这一点可以建立完整的清单。
答案 0 :(得分:14)
这可以通过Topological Sort算法解决。
如果您将每个输入序列视为通过有向图的路径,拓扑排序将从左到右排序您的节点集,使每个有向边指向右侧。
Topological Sorting上的维基百科页面包含此算法,该算法首先由Arthur Kahn于1962年描述:
L ← Empty list that will contain the sorted elements
S ← Set of all nodes with no incoming edges
while S is non-empty do
remove a node n from S
insert n into L
for each node m with an edge e from n to m do
remove edge e from the graph
if m has no other incoming edges then
insert m into S
if graph has edges then
return error (graph has at least one cycle)
else
return L (a topologically sorted order)
如果编写的算法,如果找到不明确的序列,它实际上并不会失败,但通过在循环开头插入一个检查就可以轻松添加,如下所示:
...
while S is non-empty do
if S contains more than 1 item
return error (inputs are ambiguous)
remove a node n from S
...
我不知道你在使用什么语言,但我把这个PHP实现放在一起作为概念证明:
function mergeSequences($sequences, $detectAmbiguity = false) {
// build a list of nodes, with each node recording a list of all incoming edges
$nodes = array();
foreach ($sequences as $seq) {
foreach ($seq as $i => $item) {
if (!isset($nodes[$item])) $nodes[$item] = array();
if ($i !== 0) {
$nodes[$item][] = $seq[$i-1];
}
}
}
// build a list of all nodes with no incoming edges
$avail = array();
foreach ($nodes as $item => $edges) {
if (count($edges) == 0) {
$avail[] = $item;
unset($nodes[$item]);
}
}
$sorted = array();
$curr = '(start)';
while (count($avail) > 0) {
// optional: check for ambiguous sequence
if ($detectAmbiguity && count($avail) > 1) {
throw new Exception("Ambiguous sequence: {$curr} can be followed by " . join(' or ', $avail));
}
// get the next item and add it to the sorted list
$curr = array_pop($avail);
$sorted[] = $curr;
// remove all edges from the currently selected items to all others
foreach ($nodes as $item => $edges) {
$nodes[$item] = array_diff($edges, array($curr));
if (count($nodes[$item]) == 0) {
$avail[] = $item;
unset($nodes[$item]);
}
}
}
if (count($nodes) > 0) {
throw new Exception('Sequences contain conflicting information. Cannot continue after: ' . join(', ', $sorted));
}
return $sorted;
}
你可以这样调用这个函数:
$input = array(
array('XS', 'M', 'L', 'XL'),
array('S', 'M', 'XXL'),
array('XXS', 'XS', 'S', 'L'),
);
echo(join(', ', mergeSequences($input)));
echo(join(', ', mergeSequences($input, true)));
获得以下输出:
XXS, XS, S, M, XXL, L, XL
Uncaught exception 'Exception' with message 'Ambiguous sequence: M can be followed by L or XXL'
答案 1 :(得分:6)
您正在尝试合并partially ordered sets或posets。合并的模糊部分称为antichains。所以,你想要一个算法来合并posets,并告诉你什么是反链。
Here is a paper describing an algorithm for merging posets and detecting antichains,以及link to the first author's homepage,以防您想与他联系,看看是否有可用的源代码。
答案 2 :(得分:3)
这就是我要做的事情:
答案 3 :(得分:-1)
对于排序部分,我认为根据您的描述,合并排序就足够了。需要修改的一件事是在合并期间,如果输入数组的第一个元素与结果数组相同,我们应该跳过输入数组中的元素。
如果我理解正确,您希望构建整个可能输入元素的总顺序。某些部分顺序已在输入结构中定义(因为它们已经排序),而其他部分需要由用户指定。例如,在问题中,订单
'S' < 'M' < 'XXL'
'XS'<'M'<'L'<'XL'
'XXS' < 'XS' < 'S' < 'L'
定义明确。但算法仍然不知道'XXL'是大于还是小于'XL','L' 因为三个输入结构是排序的,所以必须存在输入元素的总顺序。所以我的建议是向您的数据提供者询问所有可能数据元素的有序列表。听起来很愚蠢,但这是一个简单的方法。
如果此列表不可用,一种简单的处理方法是提示用户进行一对排序,然后检查这是否与现有输入序列冲突并记住它,当算法遇到模糊对时。我认为拓扑排序比这个应用程序更强大。由于我们处理单个数据元素,因此必须退出总订单。拓扑排序是为了处理部分顺序。