繁体   English   中英

C# 以资源高效的方式比较列表

[英]C# compare lists in a resource efficient way

如果 list2 中的字符串与 list1 中的字符串匹配,则此代码有效,并且当涉及到较大的列表时,它具有时间效率

var firstNotSecond = list1.Except(list2).ToList();

但是我正在尝试调整它,以便在 list2 中的字符串包含 list1 中的字符串时它会起作用。 所以说如果:

--list2 字符串:

"john,sally,michael"
"tim,sally,andrew"
"stuart,bill,tom"

--list1 字符串:

"sally"
"joe"

--final list 将只包含 list2 中的最后一个字符串作为前两个.Contains

"stuart,bill,tom"

我正在寻找最节省时间/资源的方法来做到这一点 - 基本上有两个嵌套循环(有或没有 LINQ)的直接实现是 O(len(list1) * len(list2) ) - 理想情况下我想要同O(MAX(LEN(列表1),LEN(列表2)))作为.Except ,但任何优于基本N ^ 2是值得欢迎的。

这是一个两个班轮,沿着 Optional Option 的想法:

var hash = new HashSet<string>(list1);
var reducedList2 = list2.Where(s => s.Split(',').All(e => !hash.Contains(e))).ToList();
  • 将 sally 和 joe 放入查找中

  • 枚举 csv 字符串列表,如果 csv 中的任何元素在查找中,则拆分它们并拒绝它们(此处实现为“如果所有元素都不在查找中,则接受它们”)

我认为,很少有不可避免的(除非你想改变你的数据存储)讨厌的东西。 如果您最后并不真正需要 List 并且可以使用可枚举,请删除 ToList 以避免昂贵的复制操作

通过成为具有字符串数组的节点的链接列表而不是 csv 的字符串列表,它可能会好得多 - 没有拆分和有效删除节点。 在内部,您当前的存储解决方案将进行大量列表大小调整和数据改组

您可以在项目list1 Where All的项目list2由不包含list1项使用System.Linq扩展方法:

var final = list1.Where(l1 => list2.All(l2 => !l1.Contains(l2))).ToList();

如果要进行不区分大小写的比较,可以使用IndexOf方法以及StringComparison.OrdinalIgnoreCase (如果未找到该项目,则返回-1 ):

var final = list1.Where(l1 => list2.All(l2 =>
    l1.IndexOf(l2, StringComparison.OrdinalIgnoreCase) < 0))
    .ToList();

您可以拆分l2并将其转换为HashSets ,因此.Contains将具有复杂度O(1) ,并且整个算法将只有O(n)

public static List<string> ListExcept(this List<string> l1, List<string> l2)
{
    IEnumerable<HashSet<string>> listOfHashes = l1.Select(x => x.Split(',').ToHashSet());
    IEnumerable<HashSet<string>> finalLists = listOfHashes.Where(hash => l2.All(x => !hash.Contains(x)));
    IEnumerable<string> joined = finalLists.Select(x => x.Aggregate("", (s, s1) => s + s1 + ",")[..^1]);

    return joined.ToList();
}

显然你可以加入调用,我将它们分开并明确指定类型以使其更清晰。

然后你可以把它称为:

l2.ListExcept(l1);

该过程本质上必须是 O(nm),因为每个项目都必须与其他项目进行比较。 如果您确实需要对非常大的字符串集进行高效处理,那么有一种方法可以构建特里树并遍历。

无需构建 trie,您可以使用更简单的字典进行查找。

通过使用 O(1) 查找匹配项,这将在更简单的方法中减少一些过程,使其更多的 O(list2.length*list1.length) 而不必比较每个值。 这只会在列表较长且 O(1) 查找有帮助的情况下提高性能。

var list2Dict= list2.Select(l => l.ToDictionary( k => k, v => 0) ).ToList();
var result = list2.Where((_,i) => !list1.Any(l => list2Dict[i].ContainsKey(l))).ToList();

在此处查看: https : //dotnetfiddle.net/19gOCZ

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM