在另一个较大的数组中查找数组
最近我被要求写一个工作3个testing程序。 他们将只使用核心Java API和我select的任何testing框架来编写。 unit testing应在适当的地方执行。
虽然我还没有收到任何反馈,但我想他们不喜欢我的解决scheme(否则我会听到他们的),所以我决定在这里展示我的程序,询问这个实现是否可以被认为是好的,如果不是,那为什么?
为了避免混淆,我现在只会问第一个。
实现一个在另一个更大的数组中find数组的函数。 它应该接受两个数组作为参数,它将返回第一个数组第一个完全出现的第一个数组的索引。 例如,findArray([2,3,7,1,20],[7,1])应该返回2。
我没有试图find任何现有的解决scheme,而是想自己做。
可能的原因:1.应该是静态的。 2.应该使用行注释,而不是块注释。 3.没有先检查空值(我知道,只是发现太晚了)。 4.?
更新 :
有很多原因已经提出,我很难select一个答案,因为许多答案都有一个好的解决scheme。 正如@adietrich所提到的,我倾向于相信他们希望我展示核心API的知识(他们甚至要求编写一个函数,而不是写一个algorithm)。
我相信确保工作的最好方法是尽可能多的提供解决scheme,包括:1.使用Collections.indexOfSubList()方法实现,以显示我知道核心集合API。 2.使用暴力方法实施,但提供更优雅的解决scheme。 3.使用searchalgorithm实现,例如Boyer-Moore。 4.使用System.arraycopy()和Arrays.equal()的组合来实现。 然而,在性能方面不是最好的解决scheme,它会显示我对标准数组例程的知识。
谢谢大家的回答!
更新结束。
这是我写的:
实际计划:
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; } }
testing代码:
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); } }
“只使用核心Java API”的要求也意味着他们想看看你是否会重新发明。 所以除了自己的实现之外,你可以给出单线解决scheme,只是为了安全起见:
public static int findArray(Integer[] array, Integer[] subArray) { return Collections.indexOfSubList(Arrays.asList(array), Arrays.asList(subArray)); }
指出给出的示例包含无效的数组文字可能是也可能不是一个好主意。
为了在更大的整数数组中find整数数组,可以使用与查找更大的string中的子串相同的algorithm。 为此有许多algorithm已知(见维基百科 )。 特别是Boyer-Moorestringsearch对于大型arrays来说是高效的。 你试图实现的algorithm效率不高(维基百科称这是“天真”的实现)。
对于你的问题:
- 是的,这样的方法应该是静态的
- 不在乎,这是品味的问题
- 可以包含null检查,或者您应该在JavaDoc中声明不允许使用null值,或者JavaDoc应该声明当任何参数为null时,将引发NullPointerException。
那么,从我的头顶上:
-
是的,应该是静态的。
-
抱怨这个公司不值得为之工作。
-
是的,但是你会怎么做? 返回? 或抛出exception? 它会抛出一个exception,它已经是这样了。
-
我认为主要的问题是你的代码不是很优雅。 内循环中检查太多。 太多的重复检查。
只是生,离我的头顶:
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; }
你可以保留第一个元素的检查,所以你没有为数组中的每个元素设置布尔值和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; }
以下是使用KMP模式匹配algorithm的一种方法。 这个解决scheme需要O(n + m)。 其中n =大arrays长度,m =子arrays长度。 有关更多信息,请查看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; }
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; }
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 }
以前发布的一点优化代码:
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; }
我会build议以下改进:
- 使函数静态,以便您可以避免创build一个实例
- 外部循环条件可能是
i <= largeArray.length-subArray.length
,以避免循环内的testing - 删除
largeArray[i] == subArray[0]
的testing(largeArray[i] == subArray[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; }
首先给你可能的原因:
- 是。 而类
final
与一个private
构造函数。 - 根本不应该使用这种评论。 代码应该是不言自明的。
- 你基本上隐式检查
null
通过访问将引发NullPointerException
的length
字段。 只有在largeArray.length == 0
和subArray == null
的情况下,才会滑过。
更多潜在的原因:
- 这个类不包含数组操作的任何函数,与文档所说的相反。
- 该方法的文档非常稀疏。 它应该说明何时抛出哪些exception(例如
NullPointerException
)以及如果没有find第二个数组或者它是否为空,那么返回值是期望的。 - 代码比需要更复杂。
- 为什么第一个要素的平等性如此重要,以至于得到自己的支票呢?
- 在第一个循环中,假定第二个数组将被find,这是无意的。
- 不需要的variables和跳转(
boolean
和break
),进一步减less易读性。 -
largeArray.length <= i+j
不容易把握。 应该在循环前检查,提高一路上的performance。 - 我会交换
subArray[j] != largeArray[i+j]
的操作数。 对我来说似乎更自然。 - 总之太久了。
- testing代码缺less更多的边缘情况(
null
数组,第一个数组为空,两个数组都是空的,第一个数组包含在第二个数组中,第二个数组包含多次等)。 - 为什么最后一个名为
testFindArrayExistsVeryComplex
?
缺less的练习是数组参数的组件types的规范,分别是方法的签名。 无论组件types是原始types还是引用types,都会产生巨大的差异。 adietrich的解决scheme假设一个引用types(因此可以被进一步改进),我的假设是一个原始types( int
)。
所以这里是我的注意力,集中在代码/无视文档和testing:
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() {} }
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; }
番石榴代码:
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; }