【问题标题】:How to search an array with array?如何用数组搜索数组?
【发布时间】:2011-09-08 08:25:02
【问题描述】:

我有 2 字节数组:

    Dim A() As Byte = {1, 2, 3, 4, 5, 6, 7, 8, 9}
    Dim B() As Byte = {5, 6, 7}

现在我想找出A中完整B的出现。我尝试了 Array.IndexOf(A, B) 没有运气。有没有一种简单的方法来逐个数组搜索而不需要使用任何循环?

它应该以与 B() 相同的顺序找到 5,6,7 的索引(位置)。 如果 A() 包含 {1,2,3,4,7,6,5,9} 它应该返回 false 或 -1,因为它们的顺序不同。

【问题讨论】:

  • “没有任何循环”是不可能的。在没有循环的情况下,您不能从数组中获取多个值,因为数组可以是任意大小。您的意思是代码中没有显式循环吗?
  • @Stefan,谢谢!这很快而且效果很好。 :)

标签: c# .net vb.net arrays


【解决方案1】:

以下 Linq 语句将给出一个 IEnumerable<int>,其中包含 b 在 a 中的位置(如果没有出现,则为空集):

Enumerable
    .Range( 0, 1 + a.Length - b.Length )
    .Where( i => a.Skip(i).Take(b.Length).SequenceEqual(b) );

我不知道如何翻译成 VB.NET。

【讨论】:

  • 这个方法看起来不错,但是速度很慢。 byte() 中的 100000 个元素需要 16 秒,但它适用于较小的尺寸。长度也应该是 +1(至少在 VB.NET 中),因为如果元素位于列表的末尾,它们将不会被找到,这里是更正的版本: Enumerable.Range(0, A.Length - B.Length + 1).Where(Function(i) A.Skip(i).Take(B.Length).SequenceEqual(B))
  • @qxxx:你说得对。固定的。肯定有更快的方法可以做到这一点,即 Knuth-Morris-Pratt,但我对(显式)循环要求感到困惑。
【解决方案2】:

这可能有效,但它是 C# 并使用循环:

private static int[] GetIndicesOf(byte[] needle, byte[] haystack)
{
    int[] foundIndices = new int[needle.Length];

    int found = 0;

    for (int i = 0; i < haystack.Length; i++)
    {

        if (needle[found] == haystack[i])
        {
            foundIndices[found++] = i;

            if (found == needle.Length)
                return foundIndices;
        }
        else
        {
            i -= found;   // Re-evaluate from the start of the found sentence + 1
            found = 0;    // Gap found, reset, maybe later in the haystack another occurrance of needle[0] is found
            continue;
        }
    }

    return null;            
}

用输入测试:

Byte[] haystack = { 5, 6, 7, 8, 9, 0, 5, 6, 7 };
Byte[] needle = { 5, 6, 7 };
// Returns {0, 1, 2}

Byte[] haystack = { 5, 6, 0, 8, 9, 0, 5, 6, 7 };
Byte[] needle = { 5, 6, 7 };
// Returns {6, 7, 8}

Byte[] haystack = { 5, 6, 0, 7, 9, 0, 5, 6, 8 };
Byte[] needle = { 5, 6, 7 };
// Returns null

Byte[] haystack = { 1, 2, 1, 2, 2 };
Byte[] needle = { 1, 2, 2 };
// Returns {2, 3, 4}

Byte[] haystack = { 1, 2, 1, 2, 1, 2, 3 };
Byte[] needle = { 1, 2, 1, 2, 3 };
// Returns {2, 3, 4, 5, 6}

Byte[] haystack = { 1, 1, 1, 1, 2 };
Byte[] needle = { 1, 2 };
// Returns {3, 4}

但是@spender 的 Linq 实现看起来更好。 :-P

【讨论】:

  • 对于 heystack = {1, 2, 1, 2, 2} 和​​ needle = {1, 2, 2},这显然是错误的。如果你每次都回溯 needle.length,它可能会起作用。
  • 这对于我需要的东西来说非常快,但没有用 Gleno 的问题对其进行测试
  • @Gleno 不正确,请参阅我的第二个测试。
  • @CodeCaster,在我的示例中存在重叠。试试看吧。
  • @CodeCaster,是的,看起来更好。仍然,单元测试的好论据。 :)
【解决方案3】:

如何创建一个方法:

  1. 将搜索列表的元素连接成一个字符串
  2. 将要搜索的列表元素连接到一个字符串
  3. 在第一个字符串中查找第二个字符串的存在

像这样:

public bool IsSubSetOf(IList<int> list1, IList<int> list2){
   var string1 = string.Join("", list1);
   var string2 = string.Join("", list2);
   return string1.Contains(string2);
}

未测试...

【讨论】:

  • 这对于 {1, 11, 111, 11, 11} 和寻找 {1, 1, 1} 来说是错误的。
  • 这是个好主意(没有显式循环!),但在很多情况下会中断。您可能需要为每个值添加分隔符围绕(也在第一个值之前和最后一个值之后)以使其完美运行,这比string.Join 可以提供的稍微复杂一些。您还需要返回一个索引,而不仅仅是一个布尔值 - OP 说“查找”而不是“确定是否”。这个答案可以扩展到工作,但我不会把它作为练习留给读者:)
【解决方案4】:

一般来说,解决此问题的一种有效方法是KMP algorithm。快速谷歌搜索建议可以找到一个 .NET 实现 here。它的实现伪代码可从Wikipedia 获得。

以上链接之一介绍了一种低效但无害且易于编码的方式,如下所示:

int[] T = new[]{1, 2, 3, 4, 5};
int[] P = new[]{3, 4};

for (int i = 0; i != T.Length; i++)
{
    int j = 0
    for (;i+j != T.Length && j != P.Length && T[i+j]==P[j]; j++);
    if (j == P.Length) return i;
}

【讨论】:

    【解决方案5】:

    我的看法是:

    public static int Search<T>(T[] space, T[] searched) {
      foreach (var e in Array.FindAll(space, e => e.Equals(searched[0]))) {
        var idx = Array.IndexOf(space, e);
        if (space.ArraySkip(idx).Take(searched.Length).SequenceEqual(searched))
          return idx;
      }
      return -1;
    }
    
    public static class Linqy {
      public static IEnumerable<T> ArraySkip<T>(this T[] array, int index) {
        for (int i = index;  i < array.Length; i++) {
          yield return array[i];
        }
      }
    }
    

    与往常一样,这是否“足够好”取决于您的数据,或者您将不得不求助于更复杂但更有效的算法。我介绍了一个 arrayskip,因为 Linq 跳过确实只假设 IEnumerable 接口并且会枚举到索引。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-27
      • 1970-01-01
      • 2012-09-16
      • 2011-09-12
      • 2015-07-02
      相关资源
      最近更新 更多