3sum Solution not passing test case (Java) - java

I'm trying to program a solution to the 3sum question on leetcode(link: https://leetcode.com/problems/3sum/).
This is what I've done so far:
public int binary_search(int [] nums, int start, int end, int target)
{
if (start > end)
{
return -1;
}
int mid = (start + end) / 2;
if (nums[mid] == target)
{
return mid;
}
else {
if (nums[mid] < target)
{
return binary_search(nums, mid + 1, end, target);
} else {
return binary_search(nums, start, mid - 1, target);
}
}
}
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
ArrayList<List<Integer>> solution_set = new ArrayList();
Set<List<Integer>> ordered_solutions = new HashSet();
for (int i = 0; i < nums.length; i++)
{
if (i + 1 == nums.length)
{
continue;
}
int number_1 = nums[i];
int number_2 = nums[i+1];
int target = -(number_1 + number_2);
int target_index = binary_search(nums, 0, nums.length - 1, target);
if (binary_search(nums, 0, nums.length - 1, target) != -1 && target_index != i && target_index != i+1)
{
List<Integer> submission = new ArrayList();
submission.add(number_1); submission.add(number_2); submission.add(target);
List<Integer> ordered_submission = submission;
Collections.sort(ordered_submission);
if (ordered_solutions.add(ordered_submission) == true)
{
solution_set.add(submission);
}
}
}
return solution_set;
}
The program works as follows:
input is given to function threeSum which is then sorted and two following objects are created; An ArrayList that will store all non-duplicate solutions and a Set that is used to test for said duplicate solutions.
Then, the for loop sifts through the array and does the following:
it adds the i and i+1 element then negates them to find the number needed to sum all three numbers to zero. With this number acquired, a binary search is conducted on the array to see if this number can be found. If it is found, a few other conditions are tested to ensure that the target index is not actually the same as index i or i+1. After that, I create two objects, a submission that includes the elements in their original order and an ordered submission. If the ordered submission is inserted into the set and the set returns true, it means it's not a duplicate and i store it in the solution_set.
My problem is as follows: My program fails with the test case [0,0,0]. I believe the target is calculated as zero, but the binary search chooses the zero which is in i+1 so the solution is rejected. Does anyone have any suggestions on how this problem can be fixed?

Ok, if you want to do it with binary search, here it is. I've fixed several logical bugs, made some improvements, and added some comments, hope this will help. The code is accepted by leetcode, but it is not so efficient solution. Time complexity is O(n^2*logn). With 2 pointers approach you can do it in O(n^2).
public int binary_search(int[] nums, int start, int end, int target) {
if (start > end) {
return -1;
}
int mid = (start + end) / 2;
if (nums[mid] == target) {
return mid;
} else {
if (nums[mid] < target) {
return binary_search(nums, mid + 1, end, target);
} else {
return binary_search(nums, start, mid - 1, target);
}
}
}
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
ArrayList<List<Integer>> solution_set = new ArrayList<>();
// you can't do it using only one loop.
// Take a look at this sample input -2,0,1,1,2.
// Here you have to consider for nums[i] and nums[j] -2, 0 as well as
// -2, 1
for (int i = 0; i < nums.length - 2; i++) {
// skip duplicates
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.length - 1; j++) {
int number_1 = nums[i];
int number_2 = nums[j];
// skip duplicates and, since array is sorted, don't need to
// consider values > 0
if (i != j - 1 && nums[j - 1] == nums[j] || number_1 + number_2 > 0)
continue;
int target = -(number_1 + number_2);
// since array is sorted, start binary search only from j + 1
int target_index = binary_search(nums, j + 1, nums.length - 1, target);
if (target_index != -1) {
List<Integer> submission = new ArrayList<>();
submission.add(number_1);
submission.add(number_2);
submission.add(target);
solution_set.add(submission);
}
}
}
return solution_set;
}
Output:
[[0, 0, 0]]
[[-2, 0, 2], [-2, 1, 1]]
[[-1, -1, 2], [-1, 0, 1]]

Related

Why does the 3 sum algorithm only look at sub arrays that are to the right of a particular number?

