算法:将列表从一个订单重新排列到另一个订单的最佳方法?

时间:2010-02-07 03:23:30

标签: javascript jquery algorithm diff

编辑:我不确定我的原始问题是否足够明确。我需要一种算法来计算最小的移动顺序,以便将数组从一个顺序重新排列到另一个顺序。众所周知,两个数组都包含相同的元素(没有重复)并且具有相同的长度。例如:

reorder(
  ['d', 'a', 'c', 'b', 'e'],
  ['a', 'b', 'c', 'd', 'e']
)

应该返回类似的内容:

[
  {move:'d', after:'b'},
  {move:'c', after:'b'}
]

表示我应首先将元素'd'移动到'b'之后,然后将'c'移动到'b'之后,数组将按所需顺序移动。


背景:我正在开发一个项目(实际上将rtgui中的大部分功能移到客户端)。现在我正在进行排序。基本上我有一个div列表,我想按任意顺序排序。我可以按如下方式获得所需的订单:

var hashes = {
  before: [],
  after: [],
};
var els = $('div.interesting-class').toArray();
var len = els.length;

for(var i = 0; i < len; i++) hashes.before.push(els[i].id);
els.sort(getSortComparator());
for(var i = 0; i < len; i++) hashes.after.push(els[i].id);

现在hashes.beforehashes.after包含无序和有序的元素ID列表。重新排序列表时,到目前为止,最昂贵的操作实际上是移动DOM元素。我这样做的原因如下:

var c = $('#container-id');
$(els).each(function() {
  c.append(this);
});

这样可行,但速度比必要慢,因为平均来说,实际上只需要移动2或3个元素。因此,我需要一种算法来计算最小的移动顺序,以便将数组从一个订单重新排列到另一个订单(在这种情况下,在hashes.beforehashes.after上运行)。任何人都可以建议或提出任何想法吗?

到目前为止,我已经尝试了几种通用的“diff”算法,但它们并没有真正给我我想要的东西。我认为我需要的是这样,但更专业。

4 个答案:

答案 0 :(得分:7)

http://en.wikipedia.org/wiki/Longest_increasing_subsequence

找到增长最长的子序列(根据新的排序顺序)。然后将不在该序列中的每个元素移动到相对于序列中已有元素的位置。

在您的示例中,'a,b,e'和'a,c,e'与最长的增加子序列相关联。你能做的最好就是选择其中一个,然后移动其他元素。

答案 1 :(得分:1)

将键和数组索引拆分为单独的对象数组{key,index}。对该数组进行排序(当然,使用最佳排序;如果比较是昂贵的话,可以是合并排序,如果它们很便宜,可以使用快速排序)。您现在知道,从实际索引到排序数组以及存储在每个元素中的索引值,如何重新排列原始数组。

对键进行排序后,“最佳”移动次数将为原始数组的O(n)。如果您想重新安排原始数组,那么您可以非常简单地从排序的索引列表中派生交换。

答案 2 :(得分:0)

我首先想到的是你应该使用Selection sort而不是内置的排序方法,因为它使所需的交换量最低。这样,您可以在移动列表中的id时移动DOM元素。

答案 3 :(得分:0)

Knuth Volume 3有一节关于“排序网络”。他没有详细介绍如何构建最小比较网络的详细信息,但是引用了一些关于如何构建它们的工作(例如,由Batcher和他自己)。请注意,虽然这些是“最小比较网络”的注释,但很少(如果有的话)确实证明是最小的 - 他们试图最小化所需的比较器数量,但不是在实际达到真正的最低限度方面必然是成功的。