Java - Recursive Binary Search through Arraylist - java

So I need help with a method. using a recursive binary search algorithm to search through an arraylist.
private static < E extends Employee > int binarySearch(ArrayList<E> list, int firstElem, int lastElem, String searchLastName)
{
int middle=0;
if(firstElem > lastElem){
return -1;
}
middle = (firstElem + lastElem) / 2;
if(list.get(middle).getLastName().equals(searchLastName)){
return middle;
}else if( ){ // <-------------?
return binarySearch(list,middle+1,lastElem, searchLastName);
}
else {
return binarySearch(list, firstElem, middle -1, searchLastName);
}
}
This is what I have so far but I'm stuck on the logic part. Any help would be appreciated.

Since you say you're stuck on the logic part, I think a pseudo-code answer suffices.
First of all, I'm assuming your array is sorted in ascending order. If the array is not sorted, binary search is not possible. Because it is sorted, you can keep cutting the problem size in half with each comparison. So if the answer is in the right part of the array, you throw out the left part and only recurse into the right part.
Because with every recursive call the problem gets half as small, you get a running time of O(log n).
The basic logic is like this:
binarySearch(list,begin,end,query)
if (begin > end)
return -1
middle = (begin + end) / 2
if list[middle].value == query
return middle
if list[middle].value < query
return binarySearch(list,begin,middle,query)
return binarySearch(list,middle,end,query)

Here you are
You have to implement a compare method to decide which half you'll search in.
I did a simple method to compare two strings .. it converts is to the a number value.
Check this sample code:
import java.util.ArrayList;
public class BinarySearch {
public static void main(String[] args) {
ArrayList<Employee> arrayList = new ArrayList<Employee>();
for (int i = 0; i < 10; i++) {
Employee employee = new Employee();
employee.setLastName("name" + i);
arrayList.add(employee);
}
System.out.println(arrayList);
System.out
.println(binarySearch(arrayList, 0, arrayList.size(), "name6"));
}
private static <E extends Employee> int binarySearch(ArrayList<E> list,
int firstElem, int lastElem, String searchLastName) {
int middle = 0;
if (firstElem > lastElem) {
return -1;
}
middle = (firstElem + lastElem) / 2;
if (list.get(middle).getLastName().equals(searchLastName)) {
return middle;
} else if (compare(searchLastName, list.get(middle).getLastName()) >= 0) { // <-------------?
return binarySearch(list, middle + 1, lastElem, searchLastName);
} else {
return binarySearch(list, firstElem, middle, searchLastName);
}
}
/**
* Compare to strings.
* #param str1
* #param str2
* #return
*/
public static int compare(String str1, String str2) {
int minLength = Math.min(str1.length(), str2.length());
if (getValue(str1, minLength) > getValue(str2, minLength)) {
return 1;
} else {
return -1;
}
}
/**
* Calculate a value to a string to compare it.
* #param str
* #param length
* #return
*/
public static int getValue(String str, int length) {
int result = 0;
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
result += Math.pow(10, i) * c;
}
return result;
}
}
Here's a sample output:
[name0, name1, name2, name3, name4, name5, name6, name7, name8, name9]
6
I hope this could help.

Related

Find all integers in a string of digits which meet a criteria