I am looking at a code that solves the 3sum problem by building upon the 2sum solution. Why should low be int lo = index+1 ? Does this mean that when we are looking at subarrays , we are only interested in the sum of the numbers to right of target ? What about the numbers to the left of the target ?
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if (nums == null || nums.length < 2) {
return new ArrayList<>();
}
List<List<Integer>> results = new ArrayList<>();
Arrays.sort(nums);
for (int i=0; i<nums.length; i++) {
if (i != 0 && nums[i - 1] == nums[i]) {
continue;
}
results.addAll(twoSum(nums, i));
}
return results;
}
private List<List<Integer>> twoSum(int[] nums, int index) {
List<List<Integer>> results = new ArrayList<>();
int target = -nums[index];
int lo = index+1, hi = nums.length-1;
while (lo < hi) {
if (target == nums[lo] + nums[hi]) {
List<Integer> result = Arrays.asList(nums[lo], nums[hi], -target);
results.add(result);
lo++;
hi--;
while (lo < hi && nums[lo] == nums[lo - 1])
lo++; // skip same result
while (lo < hi && nums[hi] == nums[hi + 1])
hi--; // skip same result
} else if (target < nums[lo] + nums[hi]) {
hi--;
} else {
lo++;
}
}
return results;
}
}
Because for every number num[i] at position i, you can use 2-sum algorithm on the array [i+1...N] to find two numbers such that their sum is equal to -num[i], which means the sum of 3 numbers is 0.
For e.g.
Let the array be [-1, 0, 1, 2, -1, -4]
The algorithm will find any 2-sums in [0, 1, 2, -1, -4] is equal to 1
Then it will find any 2-sums in [1, 2, -1, -4] is equal to 0
etc.
Now you can see the starting point of the sub-array is i+1 when you searching 2-sums for num[i], which is why int lo = index + 1
The search is exhausive so all tuples which sum is equal to 0 will be found.

Java: How to implement 3 sum?

