在另一个更大的数组中查找数组

时间:2010-10-15 07:12:53

标签: java arrays

我最近被要求为一份工作编写3个测试程序。它们只使用核心Java API和我选择的任何测试框架编写。应在适当的时候实施单元测试。

虽然我根本没有收到任何反馈,但我认为他们不喜欢我的解决方案(否则我会听到他们的意见),所以我决定在这里展示我的程序,并询问这个实现是否可以被认为是好的,如果不是,为什么呢?

为了避免混淆,我现在只问第一个。

  

实现一个找到的函数   另一个更大的数组中的数组它   应该接受两个数组作为参数   它将返回该索引   第一个数组所在的第二个数组   首先完全发生。例如,   findArray([2,3,7,1,20],[7,1])应该   返回2.

我没有尝试找到任何现有的解决方案,而是想自己做。

可能的原因: 1.应该是静态的。 2.应该使用行注释而不是块注释。 3.没有先检查空值(我知道,发现太晚了)。 4.?

更新
提出了很多理由,我很难选择一个答案,因为很多答案都有很好的解决方案。正如@adietrich所提到的,我倾向于相信他们希望我展示核心API的知识(他们甚至要求编写函数,而不是编写算法)。

我认为确保工作的最佳方法是提供尽可能多的解决方案,包括: 1.使用Collections.indexOfSubList()方法实现,以表明我知道核心集合API。 2.使用强力方法实施,但提供更优雅的解决方案。 3.使用搜索算法实现,例如Boyer-Moore。 4.使用System.arraycopy()和Arrays.equal()的组合实现。然而,就性能而言,它不是最佳解决方案,它将显示我对标准数组例程的了解。

谢谢大家的回答!
END OF UPDATE。

这是我写的:

实际计划:

package com.example.common.utils;

/**
 * This class contains functions for array manipulations.
 * 
 * @author Roman
 *
 */
public class ArrayUtils {

    /**
     * Finds a sub array in a large array
     * 
     * @param largeArray
     * @param subArray
     * @return index of sub array
     */
    public int findArray(int[] largeArray, int[] subArray) {

        /* If any of the arrays is empty then not found */
        if (largeArray.length == 0 || subArray.length == 0) {
            return -1;
        }

        /* If subarray is larger than large array then not found */
        if (subArray.length > largeArray.length) {
            return -1;
        }

        for (int i = 0; i < largeArray.length; i++) {
            /* Check if the next element of large array is the same as the first element of subarray */
            if (largeArray[i] == subArray[0]) {

                boolean subArrayFound = true;
                for (int j = 0; j < subArray.length; j++) {
                    /* If outside of large array or elements not equal then leave the loop */
                    if (largeArray.length <= i+j || subArray[j] != largeArray[i+j]) {
                        subArrayFound = false;
                        break;
                    }
                }

                /* Sub array found - return its index */
                if (subArrayFound) {
                    return i;
                }

            }
        }

        /* Return default value */
        return -1;
    }

}

测试代码:

package com.example.common.utils;

import com.example.common.utils.ArrayUtils;

import junit.framework.TestCase;

public class ArrayUtilsTest extends TestCase {

    private ArrayUtils arrayUtils = new ArrayUtils();