I am trying to come up with an algorithm in Java which when given a string of digits can identify a combination of integers which meets the following criteria
N = N1 + N2
N >= N1 >= N2
where:
N is the Nth element in the string or element at Nth position;
N1 is the (N-1) element in the string & N2 is the (N-2) element in the string.
Example 1: 224610
Elements in this string are 2, 2, 4, 6, 10.
First Set: 2+2=4 (N2=2; N1=2 & N= 4);
Second Set: 2+4=6 (N2=2; N1=4 & N=6);
Third Set: 4+6=10 (N2=4; N1=6 & N= 10)
Example 2: 11112233558
Elements in this string are 1, 11, 12, 23, 35, 58
Example 3: 1101102203
Elements in this string are 1, 101, 102, 203.
I have already written a function which can take an ArrayList of integers and tell you whether the array complies with the requirements.
public static boolean complies(ArrayList<Integer> al)
{
boolean result = true;
int alsize = al.size();
for (int n = alsize-1; n > 1; n--)
{
int N1 = al.get(n-1);
int N2 = al.get(n-2);
int N = al.get(n);
if (N != ( N1 + N2))
result = false;
if ((N < N1) || (N1 < N2))
result = false;
}
return(result);
}
The part I am struggling with his finding an elegant way to identify all possible integer combinations which I can run through the above function.
I thought about the question you asked, which was essentially how to find all the combinations of a set of digits in the order the digits were given, and realized that this could be solved recursively. All you have to do is add the first digit to all the combinations that can be made with the rest of the digits as well as put that digit in front of the first term of all the combinations. The base case is that there is only one digit, which means there is of course only one combination.
Let me give an example. With the digits 123 first we find all the combinations for 23 and since 23 has more than one digit we find all the combos for 3, which is just 3. Then add 2 to that combination which makes 2, 3 and put 2 in front of the first term of that combination, which makes 23. Now add 1 to all the combos which makes 1, 2, 3 and 1, 23 and put 1 in front of the first term which makes 12, 3 and 123.
So I made this method to find all the combinations. It returns a two dimensional arrayList with each individual arrayList being a unique combination. The preconditions are that you have to give it a string of numbers only and no empty strings. If I made a mistake or it doesn't work for your application say something. I am pretty sure you could just go through the two dimensional arrayList and check if each arrayList works in your boolean method. You can test it here.
public ArrayList<ArrayList<Integer>> findCombos(String input)
{
ArrayList<ArrayList<Integer>> answer = new ArrayList<ArrayList<Integer>>();
if(input.length()==1)
{
ArrayList<Integer> combo = new ArrayList<Integer>();
answer.add(combo);
combo.add(Integer.parseInt(input)); //this method converts from a string to an int
return answer;
}
else
{
answer = findCombos(input.substring(1));
int size = answer.size(); //you need to save this because when you add things to an arrayList the size changes
for(int i=0;i<size;i++) //this copies the arrayList back to itself
{
ArrayList<Integer> copy = new ArrayList<Integer>(answer.get(i));
answer.add(copy);
}
int digit = (char)(input.charAt(0)-'0');//this saves the current digit
for(int i=0;i<size;i++)//this adds the digit in front of all the previous combos
answer.get(i).add(0, digit);
for(int i=size;i<answer.size();i++)//this puts the digit in front of the first term of the previous combos
{
String copy = "" + answer.get(i).get(0);//I just did this to find the length of the first term easily
int append = (int)(digit*Math.pow(10, copy.length()));
answer.get(i).set(0, append+answer.get(i).get(0));
}
return answer;
}
}
The Code below works against all 3 given input, and is solid enough.
public class IntFinder
{
/**
* #param args the command line arguments
*/
private static String given = "444444889321420";
static N1N2 temp = new N1N2(given);
public static void main(String[] args) {
// TODO code application logic here
N1N2 n1 = new N1N2(given);
N1N2 n2 = new N1N2(given);
/*IntFinder.setGiven("224610");
n1 = new N(IntFinder.getGiven());
n2 = new N(IntFinder.getGiven());
n1.setValue(0, 0); // 2
n2.setValue(1, 1); // 2*/
/*IntFinder.setGiven("11112233558");
n1 = new N(IntFinder.getGiven());
n2 = new N(IntFinder.getGiven());
n1.setValue(0, 0); // 1
n2.setValue(1, 2); // 11*/
IntFinder.setGiven("1101102203");
n1 = new N1N2(IntFinder.getGiven());
n2 = new N1N2(IntFinder.getGiven());
n1.setValue(0, 0); // 1
n2.setValue(1, 3); // 101
System.out.println("string: " + n1.getValue());
System.out.println("string: " + n2.getValue());
System.out.println("result: " + ((IntFinder.findTillEndByN1N2(n1, n2) > -1) ? "found" : "NOT found"));
}
public static String setGiven(String givenString)
{
return IntFinder.given = givenString;
}
public static String getGiven()
{
return IntFinder.given;
}
public static int findTillEndByN1N2(N1N2 n1, N1N2 n2)
{
int retVal = -1, lenChange = n1.getLength() + n2.getLength() + n1.getStartIndex();
retVal = findNagainstN1N2(n1, n2, lenChange);
if (IntFinder.getGiven().length() == (n2.getEndIndex() + 1)) // base case 1 (last digit reached)
{
return 1;
}
else if (IntFinder.getGiven().length() < (n2.getEndIndex() + 1))
{
System.out.println("fatal err:");
System.exit(0);
}
if (retVal > -1) // recurse till end
{
if (!temp.getUsed())
{
temp = IntFinder.shallowCopy(n1);
temp.setUsed(true);
}
n1 = IntFinder.shallowCopy(n2);
n2.setValue(n2.getEndIndex() + 1 , retVal);
System.out.println("string: "+n2.getValue());
retVal = findTillEndByN1N2(n1, n2);
}
else
return retVal;
return retVal;
}
public static Integer findNagainstN1N2(N1N2 n1, N1N2 n2, Integer startIndex)
{
String remainingGiven = IntFinder.getGiven().substring(startIndex);
Integer i, n1n2Total = 0, retVal = -1;
n1n2Total = n1.getValue() + n2.getValue();
for (i = 0; i < remainingGiven.length(); i++)
{
try
{
int found = Integer.parseInt(remainingGiven.substring(0, (i+1)));
if (found == n1n2Total)
{
retVal = startIndex + i;
break;
}
else if (found > n1n2Total)
{
retVal = -1;
break;
}
}
catch (NumberFormatException e)
{
;
}
}
return retVal;
}
public static N1N2 shallowCopy(N1N2 from) {
N1N2 newN = new N1N2(IntFinder.getGiven());
newN.setValue(from.getStartIndex(), from.getEndIndex());
return newN;
}
}
>>N1N2.class
public class N1N2 {
private String givenString;
private int startIndex = 0;
private int endIndex = -1;
private int value = 0;
private int length = endIndex + 1;
private Boolean used = false;
public N1N2(String given) {
startIndex = 0;
endIndex = given.length() - 1;
givenString = given;
}
public int getValue() {
return value;
}
public int getLength() {
return length;
}
public Boolean getUsed()
{
return used;
}
public void setUsed(Boolean used)
{
this.used = used;
}
// public void outValues()
// {
// System.out.println("given:" + givenString + ", startIndex:"+ startIndex + ", endIndex: " + endIndex + ", length:" + length + ", value:" + value + "\n");
// }
public void setValue(int startIndex, int endIndex) {
this.value = Integer.parseInt(givenString.substring(startIndex, endIndex + 1));
this.startIndex = startIndex;
this.endIndex = endIndex;
this.length = (this.value + "").length();
// this.outValues();
}
public int getEndIndex() {
return this.endIndex;
}
public int getStartIndex() {
return this.startIndex;
}
}
Just set n1 and n2 properly before calling IntFinder.findTillEndByN1N2(n1, n2).
Observe the examples I used.
So to complete the program, create your own algorithm using two loops filling
n1.setValues and n2.setValues
Ex.
given = 1232447
//first loop
n1.setValues(0,0) // value = 1
n2.setValues(1,1) // value = 2
IntFinder.findTillEndByN1N2(n1, n2) // returns -1 // not found...
//next loop - increment n2 length
n1.setValues(0,0) // value = 1
n2.setValues(1,2) // value = 23
IntFinder.findTillEndByN1N2(n1, n2) // returns 1 // now found till end.
//ofcourse when n2 reached the last digit, increment n1 length by 1 and set n2 length back to 1.
//for given=444444889321420 // 44 444 488 932 1420
//all findTillEnd with n1 length 1 should fail so inc n1 length on outer loop
//n1.setValue(0, 1) // 2 digit number ( 44)
//n2.setValue(0, 0) // 4
//upon continuing loop, the desired result will be met.
In line with the above solution from Pham I could modify the code to exclude the edge cases and create a solution for the above question.This method will always return true if it complies with the pattern else false and yes we have to find first two elements to make it work.Checked for all test cases :
public static boolean complies(String input){
// List<Long> firstTwo = new ArrayList<Long>();
for(int i = 1; i < input.length(); i++){
for(int j = 0; j < i; j++){
long first = Long.parseLong(input.substring(0, j + 1));//substring form 0 to j
long second = Long.parseLong(input.substring(j + 1, i + 1));//substring from j + 1 to i
if(second < first)
continue;
String last = "" + first + second;
System.out.println("first :"+first+" second :"+second);
if(last.length() == input.length()){
return false;
}
// firstTwo.add(first);
// firstTwo.add(second);
while(last.length() < input.length()){
long nxt = first + second;
last += nxt;
first = second;
second = nxt;
}
if(last.equals(input)){//Matched!
System.out.println("matched");
// dont go further return true
return true;
}
// firstTwo.clear();
}
}
return false;
}

