【问题标题】:How to find index of sublist in list?如何在列表中查找子列表的索引?
【发布时间】:2010-08-20 09:41:48
【问题描述】:

我正在寻找一些有效的方法(在 .NET 中),如何查找某个字节列表中是否有字节序列,如果有,则索引第一个开始的位置。

例如,假设我有:

var sequence = new List<byte> { 5, 10, 2 };
var listOne = new List<byte> { 1, 3, 10, 5, 10, 2, 8, 9 };
var listTwo = new List<byte> { 1, 3, 10, 5, 2, 10, 8, 9 };

结果应该是我的序列在 listOne 的索引 3 和 listTwo 的索引 -1(即它不存在)上。

当然,如果后面的数字与我的序列匹配,我可以通过 int 和每个索引循环遍历列表 int 并搜索,但是有没有更有效的方法(例如使用扩展方法)?

【问题讨论】:

  • 当然,如果列表未排序,您将不得不遍历每个项目直到找到序列?使用扩展方法或 Linq 并不能神奇地提高效率。
  • 我相当怀疑是否有一些.NET 库具有这种类型的扩展。但是您可以创建自己的。
  • 我必须补充一点,我的序列相当短(数量很少),但我要搜索的列表很长(数千个项目)

标签: c# .net


【解决方案1】:

这与子字符串搜索本质上是相同的问题(实际上,顺序很重要的列表是“字符串”的概括)。

幸运的是,计算机科学长期以来经常考虑这个问题,所以你可以站在巨人的肩膀上。

看看文献。一些合理的起点是:

http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm

http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm

http://en.wikipedia.org/wiki/Rabin-karp

即使只是维基百科文章中的伪代码,也足以轻松移植到 C#。查看不同情况下的性能描述,并确定您的代码最有可能遇到哪些情况。 (我是从你所说的关于搜索键列表很短的内容中考虑的第一个)。

【讨论】:

  • 感谢您提供的链接,我只是想知道,是否有任何方法在 .NET 中使用其中一些算法实现,以节省我的时间,然后再自己实现它们。
  • System.String.IndexOf 很可能实现了其中之一!您将相同的算法应用于它不常用的数据类型会减少您找到 impl 的机会。我敢肯定在某个地方有一个,但找到它是另一回事。
【解决方案2】:

我认为最简洁的方法是创建一个这样的通用扩展方法:

public static int SubListIndex<T>(this IList<T> list, int start, IList<T> sublist)
{
    for (int listIndex = start; listIndex < list.Count - sublist.Count + 1; listIndex++)
    {
        int count = 0;
        while (count < sublist.Count && sublist[count].Equals(list[listIndex + count]))
            count++;
        if (count == sublist.Count)
            return listIndex;
    }
    return -1;
}

以这种方式调用:

var indexOne = listOne.SubListIndex(0, sequence);
var indexTwo = listTwo.SubListIndex(0, sequence);

附: 如果您需要搜索更多的子列表,也可以从给定的索引开始

【讨论】:

  • 这正是我现在正在做的。但正如 Jon Hanna 所说,有更有效的子集搜索方法。我只是想知道我是否没有遗漏 .NET 中的某些内容。
  • IMO 那些算法不容易适用于非字符字符串。例如,boyer-moore 需要一个字母大小的数组,而 Int32 的字母大小为 2^32。 Rabin-karp 使用散列,对于非真实字符串可能很难实现。我猜可能唯一真正可用的是 Knuth-Morris-Pratt,但我认为不会这么快......
  • 不错且简单的解决方案。您可以将其增强为 IEnumerable 并使用 Count() 代替 Count 和 ElementAt(...) 代替 [...]。对于 List,Enumerable 使用 IList 实现,因此没有太多开销。 sublist.Count 的变量还可以节省一些纳秒...
【解决方案3】:

我建议将每个List&lt;int&gt; 转换为String,然后使用String.IndexOf(sequence) 进行搜索以确定序列的位置或是否存在。

【讨论】:

  • 嗯我真的怀疑这会提高效率,因为你必须从列表中创建字符串(使用更多的内存和更多的计算)。当然这会让事情变得更容易,因为您不需要编写用于搜索子字符串的代码。
  • 我也担心效率问题。但它肯定会更短,也许可读。
  • 如果您创建一个扩展方法并对其进行描述,它也将简短易读,而不是使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 2022-01-10
  • 2016-06-11
  • 2014-10-13
相关资源
最近更新 更多