我很确定这必须出现在某种教科书中(或者更有可能出现在所有教科书中),但我似乎使用了错误的关键词来搜索它...... :(
我在编程时遇到的一个反复出现的任务是我正在处理来自不同来源的对象列表,我需要以某种方式保持同步。通常存在某种“主列表”,例如由一些外部API返回,然后是我自己创建的对象列表,每个对象都与主列表中的对象相对应(想想“包装器”或“适配器” - 它们通常包含有关我的应用程序特定的外部对象的扩展信息和/或者它们简化了对外部对象的访问。)
那么我通常如何解决这个问题呢?我应该谷歌的算法名称是什么?
在过去,我已经以各种方式实现了这一点(参见下面的示例),但总觉得应该有更清洁,更有效的方式,特别是不需要两次迭代的方法(每个列表一个)。
以下是一个示例方法:
更新1 感谢您到目前为止的所有回复!我需要一些时间来查看链接 [...] (文字移至问题正文)
更新2 将中间段重构为(希望)更易于解析的子弹列表,并在第一次更新后添加了详细信息。
答案 0 :(得分:3)
这看起来像设置协调问题,即同步无序数据的问题。关于SO的问题被问到:Implementation of set reconciliation algorithm。
Google上的大多数参考文献都是技术论文摘要。
答案 1 :(得分:2)
两种典型的解决方案是: 1.将主列表复制到同步列表。 2.在所有元素对之间进行O(N * N)比较。
您已经排除了智能选项:共享身份,排序和更改通知。
请注意,列表是否可以以有意义的方式排序,甚至完全无关。例如,在比较两个字符串列表时,按字母顺序排序是理想的。但是,如果您按字符串 length 对两个列表进行排序,列表比较仍然会更有效!你仍然需要对相同长度的字符串进行完全成对比较,但这可能是一个更小的数量对。
答案 2 :(得分:2)
解决此类问题的最佳方法通常是不直接解决问题。
如果您真的无法在代码中使用已排序的二进制可搜索容器(如集合甚至是已排序的向量),那么......
你的记忆力很强吗?如果没有,那么我只是创建一个包含其中一个列表内容的字典(例如std :: set),然后迭代第二个我希望与第一个同步的字典。
这样你就可以用 logn来创建字典(或者n X用于哈希字典,具体取决于哪个更有效)+ m logn操作来覆盖第二个列出并同步它(或者只是M Y) - 如果你真的必须首先使用列表,那就很难被击败 - 只有当你需要它时它也只做一次它并且它比保持它更好然后保持列表将所有时间排序,这两个任务都是^ 2任务。
答案 3 :(得分:1)
在C ++ STL中,算法称为set_union。此外,如果将联合作为第3个列表,实现算法可能会简单得多。
答案 4 :(得分:1)
看起来像一个名叫Michael Heyeck的人对这个问题有一个很好的O(n) solution。查看该博客文章以获得解释和一些代码。
基本上,该解决方案在一次通过中跟踪主列表和从列表,并将索引跟踪到每个列表中。然后管理两个数据结构:要在从属列表上重放的插入列表,以及删除列表。
它看起来很简单,并且还有一个极简主义证明的好处,Heyeck在subsequent post中跟进了它。这篇文章中的代码片段也更加紧凑:
def sync_ordered_list(a, b):
x = 0; y = 0; i = []; d = []
while (x < len(a)) or (y < len(b)):
if y >= len(b): d.append(x); x += 1
elif x >= len(a): i.append((y, b[y])); y += 1
elif a[x] < b[y]: d.append(x); x += 1
elif a[x] > b[y]: i.append((y, b[y])); y += 1
else: x += 1; y += 1
return (i,d)
再次感谢Michael Heyeck。
答案 5 :(得分:0)
我过去在一个项目中遇到过这样的问题。
该项目有一个主数据源和几个独立更新数据的客户端,最后所有这些都必须拥有最新的统一数据集,这些数据是它们的总和。
我所做的是构建类似于SVN协议的东西,每次我想更新master数据库(可以通过Web服务访问)时,我都得到了修订号。将我的本地数据存储更新为该版本,然后将任何版本号未涵盖的实体提交到数据库。
每个客户都可以将其本地数据存储更新到最新版本。
答案 6 :(得分:0)
这是Michael Heyek的python代码的Javascript版本。
var b= [1,3,8,12,16,19,22,24,26]; // new situation
var a = [1,2,8,9,19,22,23,26]; // previous situation
var result = sync_ordered_lists(a,b);
console.log(result);
function sync_ordered_lists(a,b){
// by Michael Heyeck see http://www.mlsite.net/blog/?p=2250
// a is the subject list
// b is the target list
// x is the "current position" in the subject list
// y is the "current position" in the target list
// i is the list of inserts
// d is the list of deletes
var x = 0;
var y = 0;
var i = [];
var d = [];
var acc = {}; // object containing inserts and deletes arrays
while (x < a.length || y < b.length) {
if (y >= b.length){
d.push(x);
x++;
} else if (x >= a.length){
i.push([y, b[y]]);
y++;
} else if (a[x] < b[y]){
d.push(x);
x++;
} else if (a[x] > b[y]){
i.push([y, b[y]]);
y++;
} else {
x++; y++;
}
}
acc.inserts = i;
acc.deletes = d;
return acc;
}
答案 7 :(得分:-1)
非常蛮力和纯粹的技术方法:
从List类继承(抱歉不知道你的语言是什么)。覆盖子列表类中的添加/删除方法。使用您的班级而不是基础班级。现在,您可以使用自己的方法跟踪更改并在线同步列表。