Storing values of a Fibonacci sequence w/ recursion with minimal runtime

I know my code has a lot of issues right now, but I just want to get the ideas correct before trying anything. I need to have a method which accepts an integer n that returns the nth number in the Fibonacci sequence. While solving it normally with recursion, I have to minimize runtime so when it gets something like the 45th integer, it will still run fairly quickly. Also, I can't use class constants and globals.
The normal way w/ recursion.
public static int fibonacci(int n) {
if (n <= 2) { // to indicate the first two elems in the sequence
return 1;
} else { // goes back to very first integer to calculate (n-1) and (n+1) for (n)
return fibonacci(n-1) + fibonacci(n-2);
}
}
I believe the issue is that there is a lot of redundancy in this process. I figure that I can create a List to calculate up to nth elements so it only run through once before i return the nth element. However, I am having trouble seeing how to use recursion in that case though.
If I am understanding it correctly, the standard recursive method is slow because there are a lot of repeats:
fib(6) = fib(5) + fib(4)
fib(5) = fib(4) + fib(3)
fib(4) = fib(3) + 1
fib(3) = 1 + 1
Is this the correct way of approaching this? Is it needed to have some form of container to have a faster output while still being recursive? Should I use a helper method? I just recently got into recursive programming and I am having a hard time wrapping my head around this since I've been so used to iterative approaches. Thanks.
Here's my flawed and unfinished code:
public static int fasterFib(int n) {
ArrayList<Integer> results = new ArrayList<Integer>();
if (n <= 2) { // if
return 1;
} else if (results.size() <= n){ // If the list has fewer elems than
results.add(0, 1);
results.add(0, 1);
results.add(results.get(results.size() - 1 + results.get(results.size() - 2)));
return fasterFib(n); // not sure what to do with this yet
} else if (results.size() == n) { // base case if reached elems
return results.get(n);
}
return 0;
}
I think you want to use a Map<Integer, Integer> instead of a List. You should probably move that collection outside of your method (so it can cache the results) -
private static Map<Integer, Integer> results = new HashMap<>();
public static int fasterFib(int n) {
if (n == 0) {
return 0;
} else if (n <= 2) { // if
return 1;
}
if (results.get(n) != null) {
return results.get(n);
} else {
int v = fasterFib(n - 1) + fasterFib(n - 2);
results.put(n, v);
return v;
}
}
This optimization is called memoization, from the Wikipedia article -
In computing, memoization is an optimization technique used primarily to speed up computer programs by keeping the results of expensive function calls and returning the cached result when the same inputs occur again.
You can use Map::computeIfAbsent method (since 1.8) to re-use the already calculated numbers.
import java.util.HashMap;
import java.util.Map;
public class Fibonacci {
private final Map<Integer, Integer> cache = new HashMap<>();
public int fib(int n) {
if (n <= 2) {
return n;
} else {
return cache.computeIfAbsent(n, (key) -> fib(n - 1) + fib(n - 2));
}
}
}
The other way to do this is to use a helper method.
static private int fibonacci(int a, int b, int n) {
if(n == 0) return a;
else return fibonacci(b, a+b, n-1);
}
static public int fibonacci(int n) {
return fibonacci(0, 1, n);
}
How about a class and a private static HashMap?
import java.util.HashMap;
public class Fibonacci {
private static HashMap<Integer,Long> cache = new HashMap<Integer,Long>();
public Long get(Integer n) {
if ( n <= 2 ) {
return 1L;
} else if (cache.containsKey(n)) {
return cache.get(n);
} else {
Long result = get(n-1) + get(n-2);
cache.put(n, result);
System.err.println("Calculate once for " + n);
return result;
}
}
/**
* #param args
*/
public static void main(String[] args) {
Fibonacci f = new Fibonacci();
System.out.println(f.get(10));
System.out.println(f.get(15));
}
}
public class Fibonacci {
private Map<Integer, Integer> cache = new HashMap<>();
private void addToCache(int index, int value) {
cache.put(index, value);
}
private int getFromCache(int index) {
return cache.computeIfAbsent(index, this::fibonacci);
}
public int fibonacci(int i) {
if (i == 1)
addToCache(i, 0);
else if (i == 2)
addToCache(i, 1);
else
addToCache(i, getFromCache(i - 1) + getFromCache(i - 2));
return getFromCache(i);
}
}
You can use memoization (store the values you already have in an array, if the value at a given index of this array is not a specific value you have given to ignore --> return that).
Code:
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int n = Integer.parseInt(s.nextLine());
int[] memo = new int[n+1];
for (int i = 0; i < n+1 ; i++) {
memo[i] = -1;
}
System.out.println(fib(n,memo));
}
static int fib(int n, int[] memo){
if (n<=1){
return n;
}
if(memo[n] != -1){
return memo[n];
}
memo[n] = fib(n-1,memo) + fib(n-2,memo);
return memo[n];
}
Explaination:
memo :
-> int array (all values -1)
-> length (n+1) // easier for working on index
You assign a value to a given index of memo ex: memo[2]
memo will look like [-1,-1, 1, ..... ]
Every time you need to know the fib of 2 it will return memo[2] -> 1
Which saves a lot of computing time on bigger numbers.
private static Map<Integer, Integer> cache = new HashMap<Integer, Integer(){
{
put(0, 1);
put(1, 1);
}
};
/**
* Smallest fibonacci sequence program using dynamic programming.
* #param n
* #return
*/
public static int fibonacci(int n){
return n < 2 ? n : cache.computeIfAbsent(n, (key) -> fibonacci( n - 1) + fibonacci(n - 2));
}
public static long Fib(int n, Dictionary<int, long> dict)
{
if (n <= 1)
return n;
if (dict.ContainsKey(n))
return dict[n];
var value = Fib(n - 1,dict) + Fib(n - 2,dict);
dict[n] = value;
return value;
}

