My assignment for school is to implement a method that checks if a given ArrayList is part of the Fibonacci sequence.
The array must not be empty and must be bigger than 3.
I understood that I have to check if one number of the array and the next one are part of the Fibonacci sequence, however I have a lot of trouble with it since you're supposed to accept the array if it's any part of the sequence and not just from the start.
e.g.: 0 1 1 2 3 5 will be accepted as well as 2 3 5 8 13 21.
This is my code so far. I know it's very flawed but i really have no clue how to move on.
public class ArrayCheck {
/**
* Tests if the given array is a part of the Fibonacci sequence.
*
* #param arr array to be tested
* #return true if the elements are part of the fibonacci sequence
*/
public boolean isFibonacci(ArrayList<Integer> arr) {
//check if array exists
if(arr.size() == 0)
return false;
//check if array is bigger than 3
if (arr.size() < 3)
return false;
//check for the startsequence of 0,1,1
else if(arr.get(0) == 0 && arr.get(1) == 1 && arr.get(2) == 1)
return true;
//check every number in array
for(int i = 0; i < arr.size(); i++) {
//check if i >= 2 is fib
if(i >= 2) {
int fibn = i;
int nextfib = i + 1;
int fibnew = (fibn - 1) + (fibn - 2);
int fibnext = (nextfib - 1) + (nextfib - 2);
if (arr.get(i) != fibnew && arr.get(i + 1) != fibnext)
return false;
}
//check if the order is right
if(arr.get(i) > arr.get(i+1))
return false;
}
return true;
}
Any help is greatly appreciated!
Well, you have a few issues with your code. First of all, if you array is at least 3 items, you check if only the first three are the start of the Fibonacci sequence:
//check for the startsequence of 0,1,1
else if(arr.get(0)==0 && arr.get(1)==1 && arr.get(2)==1){
return true;
}
This is bad, as this mean 0 1 1 5 which is not part of the sequence will return true.
What you need to do is split this into two tasks:
Find the first relevant number in the sequence (i.e. if the array starts with 7, you know this isn't a part of the sequence; alternatively, if it starts with 8, you know you need to start checking from 8 onward).
Once you've found the "start", simply check that the rest of the array follows the Fibonacci rule. you'll need to manually verify the first two items.
public boolean isFibonacci(ArrayList<Integer> arr) {
if (arr.size() < 3){
return false;
}
/** find if the first element is part of the sequence: **/
int fib1 = 0;
int fib2 = 1;
while (fib1 < arr.get(0)) {
int tmp = fib1 + fib2;
fib1 = fib2;
fib2 = tmp;
}
if (fib1 != arr.get(0)) {
// first element is not part of Fibonacci sequence
return false;
}
if (fib2 != arr.get(1)) {
// the first two elements are not part of the Fibonacci sequence
return false;
}
/*** now simply verify that the rest of the elements uphold the rule:
each element is the sum of the two previous ones: **/
for(int i=2; i < arr.size(); i++) {
// make sure there are no negatives in the array:
if (arr.get(i) < 0)
return false;
if (arr.get(i) != (arr.get(i-1) + arr.get(i-2)))
return false;
}
//everything checks out okay - return true:
return true;
}
private boolean isFib(final List<Integer> li) {
//check if each int is the sum of the two prior ints
for (int i = 2; i < li.size(); i++) {
if (li.get(i) != li.get(i - 1) + li.get(i - 2)) {
return false;
}
}
//reverse the fibonacci sequence and check if we end up at the correct starting point (0, 1)
int i1 = li.get(0);
int i2 = li.get(1);
while (i1 > 0) {
final int tmp = i1;
i1 = i2 - i1;
i2 = tmp;
}
return i1 == 0 && i2 == 1;
}
I'd suggest a solution which abstracts Fibonacci sequence generator in a separate Iterator<Integer>, then uses it to check if provided list matches any part of the sequence.
Iterator is quite simple and straightforward:
public static class FiboIterator implements Iterator<Integer> {
#Override
public boolean hasNext() { return true; }
int i = -1, j = -1; // previous two items of Fibo sequence
#Override
public Integer next() {
int k = (i < 0) ? (j < 0 ? 0 : 1) : (i + j);
i = j;
j = k;
return k;
}
}
Main checking method:
public static boolean isFibo(List<Integer> seq) {
if (seq.size() < 3)
return false;
final Iterator<Integer> f = new FiboIterator();
int start = seq.get(0), k;
while ((k = f.next()) < start); // roll Fibo to match the starting item in input
if (start != k) // starting item doesn't match
return false;
if (start == 1 && seq.get(1) != 1) // special case: [1, 2, ...]
f.next();
for (int i = 1; i < seq.size(); i++) { // check if other items match
if (seq.get(i) != f.next())
return false;
}
return true;
}
And finally a few unit tests:
#Test
public void testFibo() {
assertTrue(isFibo(Arrays.asList(0, 1, 1, 2)));
assertTrue(isFibo(Arrays.asList(1, 1, 2, 3, 5)));
assertTrue(isFibo(Arrays.asList(1, 2, 3, 5, 8)));
assertTrue(isFibo(Arrays.asList(5, 8, 13, 21, 34)));
assertFalse(isFibo(Arrays.asList(1, 2, 0)));
assertFalse(isFibo(Arrays.asList(1, 0, 1)));
assertFalse(isFibo(Arrays.asList(5, 5, 10, 15)));
}
In this project, I am trying to write a program that creates a 9x9 Sudoku board with 9 3x3 subgrids, along with a header row and column that lists the letters a to i. The program compiles correctly, but when I hit run, it gives the following error:
java.lang.ArrayIndexOutOfBoundsException: 0
at Sudoku.main(Sudoku.java:218)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at `enter code here`edu.rice.cs.drjava.model.compiler.JavacCompiler.runCommand(JavacCompiler.java:272)
Now, when I submitted this, the grading program stated that my print(), rowsComplete(), columnsComplete(), and isComplete() methods were incorrect, and that my main() was throwing a java.util.NoSuchElementException. I am confused as to why this is happening. Here is my code for Java, as well as notes on what exactly the methods are supposed to be doing.
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
public class Sudoku
{
public static final int SIZE = 9;
public static final int SUBGRID = 3;
public int[][] game;
public int[][] originalGame;
public Sudoku(String puzzleFile)
{
try
{
game = new int[SIZE][SIZE];
originalGame = new int[SIZE][SIZE];
File file = new File(puzzleFile);
Scanner in = new Scanner(file);
int i = 0;
int j = 0;
int k;
for (i = 0; i<SIZE; i++){
for (j = 0; j<SIZE; j++){
k = in.nextInt();
game[i][j]=k;
originalGame[i][j] = k;
}
}
}
catch (FileNotFoundException e)
{
System.out.println("FileNotFound: " + e.getMessage());
}
}
public void setZero(int[] array)
{
int i = 0;
for (i = 0; i < array.length; i++)
{
array[i] = 0;
}
}
/**
* This method determines whether the ints 1-9 are present exactly
* once in each row. Sets valSeen[i] = 1 if it sees i. If at any
* point valSeen[i] is already 1, the rows are not complete because of
* duplicate entries.
*
* If game[x][y] == -1, there is a blank entry so the row cannot be complete.
*
* #param valSeen: an array of ints that serve as flags to indicate whether
* their entry has been seen before or not.
*
* returns: true if each digit 1-9 is present in the row exactly once, else false
**/
public boolean rowsComplete(int[] valSeen)
{
int temp;
int k = 0;
for(int rows = 0; rows<SIZE; rows++){
for(int cols = 0; cols<SIZE; cols++){
if(game[rows][cols]==-1)
return false;
temp = game[rows][cols];
valSeen[temp-1]++;
}
for(k=0; k<valSeen.length; k++){
if(valSeen[k]!=1)
return false;
else return true;
}
setZero(valSeen);
}
return true;
}
/**
* This method determines whether the ints 1-9 are present exactly
* once in each column. Sets valSeen[i] = 1 if it sees i. If at any
* point valSeen[i] is already 1, the rows are not complete because of
* duplicate entries.
*
* If game[x][y] == -1, there is a blank entry so the row cannot be complete.
*
* #param valSeen: an array of ints that serve as flags to indicate whether
* their entry has been seen before or not.
*
* returns: true if each digit 1-9 is present in the column exactly once, else false
**/
public boolean columnsComplete(int[] valSeen)
{
int temp;
int k = 0;
for(int cols = 0; cols<SIZE; cols++){
for(int rows = 0; rows<SIZE; rows++){
if(game[rows][cols]==-1)
return false;
temp = game[rows][cols];
valSeen[temp-1]++;
}
for(k=0; k<valSeen.length; k++){
if(valSeen[k]!=1)
return false;
else return true;
}
setZero(valSeen);
}
return true;
}
/**
* This method determines whether the ints 1-9 are present exactly
* once in each subgrid. Sets valSeen[i] = 1 if it sees i. If at any
* point valSeen[i] is already 1, the rows are not complete because of
* duplicate entries.
*
* If game[x][y] == -1, there is a blank entry so the row cannot be complete.
*
* #param valSeen: an array of ints that serve as flags to indicate whether
* their entry has been seen before or not.
*
* returns: true if each digit 1-9 is present in each subgrid exactly once, else false
**/
public boolean subgridsComplete(int[] valSeen)
{
int temp;
for(int rows=0; rows<SIZE; rows+=3){
for (int cols=0; cols<SIZE; cols+=3){
for(int subrows=0; subrows<SUBGRID; subrows++){
for (int subcols=0; subcols<SUBGRID; subcols++){
temp= game[rows+subrows][cols+subcols];
if(temp==-1)
return false;
else
valSeen[temp-1]++;
}
}
for(int k=0; k<valSeen.length; k++){
if(valSeen[k]!=1)
return false;
else return true;
}
setZero(valSeen);
}
}
return true;
}
// Create the array valSeen here. I suggest making it = new int[SIZE+1].
// That way, it will have indexes 0-9, so the ints 1-9 can go into indexes
// 1-9 instead of mapping them to 0-8 by subtracting 1.
// Call rowsComplete(), columnsComplete(), and subgridsComplete().
// Be SURE to initialize valSeen to 0 before each method call by using setZero().
public boolean isComplete()
{
int [] valSeen = new int[SIZE+1];
setZero(valSeen);
if(rowsComplete(valSeen) && columnsComplete(valSeen) && subgridsComplete(valSeen))
return true;
else
return false;
}
public String makeHeader()
{
String header = " ";
for (int x = 97; x<106; x++)
header += ((char)x) + " | " + " ";
return header;
}
/**
* Prints out the grid. Each entry has a space to either side, columns are separated by '|'
* within the grid / between the header and the grid but not externally. See the specification
* for a detailed example. -1 is replaced with '_'.
*
* Remember that 'a' + 1 = 'b'
*
* Prints the grid to standard out.
**/
public void print()
{
System.out.println(makeHeader());
for(int rows=0; rows<SIZE; rows++){
System.out.print(" "+(char)('a'+rows));
for (int cols=0; cols<SIZE; cols++){
if (game[rows][cols]==-1)
System.out.print(" | _");
else
System.out.print(" | "+game[rows][cols]);
}
System.out.println();
}
}
public void move(String row, String col, int val)
{
int rowNumber = ((int)(row.charAt(0)-97));
int columnNumber = ((int)(col.charAt(0)-97));
if(originalGame[rowNumber][columnNumber]==-1)
game[rowNumber][columnNumber]=val;
}
public static void main(String[] args)
{
Sudoku puzzle = new Sudoku(args[0]);
Scanner s = new Scanner(System.in);
System.out.println("");
boolean gameplay = true;
while (gameplay){
puzzle.print();
if(puzzle.isComplete()){
System.out.println("Puzzle Complete!");
gameplay=false;
} else {
System.out.println("Puzzle Incomplete!");
System.out.println("Enter new value <row col val> :");
String rowv = s.next();
String colv = s.next();
int valv = s.nextInt();
puzzle.move(rowv, colv, valv);
}
}
}
}
In main method,
Sudoku puzzle = new Sudoku(args[0]);
Your program need an argument to initialize Sudoku which is being taken from user.
String[] args in main is a array of arguments for program which is given as parameters while starting program.
For you program, you'll have to start your Sudoku.class as
java Sudoku argument
You'll have to run you program with argument, else you'll get java.lang.ArrayIndexOutOfBoundsException: 0
I was recently asked to write 3 test programs for a job. They would be written using just core Java API's and any test framework of my choice. Unit tests should be implemented where appropriate.
Although I haven't received any feedback at all, I suppose they didn't like my solutions (otherwise I would have heard from them), so I decided to show my programs here and ask if this implementation can be considered good, and, if not, then why?
To avoid confusion, I'll ask only first one for now.
Implement a function that finds an
array in another larger array. It
should accept two arrays as parameters
and it will return the index of the
first array where the second array
first occurs in full. Eg,
findArray([2,3,7,1,20], [7,1]) should
return 2.
I didn't try to find any existing solution, but instead wanted to do it myself.
Possible reasons:
1. Should be static.
2. Should use line comments instead of block ones.
3. Didn't check for null values first (I know, just spotted too late).
4. ?
UPDATE:
Quite a few reasons have been presented, and it's very difficult for me to choose one answer as many answers have a good solution. As #adietrich mentioned, I tend to believe they wanted me to demonstrate knowledge of core API (they even asked to write a function, not to write an algorithm).
I believe the best way to secure the job was to provide as many solutions as possible, including:
1. Implementation using Collections.indexOfSubList() method to show that I know core collections API.
2. Implement using brute-force approach, but provide a more elegant solution.
3. Implement using a search algorithm, for example Boyer-Moore.
4. Implement using combination of System.arraycopy() and Arrays.equal(). However not the best solution in terms of performance, it would show my knowledge of standard array routines.
Thank you all for your answers!
END OF UPDATE.
Here is what I wrote:
Actual program:
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;
}
}
Test code:
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);
}
}
The requirement of "using just core Java API's" could also mean that they wanted to see whether you would reinvent the wheel. So in addition to your own implementation, you could give the one-line solution, just to be safe:
public static int findArray(Integer[] array, Integer[] subArray)
{
return Collections.indexOfSubList(Arrays.asList(array), Arrays.asList(subArray));
}
It may or may not be a good idea to point out that the example given contains invalid array literals.
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;
}
For finding an array of integers in a larger array of integers, you can use the same kind of algorithms as finding a substring in a larger string. For this there are many algorithms known (see Wikipedia). Especially the Boyer-Moore string search is efficient for large arrays. The algorithm that you are trying to implement is not very efficient (Wikipedia calls this the 'naive' implementation).
For your questions:
Yes, such a method should be static
Don't care, that's a question of taste
The null check can be included, or you should state in the JavaDoc that null values are not allowed, or JavaDoc should state that when either parameter is null a NullPointerException will be thrown.
Well, off the top of my head:
Yes, should be static.
A company complaining about that would not be worth working for.
Yeah, but what would you do? Return? Or throw an exception? It'll throw an exception the way it is already.
I think the main problem is that your code is not very elegant. Too many checks in the inner loop. Too many redundant checks.
Just raw, off the top of my head:
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;
}
You could keep that check for the first element so you don't have the overhead of setting up the boolean and the for loop for every single element in the array. Then you'd be looking at
public int findArray(int[] largeArray, int[] subArray) {
int subArrayLength = subArray.length;
if (subArrayLength == 0) {
return -1;
}
int limit = largeArray.length - subArrayLength;
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;
}
Following is an approach using KMP pattern matching algorithm. This solution takes O(n+m). Where n = length of large array and m = length of sub array. For more information, check:
https://en.wikipedia.org/wiki/KMP_algorithm
Brute force takes O(n*m). I just checked that Collections.indexOfSubList method is also 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;
}
A little bit optimized code that was posted before:
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;
}
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
}
UPDATE:
By reusing the same int array instance:
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
}
I would suggest the following improvements:
make the function static so that you can avoid creating an instance
the outer loop condition could be i <= largeArray.length-subArray.length, to avoid a test inside the loop
remove the test (largeArray[i] == subArray[0]) that is redundant
Here's #indexOf from String:
/**
* 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;
}
First to your possible reasons:
Yes. And the class final with a private constructor.
Shouldn't use this kind of comments at all. The code should be self-explanatory.
You're basically implicitly checking for null by accessing the length field which will throw a NullPointerException. Only in the case of a largeArray.length == 0 and a subArray == null will this slip through.
More potential reasons:
The class doesn't contain any function for array manipulations, opposed to what the documentation says.
The documentation for the method is very sparse. It should state when and which exceptions are thrown (e.g. NullPointerException) and which return value to expect if the second array isn't found or if it is empty.
The code is more complex than needed.
Why is the equality of the first elements so important that it gets its own check?
In the first loop, it is assumed that the second array will be found, which is unintentional.
Unneeded variable and jump (boolean and break), further reducing legibility.
largeArray.length <= i+j is not easy to grasp. Should be checked before the loop, improving the performance along the way.
I'd swap the operands of subArray[j] != largeArray[i+j]. Seems more natural to me.
All in all too long.
The test code is lacking more edge cases (null arrays, first array empty, both arrays empty, first array contained in second array, second array contained multiple times etc.).
Why is the last test case named testFindArrayExistsVeryComplex?
What the exercise is missing is a specification of the component type of the array parameters, respectively the signature of the method. It makes a huge difference whether the component type is a primitive type or a reference type. The solution of adietrich assumes a reference type (thus could be generified as further improvement), mine assumes a primitive type (int).
So here's my shot, concentrating on the code / disregarding documentation and tests:
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;
}
Code from Guava:
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;
}
I would to do it in three ways:
Using no imports i.e. using plain Java statements.
Using JAVA core APIs - to some extent or to much extent.
Using string pattern search algorithms like KMP etc. (Probably the most optimized one.)
1,2 and 3 are all shown above in the answers. Here is approach 2 from my side:
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.");
}
Using java 8 and lambda expressions:
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: is important to have finalString[] bigArray for enclosing space of lambda expression.
FYI: if the goal is simply to search wether an array y is a subset of an array x, we can use this:
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