Related
I am practicing a sorted search task, from testdome.com
/**
* Implement function countNumbers that accepts a sorted array of unique integers and,
* efficiently with respect to time used, counts the number of array elements that are less than the parameter lessThan.
* <p>
* For example, SortedSearch.countNumbers(new int[] { 1, 3, 5, 7 }, 4)
* should return 2 because there are two array elements less than 4.
*/
Currently according to the site my answer has a score of 50 % due to edge cases and performance, im trying to get an opinion on what i might need to add or a different approach.
Here is my code
public static int countNumbers(int[] sortedArray, int lessThan) {
int count = 0;
if(sortedArray == null) {
return 0;
}
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < sortedArray.length; i++) {
if (sortedArray[i] < lessThan) {
count++;
} else {
break;
}
}
return count;
}
And the result i get when i test it on their environment is as follows
Example case: Correct answer
Various small arrays: Correct answer
Performance test when sortedArray contains lessThan: Time limit exceeded
Performance test when sortedArray doesn't contain lessThan: Time limit exceeded
so two performance tests fail even though i cant see this tests may be i could get a suggestion here
If O(n) is giving TLE. You need something faster than O(n). Binary Search is O(logN).
public static int countNumbers(int[] sortedArray, int lessThan) {
int start = 0;
int end = sortedArray.length - 1;
int mid = 0;
while (start <= end) {
mid = start + (end - start) / 2;
if (sortedArray[mid] < lessThan) {
if (mid < sortedArray.length - 1 && sortedArray[mid + 1] < lessThan) {
start = mid + 1;
continue;
} else {
return mid + 1;
}
}
if (sortedArray[mid] >= lessThan) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return 0;
}
Or use built-in Binary Search:
Arrays.binarySearch(new int[]{1, 2, 4}, 3) + 1) * -1;
When the key is not found, it returns negative insertion position. To convert it to index, I did + 1 and multiplied by - 1 to make it positive.
Given a number current, find the number of values in an array which are larger and smaller than that value.
//sort array for binary search
int[] digits = Arrays.stream(sc.nextLine()
.split(" "))
.mapToInt(Integer::parseInt)
.sorted()
.toArray();
//for duplicate values, find higher index of current.
while(low <= high){
int mid = low + (high - low)/2;
if(digits[mid] > current){
high = mid - 1;
}else if (digits[mid] == current){
startindex = mid;
high = mid - 1;
}else{
startindex = mid;
low = mid +1;
}
}
//for duplicate values, find lower index of current.
int endindex = -1;
low = 0;
high = no_digits - 1;
while(low <= high){
int mid = low + (high - low)/2;
if(digits[mid] > current){
high = mid - 1;
}else if (digits[mid] == current){
endindex = mid;
low = mid + 1;
}else{
endindex = mid;
low = mid + 1;
}
}
System.out.println(endindex + "-" + startindex);
if(digits[0] > current){
smallest = 0;
largest = no_digits;
System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
} else if (digits[no_digits - 1] < current){
smallest = no_digits;
largest = 0;
System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
}else {
smallest = startindex;
largest = no_digits - endindex - 1;
System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
}
}
}
Sample input:
5 8 7 2 4 3 7 9 1 9 - Array of ints.
7
0
100
3
6
Output:
Smaller: 5, Greater: 3
Smaller: 0, Greater: 10
Smaller: 10, Greater: 0
Smaller: 2, Greater: 7
Smaller: 5, Greater: 5
My results:
6-5 //start and end index.
Smaller: 5, Greater: 3
-1--1
Smaller: 0, Greater: 10
9-9
Smaller: 10, Greater: 0
2-2
Smaller: 2, Greater: 7
4-4
Smaller: 5, Greater: 4
I managed to come out with the above algorithm which accounts for values larger or lower than any value in the array.
However, I am unable to find a solution to account for values that are nonexistent in the array without iterating though the array since I need to accomplish the above in O((N+Q) log N) time.
In this case, this would be the last test case where the value is 6. 6 does not exist in the array but I will still need to count all values higher/lower than 6.
Binary search algorithm produces the "insertion point" for values that do not exist in the array. Your startIndex and endIndex would give you the first "eligible" item, or the one right next to it. In other words, if you are looking for all values less than 6, the search for endpoint would yield the index of 5.
Note that you don't need to roll your own binary search algorithm: Java provides an implementation for you.
Reference: Arrays.binarySearch
EDIT The question has been edited, now it contains an additional requirement that the algorithm should work fast for multiple queries, more precisely: the overall runtime should be O((N + Q) * log(N)) where N is the size of the array and Q is the number of queries.
The approach below works only for Q = 1.
I don't see any reason not to do it in linear O(N) time.
// get this from scanner
int number = 5;
int[] array = {6, 2, 7, 4, 1, 42};
// the "algorithm"
int numLessThan = 0;
int numGreaterThan = 0;
for (int i: array) {
if (i < number) numLessThan++;
if (i > number) numGreaterThan++;
}
System.out.println(
"Num greater than: " + numGreaterThan + " " +
"Num less than: " + numLessThan
);
Output:
Num greater than: 3 Num less than: 3
If you insist on doing it with streams:
long numLessThan = Arrays.stream(array).filter(x -> x < number).count();
long numGreaterThan = Arrays.stream(array).filter(x -> x > number).count();
Even though it traverses the array twice, it is still O(N).
Since you use a Stream anyway, with a map-call no less, you're iterating the whole array anyway.
So just do
class Counters {
AtomicInteger smaller = new AtomicInteger(0);
AtomicInteger larger = new AtomicInteger(0);
private final int upperLimit;
private final int lowerLimit;
public Counters(int up, int down) {
upperLimit = up;
lowerLimit = down;
}
public void consider(int value) {
if (value > upperLimit) larger.incrementAndGet();
if (value < lowerLimit) smaller.incrementAndGet();
}
public int getSmaller() { return smaller.get(); }
public int getLarger() { return larger.get(); }
}
Counters c = new Counters(upper, lower);
IntStream.of(yourValues).parallel().forEach(c::consider);
// your output here
System.out.printf("Smaller: %d - Larger: %d", c.getSmaller(), c.getLarger());
or a more generic version
class MatchCounter<T> {
AtomicInteger count = new AtomicInteger(0);
private final Predicate<T> match;
public MatchCounter(Predicate<T> m) { match = m; }
public void consider(T value) {
if (m.test(value)) { count.incrementAndGet(); }
}
public int getCount() { return count.get(); }
}
MatchCounter<Integer> smaller = new MatchCounter<>(i -> i < lower);
MatchCounter<Integer> larger = new MatchCounter<>(i -> i > upper);
Consumer<Integer> exec = smaller::consider;
Stream.of(yourArray).parallel().forEach(exec.andThen(larger::consider));
System.out.printf("Smaller: %d - Larger: %d", smaller.getCount(), larger.getCount());
See Arrays which would come handy here.
void stats(int[] a, int sought) {
a = Arrays.copyOf(a, a.length);
Arrays.sort(a);
int index = Arrays.binarySearch(a, sought);
int smaller, larger;
if (index < 0) {
// Not found.
index = ~index; // Insertion position.
smaller = index;
larger = index:
} else {
// Found.
smaller = index;
while (smaller > 0 && a[smaller] == sought) {
--smaller;
}
while (index <= 0 && a[index] == sought) {
++index;
}
}
larger = a.length - index;
int equals = index - smaller;
System.out.printf("Smaller %d, equal %d, larger %d.%n",
smaller, equals, larger);
}
As you see, when finding an element, it would suffice to loop back O(N) which is less than sorting O(N log N).
Faster - O(log N) instead of O(N) for that part - would be if one could do a binary search on sought - 0.5 and sought + 0.5.
void stats(int[] a, int sought) {
a = Arrays.copyOf(a, a.length);
for (int i = 0; i < a.length; ++i) {
a[i] *= 2;
}
Arrays.sort(a);
int smallerI = Arrays.binarySearch(a, 2 * sought - 1);
int largerI = Arrays.binarySearch(a, 2 * sought + 1);
int smaller = ~smallerI;
int larger = a.length - ~largerI;
int equals = ~largerI - ~smallerI;
System.out.printf("Smaller %d, equal %d, larger %d.%n",
smaller, equals, larger);
}
This uses doubled integers, which has the drawback that the valid domain of array values is halved.
In your case your own binary search algorithm should opt for this latter case (without doubling), using an implicit sought + 0.5, never finding, looking for an insertion position.
Okay, so after your edit you state you want to run several queries over the same array so preparation time is less important.
To do that, build a red-black tree from the array; that will give you a sorted structure that allows a search in O(log N).
So what you do for the "smaller" count is go to the left until you find a node with a value equal or larger than the lower limit; count all left children of that. Analogue for the larger (go to the right, find equal or smaller, count to the right).
It won't matter if the item is not present in the array because you're looking for an "equal-or-larger" so if e.g. 6 is not present but you find a 5, you'll count from there - only you add 1 to the count.
You just have to filter and then count occurences. For example :
public static void main(String[] args) {
int[] values = {5, 8, 7, 2, 4, 3, 7, 9, 1, 9};
printCount(values, 7);
printCount(values, 0);
printCount(values, 100);
printCount(values, 3);
printCount(values, 6);
}
private static void printCount(int[] values, int value) {
long smallerCount = Arrays.stream(values).filter(v -> v < value).count();
long largerCount = Arrays.stream(values).filter(v -> v > value).count();
System.out.println(String.format("Smaller : %d, Larger: %d", smallerCount, largerCount));
}
I'm solving Codility questions as practice and couldn't answer one of the questions. I found the answer on the Internet but I don't get how this algorithm works. Could someone walk me through it step-by-step?
Here is the question:
/*
You are given integers K, M and a non-empty zero-indexed array A consisting of N integers.
Every element of the array is not greater than M.
You should divide this array into K blocks of consecutive elements.
The size of the block is any integer between 0 and N. Every element of the array should belong to some block.
The sum of the block from X to Y equals A[X] + A[X + 1] + ... + A[Y]. The sum of empty block equals 0.
The large sum is the maximal sum of any block.
For example, you are given integers K = 3, M = 5 and array A such that:
A[0] = 2
A[1] = 1
A[2] = 5
A[3] = 1
A[4] = 2
A[5] = 2
A[6] = 2
The array can be divided, for example, into the following blocks:
[2, 1, 5, 1, 2, 2, 2], [], [] with a large sum of 15;
[2], [1, 5, 1, 2], [2, 2] with a large sum of 9;
[2, 1, 5], [], [1, 2, 2, 2] with a large sum of 8;
[2, 1], [5, 1], [2, 2, 2] with a large sum of 6.
The goal is to minimize the large sum. In the above example, 6 is the minimal large sum.
Write a function:
class Solution { public int solution(int K, int M, int[] A); }
that, given integers K, M and a non-empty zero-indexed array A consisting of N integers, returns the minimal large sum.
For example, given K = 3, M = 5 and array A such that:
A[0] = 2
A[1] = 1
A[2] = 5
A[3] = 1
A[4] = 2
A[5] = 2
A[6] = 2
the function should return 6, as explained above. Assume that:
N and K are integers within the range [1..100,000];
M is an integer within the range [0..10,000];
each element of array A is an integer within the range [0..M].
Complexity:
expected worst-case time complexity is O(N*log(N+M));
expected worst-case space complexity is O(1), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
*/
And here is the solution I found with my comments about parts which I don't understand:
public static int solution(int K, int M, int[] A) {
int lower = max(A); // why lower is max?
int upper = sum(A); // why upper is sum?
while (true) {
int mid = (lower + upper) / 2;
int blocks = calculateBlockCount(A, mid); // don't I have specified number of blocks? What blocks do? Don't get that.
if (blocks < K) {
upper = mid - 1;
} else if (blocks > K) {
lower = mid + 1;
} else {
return upper;
}
}
}
private static int calculateBlockCount(int[] array, int maxSum) {
int count = 0;
int sum = array[0];
for (int i = 1; i < array.length; i++) {
if (sum + array[i] > maxSum) {
count++;
sum = array[i];
} else {
sum += array[i];
}
}
return count;
}
// returns sum of all elements in an array
private static int sum(int[] input) {
int sum = 0;
for (int n : input) {
sum += n;
}
return sum;
}
// returns max value in an array
private static int max(int[] input) {
int max = -1;
for (int n : input) {
if (n > max) {
max = n;
}
}
return max;
}
So what the code does is using a form of binary search (How binary search works is explained quite nicely here, https://www.topcoder.com/community/data-science/data-science-tutorials/binary-search/. It also uses an example quite similar to your problem.). Where you search for the minimum sum every block needs to contain. In the example case, you need the divide the array in 3 parts
When doing a binary search you need to define 2 boundaries, where you are certain that your answer can be found in between. Here, the lower boundary is the maximum value in the array (lower). For the example, this is 5 (this is if you divide your array in 7 blocks). The upper boundary (upper) is 15, which is the sum of all the elements in the array (this is if you divide the array in 1 block.)
Now comes the search part: In solution() you start with your bounds and mid point (10 for the example).
In calculateBlockCount you count (count ++ does that) how many blocks you can make if your sum is a maximum of 10 (your middle point/ or maxSum in calculateBlockCount).
For the example 10 (in the while loop) this is 2 blocks, now the code returns this (blocks) to solution. Then it checks whether is less or more than K, which is the number of blocks you want. If its less than K your mid point is high because you're putting to many array elements in your blocks. If it's more than K, than your mid point is too high and you're putting too little array elements in your array.
Now after the checking this, it halves the solution space (upper = mid-1).
This happens every loop, it halves the solution space which makes it converge quite quickly.
Now you keep going through your while adjusting the mid, till this gives the amount blocks which was in your input K.
So to go though it step by step:
Mid =10 , calculateBlockCount returns 2 blocks
solution. 2 blocks < K so upper -> mid-1 =9, mid -> 7 (lower is 5)
Mid =7 , calculateBlockCount returns 2 blocks
solution() 2 blocks < K so upper -> mid-1 =6, mid -> 5 (lower is 5, cast to int makes it 5)
Mid =5 , calculateBlockCount returns 4 blocks
solution() 4 blocks < K so lower -> mid+1 =6, mid -> 6 (lower is 6, upper is 6
Mid =6 , calculateBlockCount returns 3 blocks
So the function returns mid =6....
Hope this helps,
Gl learning to code :)
Edit. When using binary search a prerequisite is that the solution space is a monotonic function. This is true in this case as when K increases the sum is strictly decreasing.
Seems like your solution has some problems. I rewrote it as below:
class Solution {
public int solution(int K, int M, int[] A) {
// write your code in Java SE 8
int high = sum(A);
int low = max(A);
int mid = 0;
int smallestSum = 0;
while (high >= low) {
mid = (high + low) / 2;
int numberOfBlock = blockCount(mid, A);
if (numberOfBlock > K) {
low = mid + 1;
} else if (numberOfBlock <= K) {
smallestSum = mid;
high = mid - 1;
}
}
return smallestSum;
}
public int sum(int[] A) {
int total = 0;
for (int i = 0; i < A.length; i++) {
total += A[i];
}
return total;
}
public int max(int[] A) {
int max = 0;
for (int i = 0; i < A.length; i++) {
if (max < A[i]) max = A[i];
}
return max;
}
public int blockCount(int max, int[] A) {
int current = 0;
int count = 1;
for (int i = 0; i< A.length; i++) {
if (current + A[i] > max) {
current = A[i];
count++;
} else {
current += A[i];
}
}
return count;
}
}
This is helped me in case anyone else finds it helpful.
Think of it as a function: given k (the block count) we get some largeSum.
What is the inverse of this function? It's that given largeSum we get a k. This inverse function is implemented below.
In solution() we keep plugging guesses for largeSum into the inverse function until it returns the k given in the exercise.
To speed up the guessing process, we use binary search.
public class Problem {
int SLICE_MAX = 100 * 1000 + 1;
public int solution(int blockCount, int maxElement, int[] array) {
// maxGuess is determined by looking at what the max possible largeSum could be
// this happens if all elements are m and the blockCount is 1
// Math.max is necessary, because blockCount can exceed array.length,
// but this shouldn't lower maxGuess
int maxGuess = (Math.max(array.length / blockCount, array.length)) * maxElement;
int minGuess = 0;
return helper(blockCount, array, minGuess, maxGuess);
}
private int helper(int targetBlockCount, int[] array, int minGuess, int maxGuess) {
int guess = minGuess + (maxGuess - minGuess) / 2;
int resultBlockCount = inverseFunction(array, guess);
// if resultBlockCount == targetBlockCount this is not necessarily the solution
// as there might be a lower largeSum, which also satisfies resultBlockCount == targetBlockCount
if (resultBlockCount <= targetBlockCount) {
if (minGuess == guess) return guess;
// even if resultBlockCount == targetBlockCount
// we keep searching for potential lower largeSum that also satisfies resultBlockCount == targetBlockCount
// note that the search range below includes 'guess', as this might in fact be the lowest possible solution
// but we need to check in case there's a lower one
return helper(targetBlockCount, array, minGuess, guess);
} else {
return helper(targetBlockCount, array, guess + 1, maxGuess);
}
}
// think of it as a function: given k (blockCount) we get some largeSum
// the inverse of the above function is that given largeSum we get a k
// in solution() we will keep guessing largeSum using binary search until
// we hit k given in the exercise
int inverseFunction(int[] array, int largeSumGuess) {
int runningSum = 0;
int blockCount = 1;
for (int i = 0; i < array.length; i++) {
int current = array[i];
if (current > largeSumGuess) return SLICE_MAX;
if (runningSum + current <= largeSumGuess) {
runningSum += current;
} else {
runningSum = current;
blockCount++;
}
}
return blockCount;
}
}
From anhtuannd's code, I refactored using Java 8. It is slightly slower. Thanks anhtuannd.
IntSummaryStatistics summary = Arrays.stream(A).summaryStatistics();
long high = summary.getSum();
long low = summary.getMax();
long result = 0;
while (high >= low) {
long mid = (high + low) / 2;
AtomicLong blocks = new AtomicLong(1);
Arrays.stream(A).reduce(0, (acc, val) -> {
if (acc + val > mid) {
blocks.incrementAndGet();
return val;
} else {
return acc + val;
}
});
if (blocks.get() > K) {
low = mid + 1;
} else if (blocks.get() <= K) {
result = mid;
high = mid - 1;
}
}
return (int) result;
I wrote a 100% solution in python here. The result is here.
Remember: You are searching the set of possible answers not the array A
In the example given they are searching for possible answers. Consider [5] as 5 being the smallest max value for a block. And consider [2, 1, 5, 1, 2, 2, 2] 15 as the largest max value for a block.
Mid = (5 + 15) // 2. Slicing out blocks of 10 at a time won't create more than 3 blocks in total.
Make 10-1 the upper and try again (5+9)//2 is 7. Slicing out blocks of 7 at a time won't create more than 3 blocks in total.
Make 7-1 the upper and try again (5+6)//2 is 5. Slicing out blocks of 5 at a time will create more than 3 blocks in total.
Make 5+1 the lower and try again (6+6)//2 is 6. Slicing out blocks of 6 at a time won't create more than 3 blocks in total.
Therefore 6 is the lowest limit to impose on the sum of a block that will permit breaking into 3 blocks.
import java.util.Scanner;
public class Test {
public static void display(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.printf("%d\n", arr[i]);
}
System.out.printf("\n");
}
// this one is the problematic one.
public static int min(int[] arr, int start, int end) {
int middle = start + (end - start)/2;
if (start >= middle) {
return arr[start];
}
int min_1 = min(arr, start, middle);
int min_3 = (min_1 <= arr[middle])? min_1 : arr[middle];
int min_2 = min(arr, middle+1, end);
return (min_3 <= min_2)? min_3 : min_2;
}
public static void main(String[] args) {
int len;
Scanner in = new Scanner(System.in);
System.out.printf("Enter the length of array: ");
len = in.nextInt();
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int)(10*Math.random()+1);
}
arr[len-1] = 0;
display(arr);
System.out.printf("%d is the min", min(arr, 0, arr.length-1));
}
}
Problem is: the method min() works for the other places of minimum number (which is "0" in this example) for example if I put "0" in the 3rd place (by changing arr[len-1] = 0 part), method returns me 0. If I change the input length "len" to 3,7,13 or 14 it returns me 0, which is correct however for other lengths such as 4,5,8,9, it returns the second minimum number. And what is even more interesting is that if I make the end point "arr.length" instead of "arr.length-1", it won't give an error for some lengths (for instance for len = 6, it gives ArrayIndexOutOfBoundsException), moreover it works correctly and returns 0 in the situations where it doesn't give an error. I really don't understand what is the exact problem (Note: I must find the min number recursively and by finding the min of first half and second half and comparing them, so other solutions will not help me :/).
let's consider an execution passing 4, and let's say it generates arr = [6, 10, 2, 0]
The first invocation of min() will be passed 0, 3 as start,end. middle will be 0 + (3 - 0) / 2 = 1
so, you'll call
min_1 = min(arr, 0, 1)
in this execution min will have middle = 0 + (1 - 0) / 2 = 0, end hence it will return arr[0], i.e. 6.
We're back in the outer min(), where min_3 is computed as the min between (6 and 10, i.e. 6. Then we compute min_2 = min(arr, 2, 3)
we're now in this inner execution of min. Here middle = 2 + (3 - 2) / 2 = 2. As middle == start, we return arr[2], i.e. 2.
We're back in the outer min, where we finally compute the min between min_3 and min_1, which is 2, and return it. Note that we never processed arr[3]
The problem here is your condition to stop the recursion. The idea here is that you want to stop recursion when you've degenerated to a case where you're looking at an interva of size 1. The condition for that should not be start >= middle, but rather start >= end.
This question already has answers here:
Finding multiple entries with binary search
(15 answers)
Closed 3 years ago.
I've been tasked with creating a method that will print all the indices where value x is found in a sorted array.
I understand that if we just scanned through the array from 0 to N (length of array) it would have a running time of O(n) worst case. Since the array that will be passed into the method will be sorted, I'm assuming that I can take advantage of using a Binary Search since this will be O(log n). However, this only works if the array has unique values. Since the Binary Search will finish after the first "find" of a particular value. I was thinking of doing a Binary Search for finding x in the sorted array, and then checking all values before and after this index, but then if the array contained all x values, it doesn't seem like it would be that much better.
I guess what I'm asking is, is there a better way to find all the indices for a particular value in a sorted array that is better than O(n)?
public void PrintIndicesForValue42(int[] sortedArrayOfInts)
{
// search through the sortedArrayOfInts
// print all indices where we find the number 42.
}
Ex: sortedArray = { 1, 13, 42, 42, 42, 77, 78 } would print: "42 was found at Indices: 2, 3, 4"
You will get the result in O(lg n)
public static void PrintIndicesForValue(int[] numbers, int target) {
if (numbers == null)
return;
int low = 0, high = numbers.length - 1;
// get the start index of target number
int startIndex = -1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] > target) {
high = mid - 1;
} else if (numbers[mid] == target) {
startIndex = mid;
high = mid - 1;
} else
low = mid + 1;
}
// get the end index of target number
int endIndex = -1;
low = 0;
high = numbers.length - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] > target) {
high = mid - 1;
} else if (numbers[mid] == target) {
endIndex = mid;
low = mid + 1;
} else
low = mid + 1;
}
if (startIndex != -1 && endIndex != -1){
for(int i=0; i+startIndex<=endIndex;i++){
if(i>0)
System.out.print(',');
System.out.print(i+startIndex);
}
}
}
Well, if you actually do have a sorted array, you can do a binary search until you find one of the indexes you're looking for, and from there, the rest should be easy to find since they're all next to each-other.
once you've found your first one, than you go find all the instances before it, and then all the instances after it.
Using that method you should get roughly O(lg(n)+k) where k is the number of occurrences of the value that you're searching for.
EDIT:
And, No, you will never be able to access all k values in anything less than O(k) time.
Second edit: so that I can feel as though I'm actually contributing something useful:
Instead of just searching for the first and last occurrences of X than you can do a binary search for the first occurence and a binary search for the last occurrence. which will result in O(lg(n)) total. once you've done that, you'll know that all the between indexes also contain X(assuming that it's sorted)
You can do this by searching checking if the value is equal to x , AND checking if the value to the left(or right depending on whether you're looking for the first occurrence or the last occurrence) is equal to x.
public void PrintIndicesForValue42(int[] sortedArrayOfInts) {
int index_occurrence_of_42 = left = right = binarySearch(sortedArrayOfInts, 42);
while (left - 1 >= 0) {
if (sortedArrayOfInts[left-1] == 42)
left--;
}
while (right + 1 < sortedArrayOfInts.length) {
if (sortedArrayOfInts[right+1] == 42)
right++;
}
System.out.println("Indices are from: " + left + " to " + right);
}
This would run in O(log(n) + #occurrences)
Read and understand the code. It's simple enough.
Below is the java code which returns the range for which the search-key is spread in the given sorted array:
public static int doBinarySearchRec(int[] array, int start, int end, int n) {
if (start > end) {
return -1;
}
int mid = start + (end - start) / 2;
if (n == array[mid]) {
return mid;
} else if (n < array[mid]) {
return doBinarySearchRec(array, start, mid - 1, n);
} else {
return doBinarySearchRec(array, mid + 1, end, n);
}
}
/**
* Given a sorted array with duplicates and a number, find the range in the
* form of (startIndex, endIndex) of that number. For example,
*
* find_range({0 2 3 3 3 10 10}, 3) should return (2,4). find_range({0 2 3 3
* 3 10 10}, 6) should return (-1,-1). The array and the number of
* duplicates can be large.
*
*/
public static int[] binarySearchArrayWithDup(int[] array, int n) {
if (null == array) {
return null;
}
int firstMatch = doBinarySearchRec(array, 0, array.length - 1, n);
int[] resultArray = { -1, -1 };
if (firstMatch == -1) {
return resultArray;
}
int leftMost = firstMatch;
int rightMost = firstMatch;
for (int result = doBinarySearchRec(array, 0, leftMost - 1, n); result != -1;) {
leftMost = result;
result = doBinarySearchRec(array, 0, leftMost - 1, n);
}
for (int result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n); result != -1;) {
rightMost = result;
result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n);
}
resultArray[0] = leftMost;
resultArray[1] = rightMost;
return resultArray;
}
Another result for log(n) binary search for leftmost target and rightmost target. This is in C++, but I think it is quite readable.
The idea is that we always end up when left = right + 1. So, to find leftmost target, if we can move right to rightmost number which is less than target, left will be at the leftmost target.
For leftmost target:
int binary_search(vector<int>& nums, int target){
int n = nums.size();
int left = 0, right = n - 1;
// carry right to the greatest number which is less than target.
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
// when we are here, right is at the index of greatest number
// which is less than target and since left is at the next,
// it is at the first target's index
return left;
}
For the rightmost target, the idea is very similar:
int binary_search(vector<int>& nums, int target){
while(left <= right){
int mid = (left + right) / 2;
// carry left to the smallest number which is greater than target.
if(nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
// when we are here, left is at the index of smallest number
// which is greater than target and since right is at the next,
// it is at the first target's index
return right;
}
I came up with the solution using binary search, only thing is to do the binary search on both the sides if the match is found.
public static void main(String[] args) {
int a[] ={1,2,2,5,5,6,8,9,10};
System.out.println(2+" IS AVAILABLE AT = "+findDuplicateOfN(a, 0, a.length-1, 2));
System.out.println(5+" IS AVAILABLE AT = "+findDuplicateOfN(a, 0, a.length-1, 5));
int a1[] ={2,2,2,2,2,2,2,2,2};
System.out.println(2+" IS AVAILABLE AT = "+findDuplicateOfN(a1, 0, a1.length-1, 2));
int a2[] ={1,2,3,4,5,6,7,8,9};
System.out.println(10+" IS AVAILABLE AT = "+findDuplicateOfN(a2, 0, a2.length-1, 10));
}
public static String findDuplicateOfN(int[] a, int l, int h, int x){
if(l>h){
return "";
}
int m = (h-l)/2+l;
if(a[m] == x){
String matchedIndexs = ""+m;
matchedIndexs = matchedIndexs+findDuplicateOfN(a, l, m-1, x);
matchedIndexs = matchedIndexs+findDuplicateOfN(a, m+1, h, x);
return matchedIndexs;
}else if(a[m]>x){
return findDuplicateOfN(a, l, m-1, x);
}else{
return findDuplicateOfN(a, m+1, h, x);
}
}
2 IS AVAILABLE AT = 12
5 IS AVAILABLE AT = 43
2 IS AVAILABLE AT = 410236578
10 IS AVAILABLE AT =
I think this is still providing the results in O(logn) complexity.
A Hashmap might work, if you're not required to use a binary search.
Create a HashMap where the Key is the value itself, and then value is an array of indices where that value is in the array. Loop through your array, updating each array in the HashMap for each value.
Lookup time for the indices for each value will be ~ O(1), and creating the map itself will be ~ O(n).
Find_Key(int arr[], int size, int key){
int begin = 0;
int end = size - 1;
int mid = end / 2;
int res = INT_MIN;
while (begin != mid)
{
if (arr[mid] < key)
begin = mid;
else
{
end = mid;
if(arr[mid] == key)
res = mid;
}
mid = (end + begin )/2;
}
return res;
}
Assuming the array of ints is in ascending sorted order; Returns the index of the first index of key occurrence or INT_MIN. Runs in O(lg n).
It is using Modified Binary Search. It will be O(LogN). Space complexity will be O(1).
We are calling BinarySearchModified two times. One for finding start index of element and another for finding end index of element.
private static int BinarySearchModified(int[] input, double toSearch)
{
int start = 0;
int end = input.Length - 1;
while (start <= end)
{
int mid = start + (end - start)/2;
if (toSearch < input[mid]) end = mid - 1;
else start = mid + 1;
}
return start;
}
public static Result GetRange(int[] input, int toSearch)
{
if (input == null) return new Result(-1, -1);
int low = BinarySearchModified(input, toSearch - 0.5);
if ((low >= input.Length) || (input[low] != toSearch)) return new Result(-1, -1);
int high = BinarySearchModified(input, toSearch + 0.5);
return new Result(low, high - 1);
}
public struct Result
{
public int LowIndex;
public int HighIndex;
public Result(int low, int high)
{
LowIndex = low;
HighIndex = high;
}
}
public void printCopies(int[] array)
{
HashMap<Integer, Integer> memberMap = new HashMap<Integer, Integer>();
for(int i = 0; i < array.size; i++)
if(!memberMap.contains(array[i]))
memberMap.put(array[i], 1);
else
{
int temp = memberMap.get(array[i]); //get the number of occurances
memberMap.put(array[i], ++temp); //increment his occurance
}
//check keys which occured more than once
//dump them in a ArrayList
//return this ArrayList
}
Alternatevely, instead of counting the number of occurances, you can put their indices in a arraylist and put that in the map instead of the count.
HashMap<Integer, ArrayList<Integer>>
//the integer is the value, the arraylist a list of their indices
public void printCopies(int[] array)
{
HashMap<Integer, ArrayList<Integer>> memberMap = new HashMap<Integer, ArrayList<Integer>>();
for(int i = 0; i < array.size; i++)
if(!memberMap.contains(array[i]))
{
ArrayList temp = new ArrayList();
temp.add(i);
memberMap.put(array[i], temp);
}
else
{
ArrayList temp = memberMap.get(array[i]); //get the lsit of indices
temp.add(i);
memberMap.put(array[i], temp); //update the index list
}
//check keys which return lists with length > 1
//handle the result any way you want
}
heh, i guess this will have to be posted.
int predefinedDuplicate = //value here;
int index = Arrays.binarySearch(array, predefinedDuplicate);
int leftIndex, rightIndex;
//search left
for(leftIndex = index; array[leftIndex] == array[index]; leftIndex--); //let it run thru it
//leftIndex is now the first different element to the left of this duplicate number string
for(rightIndex = index; array[rightIndex] == array[index]; rightIndex++); //let it run thru it
//right index contains the first different element to the right of the string
//you can arraycopy this [leftIndex+1, rightIndex-1] string or just print it
for(int i = leftIndex+1; i<rightIndex; i++)
System.out.println(array[i] + "\t");