Finding elements in array with binary search

Trying to use Arrays.binarySearch() to search for a string in an array and return the index. However each time I call Arrays.binarySearch() I get the following exception -
Exception in thread "main" java.lang.NullPointerException
at java.util.Arrays.binarySearch0(Unknown Source)
at java.util.Arrays.binarySearch(Unknown Source)
at project.ArrayDirectory.lookupNumber(ArrayDirectory.java:97)
at project.test.main(test.java:12)
Here is my ArrayDirectory class -
public class ArrayDirectory implements Directory {
static Entry[] directory = new Entry[50];
#Override
public void addEntry(String surname, String initials, int extension) {
int n = 0;
for (int i = 0; i < directory.length; i++) { // counting number of
// entries in array
if (directory[i] != null) {
n++;
}
}
if (n == directory.length) {
Entry[] temp = new Entry[directory.length * 2]; // if array is full
// double the
// length.
for (int i = 0; i < directory.length; i++)
temp[i] = directory[i];
directory = temp;
}
int position = -1;
for (int i = 0; i < directory.length; i++) {
position = i;
if (directory[i] != null) { // sorting the array into alphabetical
// order by surname.
int y = directory[i].getSurname().compareTo(surname);
if (y > 0) {
break;
}
}
else if (directory[i] == null) {
break;
}
}
System.arraycopy(directory, position, directory, position + 1,
directory.length - position - 1);
directory[position] = new Entry(initials, surname, extension); // placing
// new
// entry
// in
// correct
// position.
}
#Override
public int lookupNumber(String surname, String initials) {
// TODO Auto-generated method stub
Entry lookup = new Entry(surname, initials);
int index = Arrays.binarySearch(directory, lookup);
return index;
}
}
Any ideas how I use binary search to find the correct index?
Thank you for you help.
edit -
I have also overridden comapreToin my Entry class -
public int compareTo(Entry other) {
return this.surname.compareTo(other.getSurname());
}
In your invocation of
int index = Arrays.binarySearch(directory,lookup);
directory seems to contain only null elements. Check that you are initializing elements correctly.
I note two things:
static Entry [] directory = new Entry [1];
First, that code allocates space for one Entry in the array. It doesn't actually instantiate an Entry. That is, directory[0] is null. Secondly, a binary-search on an array with one entry is crazy. There is only one element. It must be directory[0]. Finally, you should sort your array to do a binary search on it.
The basic concept behind a binary search is the recursion of the following steps(Note the search assumes the list or array of elements is sorted in some form and the element exists there.):
Go to the middle element of the array.
check if the searched element is equal to the element at the middle. If it is then return its index.
if not then check if the searched element is 'smaller' or 'larger' than the element in the middle.
if it is smaller then go to step 1 using only the lower/first half of the array instead of the whole.
else go to step 1 using only the upper/last half of the array instead of the whole.
As the array is continuously divided in 2 it will eventually reach the size of 1 giving the result.
Now, suppose you are looking for an integer in an int array. Here is what the code would be like:
public static int binarySearch(int number, int[] array)
{
boolean isHere = false;
Integer index =0;
for(int i=0;i<array.length;i++)
{
if(array[i] == number)
{
isHere = true;
i = array.length;
}
}
if(!isHere)
{
index = -1;
}
else
{
int arrayStart = 0;
int arrayEnd = array.length;
index = binarySearch(number, arrayStart, arrayEnd, array);
}
return index;
}
private static int binarySearch(int number, int start, int end, int[] array)
{
// this formula ensures the index number will be preserved even if
// the array is divided later.
int middle = (start+ end)/ 2;
if(array[middle] == number)
{
return middle;
}
else
{
if(number < array[middle])
{
//searches the first half of the array
return binarySearch(number, start, middle, array);
}
else
{
// searches the last half of the array
return binarySearch(number, middle, end, array);
}
}
}
You can use the compareTo() method instead of <,>, & == operators in your example. The logic should still be the same.

