Java: finding previous and next value of a navigable set - java

I have a navigable set right now that stores Integers, how could I find the previous and next elements from a specific element? I'm not sure how to use an iterator if that's how you could find those elements.
Example:
NavigableSet<Integer> values = new TreeSet<>(List.of(1, 2, 3, 4, 5));
Lets say, given 3, I want to get hands on the 2 (previous) or the 4 (next).

Higher & Lower
Check the official documentation: NavigableSet
There are the methods
higher : Returns the least element in this set strictly greater than the given element, or null if there is no such element.
lower : Returns the greatest element in this set strictly less than the given element, or null if there is no such element.
Example:
TreeSet<Integer> values = new TreeSet<>(List.of(1, 2, 4));
// 3 is not contained
int lower1 = values.lower(3); // 2
int higher1 = values.higher(3); // 4
// 2 is contained
int lower2 = values.lower(2); // 1
int higher2 = values.higher(2); // 4
Ceiling & Floor
In case you are not looking for strictly greater or smaller, there are also
ceiling: Returns the least element in this set greater than or equal to the given element, or null if there is no such element.
floor: Returns the greatest element in this set less than or equal to the given element, or null if there is no such element.
Example:
TreeSet<Integer> values = new TreeSet<>(List.of(1, 2, 4));
// 3 is not contained
int lower1 = values.floor(3); // 2
int higher1 = values.ceiling(3); // 4
// 2 is contained
int lower2 = values.floor(2); // 2
int higher2 = values.ceiling(2); // 2
Notes
Be careful with the methods returning null if there is no such element. Especially if you auto-unbox to int. I.e.
int result = values.lower(1);
will crash with a NullPointerException. Stick to Integer instead of int if you want to check for existence first.
There are also many more methods that do similar things, such as headSet or tailSet, which you can also combine with an iterator afterwards.

In a NavigableSet taken You can use floor method to get the "previous" element:
Returns the greatest element in this set less than or equal to the given element, or null if there is no such element.
and the ceiling to get the "next" element:
Returns the least element in this set greater than or equal to the given element, or null if there is no such element.
This solution can be used for any kind of object.
By the way You are using integers that have always greater or lower than another integer if they are not equals you can use higher or lower:
(higher) Returns the least element in this set strictly greater than the given element, or null if there is no such element.
(lower) Returns the greatest element in this set strictly less than the given element, or null if there is no such element.

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.

Collections.binarySearch() in 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

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.

How is 1 greater than 4?

The NavigableSet.lower(E) Javadoc says it returns the greatest element in this set strictly less than the given element, or null if there is no such element. Why is 1 the output here? Shouldn't it be 4?
NavigableSet original = new TreeSet();
original.add("1");
original.add("2");
original.add("3");
original.add("4");
original.add("10");
Object lower = original.lower("10");
System.out.println(lower);
Because the values are String(s) the Set is comparing by lexical order. Please, don't use Raw Types.
NavigableSet<Integer> original = new TreeSet<>();
original.add(1);
original.add(2);
original.add(3);
original.add(4);
original.add(10);
Object lower = original.lower(10);
System.out.println(lower);
Output is
4
thats because you are comparing Strings here, and not as assumed integers. So the actual order in the Set is : 1, 10, 2, 3, 4 !
Use the generics : NavigableSet<Integer> original = new TreeSet<>(); and add the values as integers :
original.add(1); and so on.

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.

Categories