Collections.binarySearch() in Java - java

I'm using the binarySearch() method to find the position of an element in the list. And I don't understand why the index is -6. I see that the element is at the position 1 after sorting in descending order. Can somebody tell me why I see the position -6? Thank you!
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test
{
public static void main(String[] args)
{
List<Integer> al = new ArrayList<>();
al.add(100);
al.add(30);
al.add(10);
al.add(2);
al.add(50);
Collections.sort(al, Collections.reverseOrder());
System.out.println(al);
int index = Collections.binarySearch(al, 50);
System.out.println("Found at index " + index);
}
}
The output is:
[100, 50, 30, 10, 2]
Found at index -6

The list must be ordered into ascending natural order otherwise the results are unpredictable.
From the Javadoc
Searches the specified list for the specified object using the binary
search algorithm. The list must be sorted into ascending order
according to the natural ordering of its elements (as by the
sort(java.util.List) method) prior to making this call. If it is not
sorted, the results are undefined. If the list contains multiple
elements equal to the specified object, there is no guarantee which
one will be found.
Now, if you really want to know why the result is -6, you have to know how the method works internally. It takes the mid index and checks wether it's greater or smaller than the value you're searching for.
If it's greater (which is the case here), it takes the second half and does the same computation (low becomes middle, and max stays max).
At the end, if the key is not found, the method returns -(low + 1) which in your case is -(5 + 1) because the max index becomes the low when there is no way to split it further.

You must either sort in ascending order (per the documentation of Collections.binarySearch), or pass the custom comparator as the third parameter.
int index = Collections.binarySearch(al, 50, Collections.reverseOrder());
Failure to do this will cause undefined results.

Apart from the need for the list being sorted, binary Search
returns the index of the search key, if it is contained in the list; otherwise, (-(insertion point) - 1)
(Javadoc)
Ie: a negative index designates the negative of the index at which the item being searched would be placed if it were present, minus 1.
I.e.
-1 means it would be placed at index 0
-6 means it would be placed at index 5.

Yes, ascending order is important for this algorithm. Just replace sorting with Collections.sort(al).
As results you will have:
[2, 10, 30, 50, 100]
Found at index 3

Related

Java: Round an Integer to an element of a given List