I'm studying the 3 Sum to implement it on my own, and came across the following implementation with the rules:
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)
And implementation (sorts the array, iterates through the list, and uses another two pointers to approach the target):
import java.util.*;
public class ThreeSum {
List<List<Integer>> threeSum(int[] num) {
Arrays.sort(num);
List<List<Integer>> res = new LinkedList<>();
for (int i=0; i<num.length-2; i++) {
if (i==0 || (i>0 && num[i] != num[i-1])) { //HERE
int lo = i+1;
int hi = num.length-1;
int sum = 0 - num[i];
while (lo < hi) {
if (num[lo] + num[hi] == sum) {
res.add(Arrays.asList(num[i], num[lo], num[hi]));
while (lo < hi && num[lo] == num[lo+1]) lo++; //HERE
while (lo < hi && num[hi] == num[hi-1]) hi--; //HERE
lo++; hi--;
} else if (num[lo] + num[hi] < sum) lo++;
else hi--;
}
}
}
return res;
}
//Driver
public static void main(String args[]) {
ThreeSum ts = new ThreeSum();
int[] sum = {-1, 0, 1, 2, -1, -4};
System.out.println(ts.threeSum(sum));
}
}
And my question is (located where commented: //HERE), what's the reason for checking num[i] != num[i-1], num[lo] == num[lo+1], and num[hi] == num[hi-1]? Supposedly they are supposed to skip the same result, but what does that mean? Examples would really help.
Thank you in advance and will accept answer/up vote.
Imagine you have {-1,-1,0,1,2,4} and considering triplet num[0], num[2], num[3] (-1,0,1).
lo=0 here. To exclude triplet num[1], num[2], num[3] with the same values, we should increment lo and pass over duplicate
This will prevent the list to have duplicate triplet.
For example, with you test :
int[] sum = {-1, 0, 1, 2, -1, -4};
will be sorted like :
sum = {-4, -1, -1, 0, 1, 2};
You see that you have -1 twice. Without these test, you would test twice if -1 = 0 + 1. This is not usefull so the algo simply search the next different value.
You could remove duplicate in the sorted List to prevent these test.
Thanks to MBo, we can't remove duplicate since we can have triplet with same value (but with different index)
All the three sentences is used to avoid the duplicate output.
Consider a sorted list {-2, -2 , 1, 1}
If there is no checking for num[i] != num[i-1], the output of the program would be(-2, 1, 1)and(-2, 1, 1), which are two duplicate triplets.
The checking for num[lo] != num[lo + 1]and num[hi] != num[hi - 1] are for the same reason.
Consider a sorted list
{-2,-1,-1,0,3}
If there is no checking for num[lo], you will get (-2,-1,3) and (-2,-1,3) as the output.
Still, I want to recommend a better solution for this problem. You can numerate the sum of two numbers in the list and find the 3rd number by hash or binary search. It will helps you to gain a O(n^2logn) time complexity rather than O(n^3). (I was wrong, the time complexity of this algorithm is O(n^2), sorry for that.)
Following program finds pairs of three integer with O(N*2)
Sort the input Array
and iterate each element in for loop and check for sum in program which is developed for Two sum.
Two sum in linear time after sorting ->
https://stackoverflow.com/a/49650614/4723446
public class ThreeSum {
private static int countThreeSum(int[] numbers) {
int count = 0;
for (int i = 0; i < numbers.length; i++) {
int front = 0, rear = numbers.length - 1;
while (front < rear) {
if (numbers[front] + numbers[rear] + numbers[i] == 0) {
System.out.printf(String.format("Front : {%d} Rear : {%d} I : {%d} \n", numbers[front],
numbers[rear], numbers[i]));
front++;
rear--;
count++;
} else {
if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
front++;
} else {
rear--;
}
}
}
}
return count;
}
public static void main(String[] args) {
int[] numbers = { 1, 3, 5, 7, 12, 16, 19, 15, 11, 8, -1, -3, -7, -8, -11, -17, -15 };
Arrays.sort(numbers);
System.out.println(countThreeSum(numbers));
}
}
It's worked with any NSum (3Sum, 4Sum, 5Sum, ...) and quite fast.
public class ThreeSum {
private static final int RANDOM_RANGE = 20;
private Integer array[];
private Integer arrayIndex[];
private int result[];
private int bagLength;
private int resultIndex = 0;
private void generateData(int size) {
array = new Integer[size];
Random random = new Random();
for (int i = 0; i < size; i++) {
array[i] = random.nextInt(RANDOM_RANGE) - (RANDOM_RANGE/2);
}
}
private void markArrayIndex(int size) {
arrayIndex = new Integer[size];
for (int i = 0; i < size; i++) {
arrayIndex[i] = i;
}
}
private void prepareBeforeCalculate(int size, int sumExpected, int bagLength) {
this.bagLength = bagLength;
result = new int[bagLength];
generateData(size);
markArrayIndex(size);
}
void calculate(int size, int sumExpected, int bagLength) {
prepareBeforeCalculate(size, sumExpected, bagLength);
Arrays.sort(arrayIndex, (l, r) -> array[l].compareTo(array[r]));
System.out.println(Arrays.toString(array));
long startAt = System.currentTimeMillis();
if (sumExpected > 0) findLeft(sumExpected, 0, 0, array.length);
else findRight(sumExpected, 0, 0 - 1, array.length - 1);
System.out.println("Calculating in " + ((System.currentTimeMillis() - startAt) / 1000));
}
private void findLeft(int total, int indexBag, int left, int right) {
while (left < array.length && array[arrayIndex[left]] < 0 && indexBag < bagLength) {
navigating(total, arrayIndex[left], indexBag, left, right);
left++;
}
}
private void findRight(int total, int indexBag, int left, int right) {
while (right >= 0 && array[arrayIndex[right]] >= 0 && indexBag < bagLength) {
navigating(total, arrayIndex[right], indexBag, left, right);
right--;
}
}
private void navigating(int total, int index, int indexBag, int left, int right) {
result[indexBag] = index;
total += array[index];
if (total == 0 && indexBag == bagLength - 1) {
System.out.println(String.format("R[%d] %s", resultIndex++, toResultString()));
return;
}
if (total > 0) findLeft(total, indexBag + 1, left + 1, right);
else findRight(total, indexBag + 1, left, right - 1);
}
private String toResultString() {
int [] copyResult = Arrays.copyOf(result, result.length);
Arrays.sort(copyResult);
int iMax = copyResult.length - 1;
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(array[copyResult[i]]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
}
public class ThreeSumTest {
#Test
public void test() {
ThreeSum test = new ThreeSum();
test.calculate(100, 0, 3);
Assert.assertTrue(true);
}
}

Codility - Counting Elements Lessons : fastest algorithm swap

i am studying Codility chapter 2 : Counting elements.
I tried to make the exercise, and i think I have a good solution O(n). is It a valid solution ?
Is it a better solution that the BEST solution proposed in te lesson ?
Problem: You are given an integer m (1 􏰀 m 􏰀 1 000 000) and two non-empty, zero-indexed arrays A and B of n integers, a0,a1,...,an−1 and b0,b1,...,bn−1 respectively (0 􏰀 ai,bi 􏰀 m). The goal is to check whether there is a swap operation which can be performed on these arrays in such a way that the sum of elements in array A equals the sum of elements in array B after the swap. By swap operation we mean picking one element from array A and
one element from array B and exchanging them.
I tested my solution with these values :
int a[] = {2, 7, 12, 16};
int b[] = {4, 8, 9};
m = 16;
note: I commented the return to see the swapped values.
public int resultat(int[] A, int B[], int max) {
int sumA = Arrays.stream(A).sum();
int sumB = Arrays.stream(B).sum();
int[] countA = count(A, max);
int[] countB = count(B, max);
int diff = sumA - sumB;
int diffMin = 0;
if (diff % 2 != 0) {
return -1;
}
diffMin = diff / 2;
if (sumA > sumB) {
if (diff < countA.length && diffMin < countB.length && countA[diff] != 0 && countB[diffMin] != 0) {
System.out.println("A:" + diff + "- B:" + diffMin);
//return 1;
}
} else {
if (diffMin < countA.length && diff < countB.length && countB[diff] != 0 && countA[diffMin] != 0) {
System.out.println("A:" + diffMin + "- B:" + diff);
//return 1;
}
}
return -1;
}
public int[] count(int[] X, int max) {
int[] p = new int[max + 1];
Arrays.fill(p, 0);
for (int i = 0; i < X.length; i++) {
p[X[i]] += 1;
}
return p;
}
Your solution is O(n + m), because of count(A, max) and count(B, max) invocations. count() is linear.
It's not valid solution. Counter-example: A = [1, 2, 4], B = [3, 5, 1], m = 5. Answer is true, because we can swap 2 with 3. Your code throws ArrayIndexOutOfBoundsException: -2 on countB[diff], because diff is -2. Even if you secure it with, for example diff = Math.abs(sumA - sumB), the algorithm is still not correct and it will return false.
You don't need to do Arrays.fill(p, 0), int default value is 0.
Instead of p[X[i]] += 1 you could write p[X[i]]++.
Here's (i hope) a correct solution.
Please note, that counting can still be put after checking dif is not an odd number to make performance higher.
Note, too, that listA and listB arrays are used as the value at zero place is never used. This is for better understanding, too. We don't need the occurrence of the value 0 but we need the occurrence of max value.
public boolean solution(int[] A, int[] B, int max) {
int[] listA = new int[max+1];
int[] listB = new int[max+1];
int listAsum =0;
int listBsum=0;
for(int i = 0; i<A.length; i++){
listA[A[i]]++;
listAsum +=A[i];
listBsum +=B[i];
}
int diff = listAsum - listBsum;
if(diff%2 == 1) return false;
diff /=2;
for(int i=0; i<A.length; i++){
if((B[i] - diff) >= 0 && (B[i]-diff) <= max && listA[(B[i]-diff)] > 0) return true;
}
return false;
}
public boolean solution(int[] A, int[] B, int max) {
int[] count = new int[max + 1];//count(A, max);
int sum_a = 0; //Arrays.stream(A).sum();
int sum_b = 0;//Arrays.stream(B).sum();
for (int i = 0; i < A.length; i++) {
count[A[i]]++;
sum_a += A[i];
sum_b += B[i];
}
int d = sum_b - sum_a;
if (d % 2 == 1) return false;
d /= 2;
for (int i = 0; i < A.length; i++) {
if ((B[i] - d) >= 0 && (B[i] - d) <= max && count[(B[i] - d)] > 0)
return true;
}
return false;
}
public int[] count(int[] X, int max) {
int[] p = new int[max + 1];
Arrays.fill(p, 0);
for (int i = 0; i < X.length; i++) {
p[X[i]]++;
}
return p;
}

Efficient algo to find number of integers in a sorted array that are within a certain range in O(log(N)) time?

I came across a interview question that has to be done in O(logn)
Given a sorted integer array and a number, find the start and end indexes of the number in the array.
Ex1: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 3 --> Output = {3,6}
Ex2: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 5 --> Output = {-1,-1}
I am trying to find an efficient algo for this but so fat have not been successful.
You can use the concept of binary search to find the starting and ending index:
To find the starting index, you halve the array, if the value is equal to or greater than the input number, repeat with the lower half of the array, otherwise repeat with the higher half. stop when you reached an array of size 1.
To find the starting index, you halve the array, if the value is greater than the input number, repeat with the lower half of the array, otherwise repeat with the higher half. stop when you reached an array of size 1.
Note that when we reached an array of size 1, we may be one cell next to the input number, so we check if it equals the input number, if not, we fix the index by adding/decreasing 1 from the index we found.
findStartIndex(int[] A, int num)
{
int start = 0; end = A.length-1;
while (end != start)
{
mid = (end - start)/2;
if (A[mid] >= num)
end = mid;
else
start = mid;
}
if(A[start] == num)
return start;
else
return start+1;
}
findEndIndex(int[] A, int num)
{
int start = 0; end = A.length-1;
while (end != start)
{
mid = (end - start)/2;
if (A[mid] > num)
end = mid;
else
start = mid;
}
if(A[start] == num)
return start;
else
return start-1;
}
And the whole procedure:
int start = findStartIndex(A, num);
if (A[start]!=num)
{
print("-1,-1");
}
else
{
int end = findEndIndex(A, num);
print(start, end);
}
Sounds like a binary search -- log graphs iirc represent the effect of "halving" with each increment, which basically is binary search.
Pseudocode:
Set number to search for
Get length of array, check if number is at the half point
if the half is > the #, check the half of the bottom half. is <, do the inverse
repeat
if the half point is the #, mark the first time this happens as a variable storing its index
then repeat binary searches above , and then binary searches below (separately), such that you check for how far to the left/right it can repeat.
note*: and you sort binary left/right instead of just incrementally, in case your code is tested in a dataset with like 1,000,000 3's in a row or something
Is this clear enough to go from there?
The solution is to binary search the array concurrently (does't actually have to be concurrent :P ) at the start. The key is that the left and right searches are slightly different. For the right side if you encounter a dupe you have to search to the right, and for the left side if you encounter a dupe you search to the left. what you are searching for is the boundary so on the right side you check for.
yournum, not_yournum
This is the boundary and on the left side you just search for the boundary in the opposite direction. At the end return the indices of the boundaries.
Double binary search. You start with lower index = 0, upper index = length - 1. Then you check the point halfway and adjust your indexes accordingly.
The trick is that once you've found target, the pivot splits in two pivots.
Since no one has posted working code yet, I'll post some (Java):
public class DuplicateNumberRangeFinder {
public static void main(String[] args) {
int[] nums = { 0, 0, 2, 3, 3, 3, 3, 4, 7, 7, 9 };
Range range = findDuplicateNumberRange(nums, 3);
System.out.println(range);
}
public static Range findDuplicateNumberRange(int[] nums, int toFind) {
Range notFound = new Range(-1, -1);
if (nums == null || nums.length == 0) {
return notFound;
}
int startIndex = notFound.startIndex;
int endIndex = notFound.endIndex;
int n = nums.length;
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == toFind && (mid == 0 || nums[mid - 1] < toFind)) {
startIndex = mid;
break;
} else if (nums[mid] < toFind) {
low = mid + 1;
} else if (nums[mid] >= toFind) {
high = mid - 1;
}
}
low = 0;
high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == toFind && (mid == n - 1 || nums[mid + 1] > toFind)) {
endIndex = mid;
break;
} else if (nums[mid] <= toFind) {
low = mid + 1;
} else if (nums[mid] > toFind) {
high = mid - 1;
}
}
return new Range(startIndex, endIndex);
}
private static class Range {
int startIndex;
int endIndex;
public Range(int startIndex, int endIndex) {
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public String toString() {
return "[" + this.startIndex + ", " + this.endIndex + "]";
}
}
}
It may be error on my end, but Ron Teller's answer has an infinite loop when I've tested it. Here's a working example in Java, that can be tested here if you change the searchRange function to not be static.
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class RangeInArray {
// DO NOT MODIFY THE LIST
public static ArrayList<Integer> searchRange(final List<Integer> a, int b) {
ArrayList<Integer> range = new ArrayList<>();
int startIndex = findStartIndex(a, b);
if(a.get(startIndex) != b) {
range.add(-1);
range.add(-1);
return range;
}
range.add(startIndex);
range.add(findEndIndex(a, b));
return range;
}
public static int findStartIndex(List<Integer> a, int b) {
int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;
while(lowerBound < upperBound) {
midIndex = (upperBound + lowerBound) / 2;
if(b <= a.get(midIndex)) upperBound = midIndex - 1;
else lowerBound = midIndex + 1;
}
if(a.get(lowerBound) == b) return lowerBound;
return lowerBound + 1;
}
public static int findEndIndex(List<Integer> a, int b) {
int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;
while(lowerBound < upperBound) {
midIndex = (upperBound + lowerBound) / 2;
if(b < a.get(midIndex)) upperBound = midIndex - 1;
else lowerBound = midIndex + 1;
}
if(a.get(lowerBound) == b) return lowerBound;
return lowerBound - 1;
}
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
list.add(4);
list.add(4);
list.add(4);
list.add(5);
list.add(5);
list.add(5);
System.out.println("Calling search range");
for(int n : searchRange(list, 2)) {
System.out.print(n + " ");
}
}
}