Java : Binary Search for String Arrays

I found some code that can be used to perform a binary search on an array of integers, and I am trying to change it so that I can use it on an array of strings instead.
Here is what I have so far:
import edu.princeton.cs.introcs.In;
import edu.princeton.cs.introcs.StdIn;
import edu.princeton.cs.introcs.StdOut;
import java.util.Arrays;
public class BinarySearch {
/**
* This class should not be instantiated.
*/
private BinarySearch() { }
/**
* Searches for the integer key in the sorted array a[].
* #param key the search key
* #param a the array of integers, must be sorted in ascending order
* #return index of key in array a[] if present; -1 if not present
*/
public static int rank(String key, String[] a) {
int lo = 0;
int hi = a.length - 1;
int steps = 0;
while (lo <= hi) {
steps = steps + 1; // this will keep count of the number of steps needed
// Key is in a[lo..hi] or not present.
int mid = lo + (hi - lo) / 2;
if (key < a[mid])
{
hi = mid - 1;
}
else if (key > a[mid])
{
lo = mid + 1;
}
else return mid;
}
return steps;
}
/**
* Reads in a sequence of integers from the whitelist file, specified as
* a command-line argument. Reads in integers from standard input and
* prints to standard output those integers that do *not* appear in the file.
* #param args
*/
public static void main(String[] args) {
// read the integers from a file
In in = new In("C:\\Users\\Owner\\Desktop\\EnglishWordList.txt");
String[] whitelist = in.readAllStrings();
// sort the array
Arrays.sort(whitelist);
// read integer key from standard input; print if not in whitelist
while (!StdIn.isEmpty()) {
String key = StdIn.readString();
if (rank(key, whitelist) == -1)
StdOut.println(key);
}
}
}
I am getting an error in the rank() method on both if statements. It states that I cannot use the operator "<" and ">" for strings, meaning that its not looking at ASCII code. How can I fix this problem? There may be other issues, but this is the only thing that is highlighted by my IDE. Please let me know what you think.
You can use a.compareTo(b) i.e use comparable inteface instead of using > or < operators.
Comparable interface. Compares values and returns an int which tells if the values compare less than, equal, or greater than. If your class objects have a natural order, implement the Comparable interface and define this method. All Java classes that have a natural ordering implement this (String, Double, BigInteger, ...).
Please try with the following algorithm
public boolean binarySearch(int key, int[] arrayToSearch) {
if (arrayToSearch.length == 0) {
return false;
}
int low = 0;
int high = arrayToSearch.length - 1;
while (low <= high) {
int midItem = (low + high) / 2;
if (key > arrayToSearch[midItem]) {
low = midItem + 1;
} else if (key < arrayToSearch[midItem]) {
high = midItem - 1;
} else { // The element has been found
return true;
}
}
return false;
}
Try this, it should work with Strings too
public static String binarySearch(String[] names, String x) {
int high = names.length - 1;
int low = 0;
int mid = 0;
while (low <= high) {
mid = (low + high) / 2;
if (names[mid].compareTo(x) == 0) {
return names[mid];
}
else if (names[mid].compareTo(x) > 0) {
high = mid - 1;
}
else {
low = mid + 1;
}
}
return null;
}

Find an array inside another larger array

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

Categories