    public void testFindArrayDoesntExist() {

        int[] largeArray = {1,2,3,4,5,6,7};
        int[] subArray = {8,9,10};

        int expected = -1;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

    public void testFindArrayExistSimple() {

        int[] largeArray = {1,2,3,4,5,6,7};
        int[] subArray = {3,4,5};

        int expected = 2;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

    public void testFindArrayExistFirstPosition() {

        int[] largeArray = {1,2,3,4,5,6,7};
        int[] subArray = {1,2,3};

        int expected = 0;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

    public void testFindArrayExistLastPosition() {

        int[] largeArray = {1,2,3,4,5,6,7};
        int[] subArray = {5,6,7};

        int expected = 4;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

    public void testFindArrayDoesntExistPartiallyEqual() {

        int[] largeArray = {1,2,3,4,5,6,7};
        int[] subArray = {6,7,8};

        int expected = -1;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

    public void testFindArrayExistPartiallyEqual() {

        int[] largeArray = {1,2,3,1,2,3,4,5,6,7};
        int[] subArray = {1,2,3,4};

        int expected = 3;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

    public void testFindArraySubArrayEmpty() {

        int[] largeArray = {1,2,3,4,5,6,7};
        int[] subArray = {};

        int expected = -1;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

    public void testFindArraySubArrayLargerThanArray() {

        int[] largeArray = {1,2,3,4,5,6,7};
        int[] subArray = {4,5,6,7,8,9,10,11};

        int expected = -1;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

    public void testFindArrayExistsVeryComplex() {

        int[] largeArray = {1234, 56, -345, 789, 23456, 6745};
        int[] subArray = {56, -345, 789};

        int expected = 1;
        int actual = arrayUtils.findArray(largeArray, subArray);

        assertEquals(expected, actual);
    }

}

15 个答案:

答案 0 :(得分:36)

“仅使用核心Java API”的要求也意味着他们想要看看你是否会重新发明轮子。因此,除了您自己的实现之外,您还可以提供单行解决方案,以确保安全:

public static int findArray(Integer[] array, Integer[] subArray)
{
    return Collections.indexOfSubList(Arrays.asList(array), Arrays.asList(subArray));
}

指出给定的示例包含无效的数组文字可能是也可能不是一个好主意。

答案 1 :(得分:4)

好吧,我的头顶:

  1. 是的,应该是静态的。

  2. 一家抱怨此事的公司不值得为之工作。

  3. 是的,但你会怎么做?返回?或抛出异常?它会像现在这样抛出异常。

  4. 我认为主要的问题是你的代码不是很优雅。内循环中的检查太多。冗余检查太多。

  5. 只是生硬,脱离我的头顶:

    public int findArray(int[] largeArray, int[] subArray) {
    
        int subArrayLength = subArray.length;
    
        if (subArrayLength == 0) {
            return -1;
        }
    
        int limit = largeArray.length - subArrayLength;
    
        int i=0;
    
        for (int i = 0; i <= limit; i++) {
            boolean subArrayFound = true;
    
            for (int j = 0; j < subArrayLength; j++) {
                if (subArray[j] != largeArray[i+j]) {
                    subArrayFound = false;
                    break;
                }
    
            /* Sub array found - return its index */
            if (subArrayFound) {
                return i;
            }
        }
    
        /* Return default value */
        return -1;
    }
    

    可以继续检查第一个元素,这样就不会有为数组中每个元素设置boolean和for循环的开销。然后你会看着

    public int findArray(int[] largeArray, int[] subArray) {
    
        int subArrayLength = subArray.length;
    
        if (subArrayLength == 0) {
            return -1;
        }
    
        int limit = largeArray.length - subArrayLength;
    
        int i=0;
    
        for (int i = 0; i <= limit; i++) {
            if (subArray[0] == largeArray[i]) {
                boolean subArrayFound = true;
    
                for (int j = 1; j < subArrayLength; j++) {
                    if (subArray[j] != largeArray[i+j]) {
                        subArrayFound = false;
                        break;
                    }
    
                /* Sub array found - return its index */
                if (subArrayFound) {
                    return i;
                }
            }
        }
    
        /* Return default value */
        return -1;
    }
    

答案 2 :(得分:4)

要在更大的整数数组中查找整数数组,可以使用与在较大字符串中查找子字符串相同的算法。为此,已知许多算法(参见Wikipedia)。特别是Boyer-Moore字符串搜索对于大型阵列是有效的。您尝试实现的算法效率不高(维基百科称之为'天真'实现)。

对于你的问题:

  1. 是的,这样的方法应该是静态的
  2. 不在乎,这是一个品味问题
  3. 可以包含空检查,或者您应该在JavaDoc中声明不允许空值,或者JavaDoc应该声明当任一参数为null时将抛出NullPointerException。

答案 3 :(得分:4)

Clean and improved code 

public static int findArrayIndex(int[] subArray, int[] parentArray) {
    if(subArray.length==0){
        return -1;
    }
    int sL = subArray.length;
    int l = parentArray.length - subArray.length;
    int k = 0;
    for (int i = 0; i < l; i++) {
        if (parentArray[i] == subArray[k]) {
            for (int j = 0; j < subArray.length; j++) {
                if (parentArray[i + j] == subArray[j]) {
                    sL--;
                    if (sL == 0) {
                        return i;
                    }

                }

            }
        }

    }
    return -1;
}

答案 4 :(得分:3)

以下是使用KMP模式匹配算法的方法。该解决方案需要O(n + m)。其中n =大数组的长度,m =子数组的长度。有关更多信息,请查看https://en.wikipedia.org/wiki/KMP_algorithm 蛮力需要O(n m)。我刚检查了Collections.indexOfSubList方法也是O(n m)。

public static int subStringIndex(int[] largeArray, int[] subArray) {
    if (largeArray.length == 0 || subArray.length == 0){
      throw new IllegalArgumentException();
}
    if (subArray.length > largeArray.length){
      throw new IllegalArgumentException();
}

    int[] prefixArr = getPrefixArr(subArray);
    int indexToReturn = -1;

    for (int m = 0, s = 0; m < largeArray.length; m++) {
      if (subArray[s] == largeArray[m]) {
        s++;
      } else {
        if (s != 0) {
          s = prefixArr[s - 1];
          m--;
        }
      }
      if (s == subArray.length) {
        indexToReturn = m - subArray.length + 1;
        break;
      }
    }

    return indexToReturn;
  }

  private static int[] getPrefixArr(int[] subArray) {
    int[] prefixArr = new int[subArray.length];
    prefixArr[0] = 0;

    for (int i = 1, j = 0; i < prefixArr.length; i++) {
      while (subArray[i] != subArray[j]) {
        if (j == 0) {
          break;
        }
        j = prefixArr[j - 1];
      }

      if (subArray[i] == subArray[j]) {
        prefixArr[i] = j + 1;
        j++;
      } else {
        prefixArr[i] = j;
      }

    }
    return prefixArr;
  }

答案 5 :(得分:1)

之前发布的一些优化代码:

public int findArray(byte[] largeArray, byte[] subArray) {
    if (subArray.length == 0) {
        return -1;
    }
    int limit = largeArray.length - subArray.length;
    next:
    for (int i = 0; i <= limit; i++) {
        for (int j = 0; j < subArray.length; j++) {
            if (subArray[j] != largeArray[i+j]) {
                continue next;
            }
        }
        /* Sub array found - return its index */
        return i;
    }
    /* Return default value */
    return -1;
}

答案 6 :(得分:0)

我建议进行以下改进:

  • 使函数保持静态,以便您可以避免创建实例
  • 外部循环条件可以是i <= largeArray.length-subArray.length,以避免在循环内进行测试
  • 删除多余的测试(largeArray[i] == subArray[0]

答案 7 :(得分:0)

int findSubArr(int[] arr,int[] subarr)
{
    int lim=arr.length-subarr.length;

    for(int i=0;i<=lim;i++)
    {
        int[] tmpArr=Arrays.copyOfRange(arr,i,i+subarr.length);
        if(Arrays.equals(tmpArr,subarr))
            return i;   //returns starting index of sub array
    }
    return -1;//return -1 on finding no sub-array   
}

<强>更新

通过重用相同的int数组实例:

int findSubArr(int[] arr,int[] subarr)
{
    int lim=arr.length-subarr.length;
    int[] tmpArr=new int[subarr.length];
    for(int i=0;i<=lim;i++)
    {
        System.arraycopy(arr,i,tmpArr,0,subarr.length);
        if(Arrays.equals(tmpArr,subarr))
          return i; //returns starting index of sub array
    }
    return -1;//return -1 on finding no sub-array   

}

答案 8 :(得分:0)

来自String的#indexOf:

/**
 * Code shared by String and StringBuffer to do searches. The
 * source is the character array being searched, and the target
 * is the string being searched for.
 *
 * @param   source       the characters being searched.
 * @param   sourceOffset offset of the source string.
 * @param   sourceCount  count of the source string.
 * @param   target       the characters being searched for.
 * @param   targetOffset offset of the target string.
 * @param   targetCount  count of the target string.
 * @param   fromIndex    the index to begin searching from.
 */
static int indexOf(char[] source, int sourceOffset, int sourceCount,
        char[] target, int targetOffset, int targetCount,
        int fromIndex) {
    if (fromIndex >= sourceCount) {
        return (targetCount == 0 ? sourceCount : -1);
    }
    if (fromIndex < 0) {
        fromIndex = 0;
    }
    if (targetCount == 0) {
        return fromIndex;
    }

    char first = target[targetOffset];
    int max = sourceOffset + (sourceCount - targetCount);

    for (int i = sourceOffset + fromIndex; i <= max; i++) {
        /* Look for first character. */
        if (source[i] != first) {
            while (++i <= max && source[i] != first);
        }

        /* Found first character, now look at the rest of v2 */
        if (i <= max) {
            int j = i + 1;
            int end = j + targetCount - 1;
            for (int k = targetOffset + 1; j < end && source[j]
                    == target[k]; j++, k++);

            if (j == end) {
                /* Found whole string. */
                return i - sourceOffset;
            }
        }
    }
    return -1;
}

答案 9 :(得分:0)

首先是您可能的原因:

  1. 是。以及final构造函数的类private
  2. 根本不应该使用这种评论。代码应该是不言自明的。
  3. 基本通过访问null字段隐式检查length,该字段将引发NullPointerException。只有在largeArray.length == 0subArray == null的情况下才能通过。
  4. 更多潜在原因:

    • 该类不包含任何数组操作的函数,与文档所说的相反。
    • 该方法的文档非常稀疏。它应该说明抛出的时间和异常(例如NullPointerException)以及如果找不到第二个数组或者它是空的则返回值。
    • 代码比需要的更复杂。
      1. 为什么第一个元素的相等性如此重要以至于它自己检查?
      2. 在第一个循环中,假设将找到第二个数组,这是无意的。
      3. 不需要的变量和跳转(booleanbreak),进一步降低了易读性。
      4. largeArray.length <= i+j不容易掌握。应该在循环之前检查,改善整个过程中的性能。
      5. 我会交换subArray[j] != largeArray[i+j]的操作数。对我来说似乎更自然。
      6. 一切都太久了。
    • 测试代码缺少更多边缘情况(null数组,第一个数组为空,两个数组都为空,第一个数组包含在第二个数组中,第二个数组包含多次等。)。
    • 为什么最后一个测试用例名为testFindArrayExistsVeryComplex

    缺少的练习是数组参数的组件类型的规范,分别是方法的签名。组件类型是基本类型还是引用类型会产生巨大差异。 The solution of adietrich假设一个引用类型(因此可以作为进一步的改进),我假设一个原始类型(int)。

    所以这是我的镜头,专注于代码/忽视文档和测试:

    public final class ArrayUtils {
        // main method
    
        public static int indexOf(int[] haystack, int[] needle) {
            return indexOf(haystack, needle, 0);
        }
    
        // helper methods
    
        private static int indexOf(int[] haystack, int[] needle, int fromIndex) {
            for (int i = fromIndex; i < haystack.length - needle.length; i++) {
                if (containsAt(haystack, needle, i)) {
                    return i;
                }
            }
            return -1;
        }
    
        private static boolean containsAt(int[] haystack, int[] needle, int offset) {
            for (int i = 0; i < needle.length; i++) {
                if (haystack[i + offset] != needle[i]) {
                    return false;
                }
            }
            return true;
        }
    
        // prevent initialization
    
        private ArrayUtils() {}
    }
    

答案 10 :(得分:0)

    byte[] arr1 = {1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 1, 3, 4, 56, 6, 7};
    byte[] arr2 = {9, 1, 3};

    boolean i = IsContainsSubArray(arr1, arr2);
 public static boolean IsContainsSubArray(byte[] Large_Array, byte[] Sub_Array){
    try {
        int Large_Array_size, Sub_Array_size, k = 0;

        Large_Array_size = Large_Array.length;
        Sub_Array_size = Sub_Array.length;

        if (Sub_Array_size > Large_Array_size) {
            return false;
        }
        for (int i = 0; i < Large_Array_size; i++) {
            if (Large_Array[i] == Sub_Array[k]) {
                k++;
            } else {
                k = 0;
            }
            if (k == Sub_Array_size) {
                return true;
            }
        }
    } catch (Exception e) {
    }
    return false;
}

答案 11 :(得分:0)

来自番石榴的代码:

import javax.annotation.Nullable;

/**
 * Ensures that an object reference passed as a parameter to the calling method is not null.
 *
 * @param reference an object reference
 * @param errorMessage the exception message to use if the check fails; will be converted to a
 *     string using {@link String#valueOf(Object)}
 * @return the non-null reference that was validated
 * @throws NullPointerException if {@code reference} is null
 */
public static <T> T checkNotNull(T reference, @Nullable Object errorMessage) {
    if (reference == null) {
        throw new NullPointerException(String.valueOf(errorMessage));
    }
    return reference;
}


/**
 * Returns the start position of the first occurrence of the specified {@code
 * target} within {@code array}, or {@code -1} if there is no such occurrence.
 *
 * <p>More formally, returns the lowest index {@code i} such that {@code
 * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly
 * the same elements as {@code target}.
 *
 * @param array the array to search for the sequence {@code target}
 * @param target the array to search for as a sub-sequence of {@code array}
 */
public static int indexOf(int[] array, int[] target) {
    checkNotNull(array, "array");
    checkNotNull(target, "target");
    if (target.length == 0) {
        return 0;
    }

    outer:
    for (int i = 0; i < array.length - target.length + 1; i++) {
        for (int j = 0; j < target.length; j++) {
            if (array[i + j] != target[j]) {
                continue outer;
            }
        }
        return i;
    }
    return -1;
}

答案 12 :(得分:0)

我会以三种方式做到这一点:

  1. 不使用任何导入,即使用纯Java语句。

  2. 在某种程度上或某些程度上使用JAVA核心API。

  3. 使用KMP等字符串模式搜索算法(可能是最优化的一种)。

1,2和3都在上面的答案中显示。这是我身边的方法2:

public static void findArray(int[] array, int[] subArray) {

        if (subArray.length > array.length) {
            return;
        }

        if (array == null || subArray == null) {
            return;
        }

        if (array.length == 0 || subArray.length == 0) {
            return;
        }

        //Solution 1
        List<Integer> master = Arrays.stream(array).boxed().collect(Collectors.toList());
        List<Integer> pattern = IntStream.of(subArray).boxed().collect(Collectors.toList());

        System.out.println(Collections.indexOfSubList(master, pattern));

        //Solution2
        for (int i = 0; i <= array.length - subArray.length; i++) {
            String s = Arrays.toString(Arrays.copyOfRange(array, i, i + subArray.length));

            if (s.equals(Arrays.toString(subArray))) {
                System.out.println("Found at:" + i);
                return;
            }
        }
        System.out.println("Not found.");
    }

答案 13 :(得分:0)

使用Java 8和lambda表达式:

String[] smallArray = {"1","2","3"};
final String[] bigArray = {"0","1","2","3","4"};
boolean result = Arrays.stream(smallArray).allMatch(s -> Arrays.stream(bigArray).anyMatch(b -> b.equals(s)));

PS:具有 final String[] bigArray来封闭lambda表达空间很重要。

答案 14 :(得分:0)

仅供参考:如果目标只是搜索数组 y 是否是数组 x 的子集,我们可以使用:

val x = Array(1,2,3,4,5)
val y = Array(3,4,5)
val z = Array(3,4,8)
x.containsSlice(y) // true
x.containsSlice(z) // false