How can i fin the index using exponential, binary or interpolatin search recursively? [duplicate]

This question already has an answer here:
How can I locate an index given the following constraints? [closed]
(1 answer)
Closed 9 years ago.
Given an array of n integers A[0…n−1], such that ∀i,0≤i≤n, we have that |A[i]−A[i+1]|≤1, and if A[0]=x, A[n−1]=y, we have that x<y. Locate the index j such that A[j]=z, for a given value of z, x≤ z ≤y
I dont understand the problem. I've been stuck on it for 4 days. Any idea of how to approach it with binary search, exponential search or interpolation search recursively? We are given an element z find the index j such that a [j] = z (a j) am i right?.
static int binarySearch(int[] searchArray, int x) {
int start, end, midPt;
start = 0;
end = searchArray.length - 1;
while (start <= end) {
midPt = (start + end) / 2;
if (searchArray[midPt] == x) {
return midPt;
} else if (searchArray[midPt] < x) {
start = midPt + 1;
} else {
end = midPt - 1;
}
}
return -1;
}
You can use the basic binary search algorithm. The fact that A[i] and A[i+1] differ by at most 1 guarantees you will find a match.
Pseudocode:
search(A, z):
start := 0
end := A.length - 1
while start < end:
x = A[start]
y = A[end]
mid := (start+end)/2
if x <= z <= A[mid]:
end := mid
else if A[mid] < z <= y
start := mid + 1
return start
Note that this doesn't necessarily return the first match, but that wasn't required.
to apply your algorithms your need a sorted array.
the condition of you problem says that you have an array which has elements that differ with max 1, not necessarily sorted!!!
so, here are the steps to write the code :
check if problem data respects given conditions
sort input array + saving old indexes values, so later can can initial positions of elements
implement you search methods in recursive way
Binary search source
Interpolation search source
Here's full example source :
public class Test {
// given start ======================================================
public int[] A = new int[] { 1, 1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6,
7, 8 };
public int z = 4;
// given end =======================================================
public int[] indexes = new int[A.length];
public static void main(String[] args) throws Exception {
Test test = new Test();
if (test.z < test.A[0] || test.z > test.A[test.A.length - 1]){
System.out.println("Value z="+test.z+" can't be within given array");
return;
}
sort(test.A, test.indexes);
int index = binSearch(test.A, 0, test.A.length, test.z);
if (index > -1) {
System.out.println("Binary search result index =\t"
+ test.indexes[index]);
}
index = interpolationSearch(test.A, test.z, 0, test.A.length-1);
if (index > -1) {
System.out.println("Binary search result index =\t"
+ test.indexes[index]);
}
}
public static void sort(int[] a, int[] b) {
for (int i = 0; i < a.length; i++)
b[i] = i;
boolean notSorted = true;
while (notSorted) {
notSorted = false;
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) {
int aux = a[i];
a[i] = a[i + 1];
a[i + 1] = aux;
aux = b[i];
b[i] = b[i + 1];
b[i + 1] = aux;
notSorted = true;
}
}
}
}
public static int binSearch(int[] a, int imin, int imax, int key) {
// test if array is empty
if (imax < imin)
// set is empty, so return value showing not found
return -1;
else {
// calculate midpoint to cut set in half
int imid = (imin + imax) / 2;
// three-way comparison
if (a[imid] > key)
// key is in lower subset
return binSearch(a, imin, imid - 1, key);
else if (a[imid] < key)
// key is in upper subset
return binSearch(a, imid + 1, imax, key);
else
// key has been found
return imid;
}
}
public static int interpolationSearch(int[] sortedArray, int toFind, int low,
int high) {
if (sortedArray[low] == toFind)
return low;
// Returns index of toFind in sortedArray, or -1 if not found
int mid;
if (sortedArray[low] <= toFind && sortedArray[high] >= toFind) {
mid = low + ((toFind - sortedArray[low]) * (high - low))
/ (sortedArray[high] - sortedArray[low]); // out of range is
// possible here
if (sortedArray[mid] < toFind)
low = mid + 1;
else if (sortedArray[mid] > toFind)
// Repetition of the comparison code is forced by syntax
// limitations.
high = mid - 1;
else
return mid;
return interpolationSearch(sortedArray, toFind, low, high);
} else {
return -1;
}
}
}

Categories