【问题标题】:What is the most efficient pattern/algorithm to compare two lists and find the delta between those two lists?比较两个列表并找到这两个列表之间的增量的最有效的模式/算法是什么?
【发布时间】:2011-04-10 21:52:51
【问题描述】:

我们有两个列表,比如说学生和他们的分数。我想比较这两个列表并找到新列表和旧列表之间的增量,然后找到将任何更改插入或更新到新列表中的侵入性最小的方法。解决这个问题的最佳算法是什么?希望专注于对新列表和性能的最小更改。

示例代码:

List<ListItem> existingList = new List<ListItem>();
List<ListItem> newList = new List<ListItem>();

public TopLists()
{
  InitTwoLists();
}

private void InitTwoLists()
{
  existingList.Add(new ListItem { Name = "Shane", Score = 100 });
  existingList.Add(new ListItem { Name = "Mark", Score = 95 });
  existingList.Add(new ListItem { Name = "Shane", Score = 94 });
  existingList.Add(new ListItem { Name = "Steve", Score = 90 });
  existingList.Add(new ListItem { Name = "Brian", Score = 85 });
  existingList.Add(new ListItem { Name = "Craig", Score = 85 });
  existingList.Add(new ListItem { Name = "John", Score = 82 });
  existingList.Add(new ListItem { Name = "Steve", Score = 81 });
  existingList.Add(new ListItem { Name = "Philip", Score = 79 });
  existingList.Add(new ListItem { Name = "Peter", Score = 70 });

  newList.Add(new ListItem { Name = "Shane", Score = 100 });
  newList.Add(new ListItem { Name = "Steve", Score = 96 });  // This is change
  newList.Add(new ListItem { Name = "Mark", Score = 95 });
  newList.Add(new ListItem { Name = "Shane", Score = 94 });
  newList.Add(new ListItem { Name = "Brian", Score = 85 });
  newList.Add(new ListItem { Name = "Craig", Score = 85 });
  newList.Add(new ListItem { Name = "John", Score = 82 });
  newList.Add(new ListItem { Name = "Steve", Score = 81 });
  newList.Add(new ListItem { Name = "Philip", Score = 79 });
  newList.Add(new ListItem { Name = "Peter", Score = 70 });
}
}

public void CompareLists()
{
  // How would I find the deltas and update the new list with any changes from old?
}
}

public class ListItem
{
  public string Name { get; set; }
  public int Score { get; set; }
}

** 编辑:所需的输出 ***

所需的输出是用增量实际更改 newList。 例如在这种情况下:

newList.Add(new ListItem { Name = "Shane", Score = 100 });
  newList.Add(new ListItem { Name = "Steve", Score = 96 });  // This is change
  newList.Add(new ListItem { Name = "Mark", Score = 95 });
  newList.Add(new ListItem { Name = "Shane", Score = 94 });
  newList.Add(new ListItem { Name = "Brian", Score = 85 });
  newList.Add(new ListItem { Name = "Craig", Score = 85 });
  newList.Add(new ListItem { Name = "John", Score = 82 });
  newList.Add(new ListItem { Name = "Steve", Score = 81 });
  newList.Add(new ListItem { Name = "Roger", Score = 80 });  // Roger is a new entry
  newList.Add(new ListItem { Name = "Phillip", Score = 79 });  // Philip moved down one

// Peter 以 70 分从这个列表中掉了出来,因为我只想要前 10 名。

因此更改将是:

更新“史蒂夫”的记录 2,分数发生了变化 在位置 9 插入新记录“Roger” 将“彼得”的记录从前 10 名中删除。

【问题讨论】:

  • 您在寻找通用解决方案吗?或者是否存在某些限制,例如列表的特定排序顺序?
  • 我们应该假设列表的大小相同吗?您是否还想在列表 A 中查找不在列表 B 中的成员,反之亦然?
  • 通用解决方案。列表总是相等的。排序顺序始终是分数降序的排序。
  • 结果应该是什么格式?
  • 我不明白。你有一个旧列表和新列表。您想查找增量、应用到旧列表、获取新列表吗?输出是从旧列表再次构建的新列表并找到增量?

标签: c# algorithm computer-science


【解决方案1】:

你可以使用 Linq:

List<ListItem> list1 = GetYourList1();
List<ListItem> list2 = GetYourList2();
var diff = list1.Except(list2);

你的具体例子:

var diff = newList.Except(existingList);

不确定它是否是最有效的,但它很简洁:)

【讨论】:

  • 但是,OP 要求的是算法,而不是现有的解决方案。
  • 您可能需要尝试一下,理论上它应该可以工作,但实际上它取决于 ListItem 如何实现相等比较器。创建一个 Student 类并覆盖 Equals 可能会更好。
  • list2 中但 list1 中没有的项目怎么样?我认为这种方式只检测新添加的元素。此外,还需要为IEquatable&lt;T&gt;接口实现一个合适的实现。
  • @Oded:标题写着“模式/算法”。我认为 LINQ 是一种模式。
  • @Dave Swersky - LINQ 是一组查询语言扩展,而不是一种模式。
【解决方案2】:

如果您正在寻找通用的、与语言无关的解决方案,那么您正在寻找某种data synchronization 的有序列表。基本算法是:

i1 = 0
i2 = 0
while (i1 < list1.size && i2 < list2.size)
{
  if (list1[i1] < list2[i2])
  {
    //list1[i1] is not in list2
    i1++
  }
  else if (list1[i1] > list2[i2])
  {
    //list2[i2] is not in list1
    i2++
  }
  else
  {
    //item is in both lists
    i1++
    i2++
  }
}
if (i1 < list1.size)
   //all remaining list1 items are not in list2
if (i2 < list2.size)
   //all remaining list2 items are not in list1

【讨论】:

  • 这看起来更像是集合差异(基于排序列表)而不是有序列表差异:您丢失了订单信息。
【解决方案3】:

如果您的列表中没有两次相同的名字,这应该可以解决问题。在您的示例中,您有 2 个 Steve,但您需要一种方法来区分它们。

public static List<ListItem> CompareLists(List<ListItem> existingList, List<ListItem> newList)
{
    List<ListItem> mergedList = new List<ListItem>();
    mergedList.AddRange(newList);
    mergedList.AddRange(existingList.Except(newList, new ListItemComparer()));
    return mergedList.OrderByDescending(x => x.Score).Take(10).ToList();
}

public class ListItemComparer : IEqualityComparer<ListItem>
{
    public bool Equals(ListItem x, ListItem y)
    {
        return x.Name == y.Name;
    }

    public int GetHashCode(ListItem obj)
    {
        return obj.Name.GetHashCode();
    }
}

你可以这样称呼它:

newList = CompareLists(existingList, newList);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 2016-05-17
    • 2013-06-16
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    相关资源
    最近更新 更多