Supose i have a List of Integers: ints = {0,10,20,30,40,50} and an input value input = 17
How do i get the closest integer of the list to the given input? For my example it would be 20.
Or to restate the question: how do i round the input to the closest value of the list?
I couldn't find a function in Java that does something like that
There is nothing in Java to do this exactly, as it's not something that's particularly useful in general.
One approach would be to notice that you are looking for the smallest deviation from input, that is to say, the value where abs(number - input) is closest to 0.
Using this knowledge, we can create a Comparator that compares numbers based on their distance from the input, and pick the smallest number based on this comparator:
List<Integer> ints = List.of(0, 10, 20, 30, 40, 50);
int input = 17;
Comparator<Integer> distanceFromInputComparator =
Comparator.comparing(value -> Math.abs(value - input));
System.out.println(ints.stream().min(distanceFromInputComparator).orElseThrow());
This returns 20, as requested.
There are some caveats with this implementation that could be addressed if necessary. It currently throws an exception if the input list is empty. It also picks arbitrarily if there are two closest (e.g. if 14 & 20 are in the list and the input is 17, it's not specified which would be picked by Stream.min()) since they're both "equal" according to the comparator.
To address the tiebreaker, you could add a secondary comparison if they're equidistant. For instance, you could do either of the following:
// Pick the smaller when there's a tie
Comparator<Integer> distanceFromInputComparator = Comparator
.comparing((Integer value) -> Math.abs(value - input))
.thenComparing(Comparator.naturalOrder());
// Pick the larger when there's a tie
Comparator<Integer> distanceFromInputComparator = Comparator
.comparing((Integer value) -> Math.abs(value - input))
.thenComparing(Comparator.reverseOrder());
An algorithm:
Iterate through the list.
For each element, compare the element with the input value.
If the input value is greater than the current element, iterate to the next element.
If the input value is greater than all elements, return some sentry value (MAX_VALUE) or something.
If the input value is lesser than a given element, then it's rounded sufficiently and you would return that element.
There are ways to account for rounding down; you'll just have to reverse the direction of the inequality. Half-rounding is left as an exercise for the reader.

Longest sequence of numbers

I was recently asked this question in an interview for which i could give an O(nlogn) solution, but couldn't find a logic for O(n) . Can someone help me with O(n) solution?
In an array find the length of longest sequence of numbers
Example :
Input : 2 4 6 7 3 1
Output: 4 (because 1,2,3,4 is a sequence even though they are not in consecutive positions)
The solution should also be realistic in terms of space consumed . i.e the solution should be realistic even with an array of 1 billion numbers
For non-consecutive numbers you needs a means of sorting them in O(n). In this case you can use BitSet.
int[] ints = {2, 4, 6, 7, 3, 1};
BitSet bs = new BitSet();
IntStream.of(ints).forEach(bs::set);
// you can search for the longer consecutive sequence.
int last = 0, max = 0;
do {
int set = bs.nextSetBit(last);
int clear = bs.nextClearBit(set + 1);
int len = clear - set;
if (len > max)
max = len;
last = clear;
} while (last > 0);
System.out.println(max);
Traverse the array once and build the hash map whose key is a number from the input array and value is a boolean variable indicating whether the element has been processed or not (initially all are false). Traverse once more and do the following: when you check number a, put value true for that element in the hash map and immediately check the hash map for the existence of the elements a-1 and a+1. If found, denote their values in the hash map as true and proceed checking their neighbors, incrementing the length of the current contigous subsequence. Stop when there are no neighbors, and update longest length. Move forward in the array and continue checking unprocessed elements. It is not obvious at the first glance that this solution is O(n), but there are only two array traversals and hash map ensures that every element of the input is processed only once.
Main lesson - if you have to reduce time complexity, it is often neccesary to use additional space.

ArrayList sorting longest sequence

I'm not asking anyone to solve this for me, I just need a little push because I have no earthly idea on where to begin with this. All I know is that I should implement collections in this and have a sort.
Write a method longestSortedSequence that returns the length of the longest sorted sequence within a list of integers. For example, if a variable called list stores the following sequence of values:
[1, 3, 5, 2, 9, 7, -3, 0, 42, 308, 17]
then the call: list.longestSortedSequence() would return the value 4 because it is the length of the longest sorted sequence within this list (the sequence -3, 0, 42, 308). If the list is empty, your method should return 0. Notice that for a non-empty list the method will always return a value of at least 1 because any individual element constitutes a sorted sequence.
Assume you are adding to the ArrayIntList class with following fields:
public class ArrayIntList
{
private int[] elementData;
private int size;
// your code goes here
}
Iterate the array, and increment the counter variable if the next element you process is larger then the last one.
If the next element is smaller, or the end of the array is reached, store the current counter value if its larger then the currently stored max value and reset the counter variable with 0.
Pseudo code:
Variable X: first item of list
Variable Y: length of sequence (initial: 1)
Variable Z: max length occurred (initial: 0)
Loop over the list starting from 2nd index
if item is higher than X
set X to item
add 1 to Y
else
if Y is higher than Z
set Z to Y
end if
set X to item
set Y to 1
end if
End-Loop
This method will restart the counter every time the sequence 'restarts', aka: it's no longer sorted. While the list is sorted it just adds 1 for each element that is in sorted order.
When the sequence stops being ordered it checks if the current sequence is longer than the longest sequence length so far. If it is, you have your new longest sequence.
Have you thought about a for loop and if else statements? i hope this doesn't give it away. think one element at a time.
Loop over your array and compare i element with i+1 element. Make a counter. While i is less than i+1 increment the counter, when i is greater than i+1 reset the counter.

In java, what is the best way to sort an integer array if only the last number item is displaced?

It's an array of integers.
It was created this way:
No element is repeated. Every time an element is added, its number is the next available integer, from 0 onwards. This way, if you add 6 elements in a row, they will be 0, 1, 2, 3, 4, 5, in that order. If you delete an element, the array shrinks, and a 'hole' is left between two of the elements, they are no longer consecutive because of that gap: 0, 1, 3, 4, 5. Then comes the problem: if you add a new element, it gets added to the end, but has the next available integer. So, the array is now 0, 1, 3, 4, 5, 2. It needs to be sorted, so the 2 can occupy its place between the 1 and the 3.
What is the best way to do it? I have thought of several methods. The list is nearly ordered, and it has the property that, when it is ordered, every element is equal to or greater than its index in the array. I am currently doing a bubble sort (don't laugh), i think quick sort is overkill, i dont want to go recursive or use temporary arrays, and i dont want to change the add-element method (which adds the element at the end), so it must be sorted immediately after adding an element (so only the last element is out of place)
Take the last element and do a insertion sort.
Maybe you should look at the idea of Insertion Sort.
You do not need to sort the whole list, just insert the last element in order:
int pos = array.length - 1:
while (pos > 0 && array[pos] < array[pos - 1]) {
tmp = array[pos - 1];
array[pos - 1] = array[pos];
array[pos] = tmp;
pos--;
}
What about using a java.util.BitSet instead? You'd get constant-time insertion and removal (finding the first free place will still take O(n) though). And with nextSetBit, you can iterate over the set in ascending order. With nextClearBir(), finding the first unused index becomes trivial, too.
//based on gpeche's code
int pos = array.length - 1;
int val = array[pos];
while (pos > 0 && array[pos-1] > val) {
array[pos] = array[pos - 1];
pos--;
}
array[pos] = val;
Are you constrained to use an array? If not, then just use a TreeSet. Why write your own sort algorithm when you can make use of standard libraries that perform this function already? It has guaranteed O(log(n)) time for insertions.
import java.util.TreeSet;
TreeSet<Integer> sortedSet = new TreeSet<Integer>();
// Add integers as needed
sortedSet.add( someInt );
// If you need an array at the end
Integer[] array = sortedSet.toArray( new Integer[sortedSet.size()] );

Search for the smallest element in a list after some value

Consider a list of integers <1,5,10> (assume sorted in ascending fashion).
Given an integer, say, key = 6, is there a utility method that returns the smallest element after key (in this case it would be 10)?
NB: Looping through the elements in the list and comparing it with key is an obvious way to do it, but I'm just wondering if there exist a utility method to do the same thing :)
Have you considered Binary Search? Collections has a binarySearch method which you could use.
From the Collections binarySearch documentation:
Returns:
index of the search key, if
it is contained in the list;
otherwise, (-(insertion point) - 1).
The insertion point is defined as the
point at which the key would be
inserted into the list: the index of
the first element greater than the
key, or list.size(), if all elements
in the list are less than the
specified key. Note that this
guarantees that the return value will
be >= 0 if and only if the key is
found.
I will let you figure out how you can use the return value of Collections.binarySearch to get the answer you need.
Binary search works, but if in fact you have a sorted set of values, then instead of a List, a SortedSet (or even better a NavigableSet), is the most natural data structure of choice.
Here's an example usage:
NavigableSet<Integer> nums = new TreeSet<Integer>(
Arrays.asList(9,1,5,7,3)
);
System.out.println(nums); // "[1, 3, 5, 7, 9]"
System.out.println(nums.first()); // "1"
System.out.println(nums.last()); // "9"
System.out.println(nums.higher(3)); // "5"
System.out.println(nums.lower(8)); // "7"
System.out.println(nums.subSet(2,8)); // "[3, 5, 7]"
System.out.println(nums.subSet(1, true, 5, true)); // "[1, 3, 5]"
There's also NavigableMap counterpart that can be even more useful in some scenarios.

Categories