Java arrays question with multiple answers - java

I have a java problem at codewars.
In this kata, you will write a function that returns the positions and
the values of the "peaks" (or local maxima) of a numeric array.
For example, the array arr = [0, 1, 2, 5, 1, 0] has a peak at
position 3 with a value of 5 (since arr[3] equals 5).
The output will be returned as a Map<String,List>with two key-value pairs:"pos"and"peaks". If there is no peak in the given array, simply
return `{"pos" => [], "peaks" => []}.
Example: pickPeaks([3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3]) should
return {pos: [3, 7], peaks: [6, 3]} (or equivalent in other
languages)
All input arrays will be valid integer arrays (although it could
still be empty), so you won't need to validate the input.
The first and last elements of the array will not be considered as
peaks (in the context of a mathematical function, we don't know what
is after and before and therefore, we don't know if it is a peak or
not).
Also, beware of plateaus! [1, 2, 2, 2, 1] has a peak while [1, 2, 2, 2, 3] and [1, 2, 2, 2, 2] do not. In case of a plateau-peak,
please only return the position and value of the beginning of the
plateau. For example: pickPeaks([1, 2, 2, 2, 1]) returns {pos: [1], peaks: [2]} (or equivalent in other languages)
Here is my answer:
public static Map<String, List<Integer>> getPeaks(int[] arr) {
HashMap<String, List<Integer>> stringListHashMap = new HashMap<>();
List<Integer> positions = new ArrayList<>();
List<Integer> values = new ArrayList<>();
values.add(arr[1]);
positions.add(1);
for (int i = 2; i < arr.length - 1; i++) {
if (values.get(values.size() - 1) < arr[i]) {
values.clear();
positions.clear();
values.add(arr[i]);
positions.add(i);
}
}
stringListHashMap.put("pos", positions);
stringListHashMap.put("peaks", values);
return stringListHashMap;
}
The above code works fine, but I don't pass the unit tests because of this ->
should support finding peaks expected:<{pos=[3, 7], peaks=[6, 3]}> but was:<{pos=[3], peaks=[6]}>
I have narrowed down the problem to this ->
Example: pickPeaks([3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3]) should return {pos: [3, 7], peaks: [6, 3]} (or equivalent in other languages)
There is clearly only one peak which is 6 and its index 3. Then what is the person asking the question trying to say? I feel like I'm missing something.

The question is asking for local maxima, not global maxima. If a value is larger than its neighbours (and is not the first or last value), it's a peak, even if there is another value elsewhere that is even greater. Hence, with the input [3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3], the eighth value (pos 7) is a peak, since the value 3 is greater than both the preceding neighbour (2) and following neighbour (2).

From what I understand, peak is surrounded by valley. So if you go up than down you have a peak, if you again go up and again down you have another peak. If you go up and than horizontal and than down, first position is peak, you just walk on plateau. It's like when you go mountain. You can climb 2 or 3 peaks in one trip.
Here is the solution:
public static Map<String, List<Integer>> getPeaks(int[] arr) {
Map<String, List<Integer>> result = new HashMap<>();
int peak = 0;
for (int i = 1; i < arr.length - 1; i++) {
if (arr[i-1] < arr[i]){
peak = i;
}
if(arr[i] > arr[i+1] && peak !=0) {
result.computeIfAbsent("pos", k -> new ArrayList<>()).add(peak);
result.computeIfAbsent("peaks", k -> new ArrayList<>()).add(arr[i]);
peak = 0;
}
}
return result;
}
I had to keep the current peak, as you can go horizontal and we need first position when you reach peak.
And I reset it when I go down.
So if arr[i-1] < arr[i] it means I reach a peak and if arr[i] > arr[i+1] && peak !=0 it means I went down.
Another mention, use Map as reference instead of HashMap. Same as you proceed for List.

Related

Find all longest ascending(not necessarily consecutive) number sequences in List

I am given an array of numbers(unsorted):
[1,2,1,2,3,1,3,7]
My task is to write a method which returns ALL longest ascending sequences of numbers. In this case for given input,the output should be:
[[1,2,3],[1,3,7]]
I have a problem in appending arrays in resulting list
public List<List<Integer>> getAscendingSequences(String url) {
List<Integer> numbers = createListFromFile(url);
List<List<Integer>> results = new ArrayList<>();
List<Integer> longestArray = new ArrayList<>();
List<Integer> currentArray = new ArrayList<>();
int maxSize = 0;
for (int i = 1; i < numbers.size(); i++) {
if (currentArray.isEmpty()) {
currentArray.add(numbers.get(i - 1));
}
if (numbers.get(i) > numbers.get(i - 1)) {
currentArray.add(numbers.get(i));
} else {
if (longestArray.size() < currentArray.size()) {
longestArray.clear();
longestArray.addAll(currentArray);
}
if(currentArray.size()==longestArray.size()){
results.add(currentArray);
}
currentArray.clear();
}
}
results.add(longestArray);
return results;
}
This returns {[1,3,7],[1,3,7],[1,2,3]}
Check this out, the problem in the code was that it wasn't handling last occurrence :-
public static List<List<Integer>> getAscendingSequences(String url) {
List<Integer> numbers = createListFromFile(url);
List<List<Integer>> results = new ArrayList<>();
List<Integer> longestArray = new ArrayList<>();
List<Integer> currentArray = new ArrayList<>();
boolean set = false;
for (int i = 1; i < numbers.size(); i++) {
set = true;
if (currentArray.isEmpty()) {
currentArray.add(numbers.get(i - 1));
set = false;
}
if (numbers.get(i) > numbers.get(i - 1)) {
currentArray.add(numbers.get(i));
set = false;
} else {
if (longestArray.size() < currentArray.size()) {
longestArray.clear();
longestArray.addAll(currentArray);
}
currentArray.clear();
}
}
results.add(longestArray);
// For handling last running condition
if(!set && currentArray.size() == longestArray.size()) {
results.add(currentArray);
}
// For handling condition where last running condition was greater than existing
if(!set && currentArray.size() > longestArray.size()) {
results.clear();
results.add(currentArray);
}
return results;
}
I'm coming a bit late to this, but this sort of problem is quite amenable to array programming techniques such as those that appear in APL and J programming languages. (The solution is probably a one-liner in those languages.) A transliteration into Java is somewhat verbose, though not as verbose as conventional iterative techniques. Here's my solution; discussion below.
List<List<Integer>> getAscendingSequences(List<Integer> in) {
var edges = IntStream.rangeClosed(0, in.size())
.filter(i -> i == 0 || i == in.size() || in.get(i-1) >= in.get(i))
.boxed()
.toList();
int max = IntStream.range(0, edges.size()-1)
.map(i -> edges.get(i+1) - edges.get(i))
.max()
.orElse(0);
return IntStream.range(0, edges.size()-1)
.filter(i -> /* max > 1 && */ max == edges.get(i+1) - edges.get(i))
.mapToObj(i -> in.subList(edges.get(i), edges.get(i+1)))
.toList();
}
The approach is conceptually similar to that shown in IamGroot's answer: first, run through the array and find ascending sequences; second, run through the sequences and find the maximum length; and third, select the sequences whose lengths match the maximum length. However, the implementation technique is completely different. Instead of loops, it uses the IntStream-over-indexes trick.
There are three stream pipelines.
The first finds the indexes of "edges" of the sequences. The key insight is that a the beginning of a new sequence occurs when the previous value is greater or equal to the current one. (This is the inverse of the more intuitive condition: we are in the midst of an ascending sequence if the previous value is less than the current value.) The filter condition also includes special cases for indexes zero and size. The resulting edges list contains indexes of all the boundaries of ascending sequences, including the head and tail boundaries.
Note that there is one more index in the edges list than there are ascending sequences. This is because each sequence is the half-open interval (lower-inclusive, upper-exclusive) between each pair of boundaries.
The second stream determines the length of each sequence by subtracting the lower boundary from the higher boundary, and then it finds the maximum length of all sequences.
The third stream processes every sequence again and selects only those whose length equals the maximum length. The sequence boundaries are also converted into a sequence using the List::subList method, which avoids creation and copying of a bunch of temporary lists.
To me, it's possible for there to be ascending sequences of length one. If you don't like these, it can be filtered out by uncommenting the additional condition in the filter of the third stream pipeline.
Sample output:
Input: [1, 2, 1, 2, 3, 1, 3, 7]
Output: [[1, 2, 3], [1, 3, 7]]
Input: [1, 2, 1, 2, 3, 1, 3, 7, 1, 3, 8]
Output: [[1, 2, 3], [1, 3, 7], [1, 3, 8]]
Input: [1, 2, 1, 2, 3, 1, 3, 7, 8]
Output: [[1, 3, 7, 8]]
Input: [3, 2, 1]
Output: [[3], [2], [1]]
Input: [1, 1, 1, 1, 2, 3, 4]
Output: [[1, 2, 3, 4]]
Input: [1, 2]
Output: [[1, 2]]
Input: [1]
Output: [[1]]
Input: []
Output: []
You can try with this approach, it is slightly different from your implementation of the logic.
Approach Here:
The below logic will prepare the listOfList with all the sequences that are in ascending order and then I am finding the maximum sequence size i.e highestListSize as the task is to prepare the list with longest sequence so, filtering the main list based on this size.
public static List<List<Integer>> getAscendingSequences(String url) {
List<Integer> numbers = createListFromFile(url);
List<List<Integer>> listOfList = new ArrayList<>();
int count = 0;
List<Integer> list = new ArrayList<>();
for (int j = count; j < numbers.size() - 1; j++) {
if (numbers.get(j + 1) > numbers.get(j)) {
list = new ArrayList<>(list);
if (!list.contains(numbers.get(j))) {
list.add(numbers.get(j));
}
list.add(numbers.get(j+1));
} else {
list = new ArrayList<>();
count++;
}
if (!list.isEmpty()) {
listOfList.add(list);
}
}
if(!listOfList.isEmpty()){
int highestListSize = listOfList.stream().mapToInt(List::size).max()
.getAsInt();
listOfList = listOfList.stream().filter(x -> x.size() == highestListSize)
.collect(Collectors.toList());
} else {
listOfList = Collections.emptyList();
}
return listOfList;
}
Testcases:
Input: {1, 2 ,1 ,2 ,3 ,1 ,3 ,7 ,1 ,3 ,8}
Output: [[1, 2, 3], [1, 3, 7], [1, 3, 8]]
Input: {1,2,1,2,3,1,3,7}
Output: [[1, 2, 3], [1, 3, 7]]
Input: {1,2,1,2,3,1,3,7,8}
Output: [[1, 3, 7, 8]]
Input: {3,2,1}
Output: []

Find minimum peak elements in an array

Question: Given an array numbers = {2, 7, 8, 5, 1, 6, 3, 9, 4}. Check the below conditions, both the conditions should be satisfied.
a[i] > a[i-1] or if first element a[i] > a[i+1]
a[i] > a[i+1] or if last element a[lastelement] > a[lastelement - 1]
Therefore:
1st Iteration - 8, 6, 9 are peak values. Remove the smallest ele. Remove 6. New arr {2, 7, 8, 5, 1, 3, 9, 4}. Output Arr - {6}
2nd Iteration - 8, 9 are peak values. Remove the smallest ele. Remove 8. New arr {2, 7, 5, 1, 3, 9, 4}. Output Arr - {6, 8}
3rd Iteration - 7, 9 are peak values. Remove the smallest ele. Remove 7. New arr {2, 5, 1, 3, 9, 4}. Output Arr - {6, 7, 8}
4th Iteration - 5, 9 are peak values. Remove the smallest ele. Remove 5. New arr {2, 1, 3, 9, 4}. Output Arr - {6, 7, 8, 5}
5th Iteration - 2, 9 are peak values. Remove the smallest ele. Remove 2. New arr {1, 3, 9, 4}. Output Arr - {6, 7, 8, 5, 2}
6th Iteration - 9 are peak values. Remove the smallest ele. Remove 9. New arr {1, 3, 4}. Output Arr - {6, 7, 8, 5, 2, 9}
7th Iteration - 4 are peak values. Remove the smallest ele. Remove 4. New arr {1, 3}. Output Arr - {6, 7, 8, 5, 2, 9, 4}
8th Iteration - 3 are peak values. Remove the smallest ele. Remove 3. New arr {1}. Output Arr - {6, 7, 8, 5, 2, 9, 4, 3}
9th Iteration - 1 are peak values. Remove the smallest ele. Remove 1. New arr {1}. Output Arr - {6, 7, 8, 5, 2, 9, 4, 3, 1}
Output: {6, 8, 7, 5, 2, 9, 4, 3, 1}
My solution is working but I am looking for optimized solution. Please let me know.
Here is my code:
public int[] findMinimumPeaks(int[] arr){
List<Integer> list1 = new ArrayList<Integer>(arr.length);
int[] output = new int[arr.length];
for(int i: arr)
list1.add(i);
for(int i =0; i<arr.length; i++){
int minIndex = minimumPeakElement(list1);
output[i] = list1.get(minIndex);
list1.remove(minIndex);
}
return output;
}
public int minimumPeakElement(List<Integer> list1){
int minIndex = 0, peakStart = Integer.MAX_VALUE, peakEnd = Integer.MAX_VALUE;
int peak = Integer.MAX_VALUE, minPeak = Integer.MAX_VALUE;
if(list1.size() >= 2){
if(list1.get(0) > list1.get(1)) peakStart = list1.get(0);
if(list1.get(list1.size() - 1) > list1.get(list1.size() - 2)) peakEnd = list1.get(list1.size() - 1);
if(peakStart < peakEnd){
minPeak = peakStart;
minIndex = 0;
}
else if(peakEnd < peakStart){
minPeak = peakEnd;
minIndex = list1.size() - 1;
}
}
for(int i=1; i<list1.size() - 1; i++){
if(list1.get(i) > list1.get(i + 1) && list1.get(i) > list1.get(i-1)) peak = list1.get(i);
if(peak < minPeak){
minPeak = peak;
minIndex = i;
}
}
return minIndex;
}
Here is an idea how to optimize asymptotic complexity.
Use single pass over elements of your initial array to split it into "up-down" "slopes" or "hills", i.e. subsequence of elements in ascending order, followed by subsequence in descending order.
Store these slopes in the following datastructure:
val slopes = MinPriorityQueue<Slope>()
class Slope(
var first: Int, // first element of the slope
var last: Int, // last element of the slope
var peak: Int, // max or peak element
var els: MaxPriorityQueue<Int>(), // all elements of the slope
var prev: Slope?, // link to the previous slope in the list or null if first
var next: Slope? // link to the next slope in the list or null if last
)
Slopes should be comparable by their peak value.
Now, having this data structure, you can poll the slope that has minimal peak value in O(log(N)). After you polled the slope, you should update the slope by removing it's peak element (i.e. poll els, then update first, last, peak), also, slope might become eligible to be merged with the previous or next slope:
Admittedly, this solution is not an easy one, having a lot of things to maintain and large number of corner cases. However, it's much better in terms of asymptotic complexity.
Initial data structure build: O(n log(n))
Polling elements while maintaining slopes: O(n log (n))
Overall complexity: O(n log(n))
Notes:
One of the corner cases, if array can have duplicate elements, then inner priority queue (els) becomes MaxPriorityQueue<Pair<Int,Int>>, i.e. you need to store the number of potentially duplicate elements along with the element value.
MinPriorityQueue and MaxPriorityQueue is an abstract heap-based data structure with min and max element at the head respectively. Can be implemented with PriorityQueue in java

Logic to remove first n elements from an array

One of the methods that I need to write for an assignment is supposed to remove the first n elements from an array. However, I am not getting the correct output. I believe I'm having an off by one error, but I am not exactly sure how to fix it.
Solutions should try to maintain the format of the code as closely as possible.
for (int i = n; i >= 0; i--) {
elementData[i] = elementData[i + 1];
size--;
}
What the code is saying is that, for every element n and below, replace it with whatever is in the index higher than it. Then reduce the size variable for every time that you do this, which should result in it looking like a remove operation when printed.
I expected the output of [0, 1, 2, 4, 6, 4, 7] to be [4, 7].
However, I got [7].
n is passed as 5 in the above case.
I realize this is an assignment question looking for an implementation, but anyone doing this in Java should use the provided APIs. For example:
public int[] trimArray(int[] source, int trimBy) {
if (trimBy > source.length) return source;
return Arrays.copyOfRange(source, trimBy, source.length);
}
trimArray(new int[]{0, 1, 2, 4, 6, 4, 7}, 5) // -> [4, 7]
You can just use this:
int[] result = new int[elementData.length - n];
for (int i = 0; i < result.length; i++) {
result[i] = elementData[n + i];
}
Which is an extended form of this:
int[] result = new int[elementData.length - n];
System.arraycopy(elementData, n, result, 0, result.length);
You can use filter:
let a = [0, 1, 2, 4, 6, 4, 7]
you want to obtain [4, 7] removing 5 first elements so type
a = a.filter((_, i) => i >= 5)
Now
a = [4, 7]
elementData = Arrays.copyOfRange( elementData, n, elementData.length ); // originalArray, startIndex, end

(Java) Use for loop to get all combinations and add to ArrayList, but ArrayList only have the last add one

I have had a problem when I am doing my java program. I want to generate all the possible combinations of the element in a String array, and store each possible one into an overall ArrayList.
I used a for loop to go through all the possible combinations and use ArrayList.add to add each String array. However, when I was trying to print out the ArrayList to check, it only have the last String array at all the positions.
If I move the System.out.println to the inside of for loop, everything looks just fine. If I move the print to the outsides the loop, it just show that I only have the same String array at all positions.
Problem related to two parts of code:
String[] inputs = {"1", "2", "3", "4", "5", "6"};
int maxLength = 4;
//Get the total number of all combinations with replacement, used for the for loop
int total = (int) Math.pow(inputs.length, maxLength);
ArrayList<String[]> allList = new ArrayList<>();
System.out.println(total);
String[] subSets = new String[maxLength];
int [] index = new int [maxLength];
Arrays.fill(index, 0);
for (int i = 0; i < total; i++)
{
for (int j = 0; j < maxLength; j++)
{
subSets[j] = inputs[index[j]];
}
allList.add(i, subSets);
if (i != (total - 1))
index = nextIndex (index, maxLength, inputs.length);
// Set the print here everything looks fine
System.out.println(Arrays.toString(allList.get(i)));
}
// However, if you pit it here to check if you get the correct ArrayList, problem happens
//for (int g = 0; g < total; g++)
//System.out.println(Arrays.toString(allList.get(g)));
Another part is:
// Get the index of the next possible combination
public static int[] nextIndex (int[] index, int maxLength, int siZe)
{
for (int i = (maxLength - 1); i > 0; i--)
{
if (index[i] == (siZe - 1))
{
index[i] = 0;
if(index[i-1] == (siZe - 1)){
continue;
}
index[i - 1]++;
break;
}else{
index[i]++;
break;
}
}
The idea of this part of my program is to generate all possible combinations (with replacement) from the String array "inputs" and store the combinations into an overall ArrayList. Printing them out just my habit to check whether each step is correct or not.
The wrong output that I keep getting (just part of the output):
[6, 6, 6, 6]
[6, 6, 6, 6]
[6, 6, 6, 6]
[6, 6, 6, 6]
[6, 6, 6, 6]
[6, 6, 6, 6]
The correct output that I want to get:
[1, 1, 1, 1]
[1, 1, 1, 2]
[1, 1, 1, 3]
[1, 1, 1, 4]
[1, 1, 1, 5]
[1, 1, 1, 6]
You are creating the subsets array just outside the for loops, so you are always updating the same array. this is the reason why, at the end you get the last permutation.
move the "String[] subSets = new String[maxLength];" just inside the for loop related to "i"
for (int i = 0; i < total; i++)
{
//create the new array here
String[] subSets = new String[maxLength];
...
}
and you will get the right output:
1296
[1, 1, 1, 1]
[1, 1, 1, 2]
[1, 1, 1, 3]
[1, 1, 1, 4]
[1, 1, 1, 5]
.....
[6, 6, 6, 4]
[6, 6, 6, 5]
[6, 6, 6, 6]
You are creating the subSets Array outside your for loop and hence everything in the list is referring to the last updated subSets object i.e. [6, 6, 6, 6]. You are adding the same object to your list again and again and hence it will update the latest values at all the places in your list.
Move it inside your for loop as follows:
for (int i = 0; i < total; i++)
{
/* Move It Here */
String[] subSets = new String[maxLength];
for (int j = 0; j < maxLength; j++)
{
subSets[j] = inputs[index[j]];
}
allList.add(subSets);
if (i != (total - 1))
index = nextIndex (index, maxLength, inputs.length);
}

Divide a group into subgroups of size k

I am stuck with one problem :
Finding all the possible ways of dividing a group of size 'n' into subgroups of size 'k'. (here n%k =0)
e.g., let set be {1,2,3,4,5,6} to divide into subgroups of 3 (k = 3,n = 6), possible sets are
a) {1,2,3},{4,5,6}
b) {1,3,5},{2,4,6}
c) {1,3,6},{2,4,5}
d) {1,3,4},{2,5,6} etc....
What i tried doing was, to first find all the combination of size k from the set.
Then loop through these combinations and find out which all combinations can be grouped together to find the list of subgroups.
But I believe the time complexity is pretty bad in this approach. Is there any better approach for this problem?
I would use a recursive method. I think this one has the optimal running time, since it exactly produces all needed subsets.
public static void solve(int[] a, int k, int i, List<List<Integer>> subsets) {
if (i == a.length) {
for (List<Integer> subset : subsets) {
System.out.print(subset);
}
System.out.println();
} else {
// loop over all subsets and try to put a[i] in
for (int j = 0; j < subsets.size(); j++) {
if (subsets.get(j).size() < k) {
// subset j not full
subsets.get(j).add(a[i]);
solve(a, k, i+1, subsets); // do recursion
subsets.get(j).remove((Integer)a[i]);
if (subsets.get(j).size() == 0) {
// don't skip empty subsets, so you won't get duplicates
break;
}
}
}
}
}
Usage:
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5, 6};
int k = 3;
List<List<Integer>> subsets = new ArrayList<List<Integer>>(a.length / k);
for (int i = 0; i < a.length / k; i++)
subsets.add(new ArrayList<Integer>(k));
solve(a, k, 0, subsets);
}
Prints:
[1, 2, 3][4, 5, 6]
[1, 2, 4][3, 5, 6]
[1, 2, 5][3, 4, 6]
[1, 2, 6][3, 4, 5]
[1, 3, 4][2, 5, 6]
[1, 3, 5][2, 4, 6]
[1, 3, 6][2, 4, 5]
[1, 4, 5][2, 3, 6]
[1, 4, 6][2, 3, 5]
[1, 5, 6][2, 3, 4]
Think about it combinatorially. If n % k != 0, you can't do it because you'll end up with one set that has less than k elements, so start off with checking whether that is the case.
Afterwards, all you need to do is recursively produce k-combinations from an n-i*k set for all i in [0; n/k]. Algorithms for producing all k-combinations of a given set can be found on SO easy enough. The idea is: there are (n choose k) possible such sets you can choose for your first set; from the remaining n-k elements, you can choose ((n-k) choose k) sets); from the remaining n-2k elements, you can choose ((n-2k) choose k) sets and so on. Assuming the order of your sets doesn't matter, you have (n choose k) * ((n-k) choose k) * ... * ((n-(n-1)k) choose k) / ((n/k)!) possibilities to choose your sets, which depending on k can be exponential in the number of elements your original set has, so if you really want to produce each and every one of them, you won't get below exponential complexity.
I found a formula for the # of possible subgroupings, in case anyone finds it interesting. (Would this be considered too off-topic? Am I posting this correctly?)
First let m = n/k be the # of subgroups. Now let the first subgroup be fixed as the first k elements of the group, the second subgroup be the next k, and so on. If we consider all possible permutations of the group, this will give us all the different subgroupings. There are n! permutations of the n elements, but we don't care about ordering, so we factor out the k! permutations of each of the m subgroups and the m! permutations of the subgroups themselves. This gives us:
n!/(m!*(k!)^m).
As a check, if k = 1 or k = n, this gives us 1 subgrouping. In the original example, n = 6, k = 3, m = 2, and we get 10 possible subgroupings (which Heuster's code found).
Now, if you compare this expression to the one G. Bach gave and use (n choose k) = n!/(k!*(n-k)!), you will see that all the (n-k)! terms cancel and it reduces to the expression above.
Bonus: if you use Stirling's approximation for n!, then the expression simplifies nicely, and you get that the # of subgroupings scales as (m^n)/m!.

Categories