Determining equivalent arrays in Java - java

In java, What is the most efficient way to decide whether two arrays contain all of the same elements. The arrays can have duplicates or can be unsorted. Most efficient meaning run time complexity and space complexity.

You can use HashMaps too keep track of the values you have previously seen :
public static void main(String[] args) {
System.out.println(allEquivalents(new String[][] { { "1", "3" }, { "1", "1", "3" } })); // true
System.out.println(allEquivalents(new String[][] { { "1" }, { "1", "1", "1" }, { "1", "1", "2" } })); // false
}
public static boolean allEquivalents(String[][] arrays) {
final HashMap<String, Integer> foundValues = new HashMap<String, Integer>();
for (int i = 0; i < arrays.length; i++) {
for (final String key : arrays[i]) {
// we have a value not seen in the previous array, return false
if (i > 0 && (!foundValues.containsKey(key) || foundValues.get(key) < i - 1)) {
return false;
}
foundValues.put(key, i);
}
}
// check if all the values where in the last array
for (final Integer i : foundValues.values()) {
if (i < arrays.length - 1) {
return false;
}
}
return true;
}
You are iterating only once on each value, and once on the values in the HashMap. So, the complexity is O(n), with n your total number of values.

Unless you are willing to use some additional storage (such as HashSet as mentioned in the comments), you must sort the two arrays prior to finding out if they are equivalent. Then you can do one iteration on the two arrays, and verify that each value found in the first array is also found in the second array.
Sorting the arrays would take you O(n log n), where n is the size of the longer of the two arrays. Iterating over the sorted arrays would take O(n). Therefore the overall time complexity would be O(n log n).
Assuming the arrays are sorted, this is how you find if they are equivalent (hopefully I don't have any bugs, as I didn't test it):
int i = 0;
int j = 0;
int value = -1;
while (i<arr1.length && j<arr2.length) {
int value = arr1[i];
if (!value.equals(arr2[j]))
return false;
do {i++;} while (i<arr1.length && arr1[i].equals(value));
do {j++;} while (j<arr2.length && arr2[j].equals(value));
}
while (i<arr1.length) {
if (!arr1[i].equals(value))
return false;
i++;
}
while (j<arr2.length) {
if (!arr2[j].equals(value))
return false;
j++;
}
return true;

Let's assume the arrays are of the same size, n - if not, return false.
I can think of two approaches...
Sort the arrays -> O(nlogn). Iterate through array a, comparing every element at a[i] to b[i], returning false if they aren't equal.
Use Trie -> O(n.m) , where m is just log of value of the largest integer in your arrays. http://en.wikipedia.org/wiki/Trie . Steps: put all elements of array a into trie, when you put it there for the first time, you set value associated with the key to 1. The next time you are inserting same key, just increment the associated value that is already present. Then iterate through the second array, doing decrement() operation which is the same as add(), only you decrement this time. If you decrement value to 0, remove the element. If you can't find such key in the trie, return false. After successfully iterating second array, check if trie is empty. If it is empty, return true, otherwise false.
Use auxiliary array -> O(n).
This approach applicable only if you know maximum value of the elements in the arrays:
boolean arrayEquality(int[] a, int[] b, int maxValue) {
int[] aux = new int[maxValue];
for (int i = 0; i < a.length; i++) {
int value = a[i];
aux[value]++;
}
for (int i = 0; i < b.length; i++) {
int value = b[i];
aux[value]--;
}
for (int i = 0; i < aux.length; i++) {
if (aux[i] != 0) return false;
}
return true;
}
Or you can use HashSets but you will need to implement your own custom HashSet which will not replace previous key.

This solution is O(n) amortized time, for either solution and supports nested arrays inside array of any/varying levels. e.g. you can compare
[1, [2, [3, 4], [4, 5], [3, 4]]]
Note: [3, 4] is a duplicate.
To use a Hash collection you need to use HashSet is duplicates are ignored and HashMap if duplicates are not ignored. Map is required as you might have duplicates in which case, you need to count them.
public static boolean unorderedEquivalence(Object[] arr1, Object[] arr2) {
return asSet(arr1).equals(asSet(arr2));
}
// if you want to ignore duplicates
private static Set<Object> asSet(Object[] arr1) {
Set<Object> ret = new HashSet<>();
for (Object o : arr1) {
if (o instanceof Object[])
o = asSet((Object[]) o);
ret.add(o);
}
return ret;
}
// if you want to count duplicates.
private static Map<Object, ? extends Number> asSet(Object[] arr1) {
Map<Object, Integer> ret = new HashMap<>();
for (Object o : arr1) {
if (o instanceof Object[])
o = asSet((Object[]) o);
Integer count = ret.get(o);
ret.put(o, count == null ? 1 : count+1);
}
return ret;
}

Related

Problem finding duplicate numbers in array, is my technique bad?

I used a HashMap to store the occurrences of each element, and then iterated over the hash map to get duplicated element, but something doesn't feel right about this solution.
Problem statement in Firecode.io:
Write a method duplicate to find the repeated or duplicate elements in an array. This method should return a list of repeated integers in a string with the elements sorted in ascending order (as illustrated below).
duplicate({1,3,4,2,1}) --> "[1]"
duplicate({1,3,4,2,1,2,4}) --> "[1, 2, 4]"
Note: You may use toString() method to return the standard string representation of most data structures, and Arrays.sort() to sort your result.*
Here is my code:
public String duplicate(int[] numbers) {
HashMap < Integer, Integer > hs = new HashMap < Integer, Integer > ();
for (int i = 0; i < numbers.length; i++) {
if (hs.get(numbers[i]) == null) {
hs.put(numbers[i], 1);
} else hs.put(numbers[i], (Integer) hs.get(numbers[i]) + 1);
}
int size = 0;
for (int i: hs.keySet()) {
if (hs.get(i) > 1) {
size++;
}
}
int j = 0;
int[] a = new int[size];
for (int i: hs.keySet()) {
if (hs.get(i) > 1) {
a[j++] = i;
}
}
Arrays.sort(a);
return Arrays.toString(a);
}
Here's the way I would do it: (comments for educational purposes, would probably not have them in production code.)
public String duplicate(int[] numbers) {
// holds the items we've encountered more than once.
// TreeSet<> keeps things in sorted order for us.
final SortedSet<Integer> duplicates = new TreeSet<>();
// keeps track of items we've encountered.
final Set<Integer> encountered = new HashSet<>();
// iterate over every number
for (final int number : numbers) {
// Add the item to encountered. Set.add() will return true if
// the element is new to the set.
if (!encountered.add(number)) {
// Since the element wasn't new, ensure this item exists in the duplicates collection.
duplicates.add(number);
}
}
return duplicates.toString();
}
Since you don't have to tell how many times an element is duplicated you only need a Set to remember which elements are unique and which not. If you know the element values (e.g. numbers between 1 and 10) you could further simplify Set to boolean[] or a bit vector:
int[] numbers = {1, 3, 4, 2, 2, 1, 2, 4, 4};
Set<Integer> unique = new HashSet<>();
Set<Integer> duplicates = new HashSet<>();
for (int n : numbers) {
if (!unique.add(n)) {
duplicates.add(n);
}
}
List<Integer> result = new ArrayList<>(duplicates);
result.sort(Integer::compareTo);
System.out.println(result); // [1, 2, 4]
If you are using Java 8 or beyond you can try:
public String duplicate(int[] numbers) {
Map<Integer, Integer> hs = new HashMap<>();
for ( int i : numbers ) {
hs.merge( i, 1, Integer::sum);
}
return '[' +
hs.entrySet()
.stream()
.filter( e -> e.getValue() > 1 )
.map(Entry::getKey)
.sorted()
.map(i -> i.toString())
.collect(Collectors.joining(", ")) +
']';
}

find the first duplicate element in an array having the minimal index

I have an array containing some duplicate elements like this :
find the first duplicate number for which the second occurrence has the minimal index. In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of the other number does. If there are no such elements, return -1
For a = [2, 1, 3, 5, 3, 2], the output should be
firstDuplicate(a) = 3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3 has a smaller index than the second occurrence of 2 does, so the answer is 3.
I tried this :
int firstDuplicate(int[] a) {
Set<Integer> set = new HashSet<>();
Map<Integer, Integer> hm = new HashMap<Integer,Integer>();
Map.Entry<Integer, Integer> min = null;
for(int i=0;i<a.length;i++){
// if(!hm.containsKey(a[i]))
hm.put(a[i],i);
}
for(Map.Entry<Integer,Integer> entry : hm.entrySet()){
if(min == null || entry.getValue() < min.getValue()){
min = entry;
}
}
return min == null ? new Integer(-1) : min.getKey();
}
It's not working out, but I got another solution online which is like this :
int firstDuplicate(int[] a) {
Set<Integer> set = new HashSet<>();
Map<Integer, Integer> hm = new HashMap<Integer,Integer>();
Map.Entry<Integer, Integer> min = null;
for(int i=0;i<a.length;i++){
if(set.add(a[i])==false && !hm.containsKey(a[i]))
hm.put(a[i],i);
}
for(Map.Entry<Integer,Integer> entry : hm.entrySet()){
if(min == null || entry.getValue() < min.getValue()){
min = entry;
}
}
return min == null ? new Integer(-1) : min.getKey();
}
Can anyone please explain me the use of Hashset here, as it doesn't allow the duplicates so how that if condition will be workable.
The reason your first attempt failed is that you add the array elements as keys to the Map without checking if they are already there, which means you can't know if there are any duplicates by the time you finish populating the Map.
The alternative code you found does something different. It uses the Set to determine if the current array element already appeared earlier in the array, and if that's the case, it adds it as key to the Map only if it's not already there. This means that the Map will only contain elements that appear multiple times in the array, and the index associated with each element is the occurrence of the first duplicate. I.e. for the array {2, 1, 3, 5, 3, 2}, the Map will contain {2=5, 3=4}. Then it will return the key having the smallest value (which corresponds with the index of the first duplicate).
However, the Map is unnecessary, since you only need to find one duplicate, not all of them. Use the Set to locate the first duplicate and return it:
int firstDuplicate(int[] a)
{
Set<Integer> set = new HashSet<>();
for(int i=0;i<a.length;i++){
if(!set.add(a[i])) {
return a[i];
}
}
return -1; // no duplicates found
}
This relies on set.add() returning false if the Set already contains the element you wish to add. Once it returns false for the first time, you found the first duplicate.
I would strongly recommend you to try this to get the correct results
you can make it more efficient time complexity O(n)
int firstDuplicate(int[] a){
int n = a.length;
for(int i=0; i<n; i++)
{
if(a[Math.abs(a[i])-1]<0) return Math.abs(a[i]);
else a[Math.abs(a[i])-1] = - a[Math.abs(a[i])-1];
}
return -1;
}
int firstDuplicate(int[] a){
int n = a.length;
for(int i=0; i<n; i++)
{
if(a[Math.abs(a[i])-1]<0) return Math.abs(a[i]);
else a[Math.abs(a[i])-1] = - a[Math.abs(a[i])-1];
}
return -1;
}
I will explain why and how this one works.
It's important that this constrain: 1 ≤ a[i] ≤ a.length is present, meaning that in an array like this: a = [2,8,2] this algorithm WILL NOT work because 8 is bigger than a.length in this case 3.
You'll find the explanation here as well:
Hashmap
This solution follows the idea of a hashmap. Another structure where you count hash[arr[i]-1]++ the number of occurrences for any given index i in the array. Example:
If you have arr[2,1,3,5,3,2] hashmap will begin in an 6 zero array: hashmap[0,0,0,0,0,0] because that's the size of arr. As the algorithm progress it will sum +1 in the position arr[i]-1. It's using the value as the index of the sum. At then end you get: arr[1,2,2,0,1,0].
This has O(n) in time complexity because it runs the full arr, and O(n) in time because it runs the array at least 1 time.
Without Hashmap
The idea of the algorithm above is that you don't need the extra structure of a hashmap but can use the same array to count the frequency. This might lead to a problem. Let i-th element be a or (arr[i]=a) then the count should be stored at arr[arr[i]-1] or (arr[a-1]), but when the frequency will be stored the element will be lost.
Example iteration:
a[2,1,3,5,3,2] -> a[2,1,3,5,3,2]
a[2,1,3,5,3,2] -> a[1,1,3,5,3,2]
a[1,1,3,5,3,2] -> a[1,1,1,5,3,2]
a[1,1,1,5,3,2] -> a[1,1,1,5,1,2] As you can see we lost the value of 3 when we read 5 as it stored the frequency in arr[arr[4]-1] or (arr[5-1]).
Solve the missing problem
To solve this problem first we put replace the i-th element with arr[arr[i]-1] or (arr[a-1]) then put -1 at array arr[arr[i]-1] or (arr[a-1]).
The algorithm:
Traverse the array from start to end.
For each element check if the element is less than or equal to zero or not. If negative or zero skip the element as it is frequency.
If an element (a = arr[i] – 1) is positive, then check if arr[a] is positive or not. If positive then that means it is the first occurrence of a in the array and replace arr[i] with arr[a], and assign arr[a] = -1. If arr[a] is negative, then it is not the first occurrence, then update arr[a] as arr[a]-- and update arr[i] as arr[i] = 0. You use an auxiliary value to save arr[a] that will be used in the next iteration.
Again, traverse the array and print i+1 as value and arr[i] as frequency.
Example iteration:
a[2,1,3,5,3,2] -> a[1,1,3,5,3,2] -> a[1,-1,3,5,3,2]
a[1,-1,3,5,3,2] -> a[1,-1,3,5,3,2] -> a[1,-1,-1,5,3,2]
a[1,-1,-1,5,3,2] -> a[1,-1,-1,0,3,2]
a[1,-1,-1,0,3,2] -> a[1,-1,-1,0,-1,2] -> a[1,-1,-2,0,-1,2]
a[1,-1,-2,0,-1,2] -> a[1,-1,-2,0,-1,0]
a[1,-1,-2,0,-1,0] -> a[1,-2,-2,0,-1,0]
firstDuplicate
After knowing this we can now grasp how firstDuplicate works. The idea is not to count the frequency but instead just print the index that has already a negative in frequency. When we get a negative frequency we return.
So running the algorithm we get:
With if(a[2-1]<0) or if(1<0) this comparation is between arr[arr[0]-1] or (arr[1]) and 0 so we don't return. a[2,1,3,5,3,2] -> a[2,-1,3,5,3,2]
With if(a[1-1]<0) or if(2<0) we don't return a[2,-1,3,5,3,2] -> a[-1,-1,3,5,3,2].
With if(a[3-1]<0) or if(3<0) we don't return. a[-1,-1,3,5,3,2] -> a[-2,-1,-3,5,3,2]
With if(a[5-1]<0) or if(3<0) we don't return. a[-2,-1,-3,5,3,2] -> a[-2,-1,-3,5,-3,2]
With if(a[3-1]<0) or if(-3<0) we return.
All of this is based on the idea that element-1 is the index.
You can use java 8 with lambda and stream.
Here is the code in one line :
Set<Integer> allItems = new HashSet<>();
Arrays.stream(a).filter(i -> !allItems.add(i)).findFirst().orElse(-1)
it returns what you expect
There are two ways to implement this problem, by using a HashSet with time complexity o(n) and by using nested loops o(n2)
for(int i = 0; i < a.length; i++){
for(int j = i +1; j < a.length; j++){
if(a[i] == a[j]){
System.out.println(a[i]);
return;
}
}
}
Or you can make it more efficient time complexity O(n)
int index -1;
Set<Integer> hashSet = new HashSet<Integer>();
for(int i = a.length-1; i >= 0; i--){
if(hashSet.contains(a[i])){
index = i;
}else{
hashSet.add(a[i]);
}
}
System.out.println(a[index]);
int firstDuplicate(int[] a)
{
int DupIndex = 0;
int DupValue = 0;
for (int i = 0; i < a.Length; i++)
{
for (int j = i + 1; j < a.Length; j++)
{
if (a[i] == a[j])
{
if (j < DupIndex)
{
DupIndex = j;
DupValue = a[i];
}
else if (DupIndex == 0)
{
DupIndex = j;
DupValue = a[i];
}
}
};
};
return (DupValue == 0) ? -1 : DupValue;
}
public static void main(String[] args){
int array[]={2, 1, 3, 5, 3, 2};
int tempArray[]=new int[array.length];
int index=0;
while(index< array.length){
if(++(tempArray[array[index]])==2)
break;
index++;
}
if(index> array.length){
System.out.println("No Duplicate");
}else {
System.out.println("First Duplicate " + array[index]);
}
}
Use counting sort, sweet and simple :)
Java implementation
Time complexity O(n) -- Linear
Space Complexity O(n) -- Linear
//Approach
//run a loop and try to insert it into a map.
//check if map contains key for arr[nextValue]
//if contains key, break loop and return value.
//if not, keep on adding in the map.
import java.util.HashMap;
import java.util.Map;
public class ReturnFirstRecurringCharacter {
public static void main(String[] args) {
int[] arr1 = {2,5,5,2};
Integer recurringNumber = checkForRecurringNumber(arr1);
if (recurringNumber != null) {
System.out.println(recurringNumber);
} else {
System.out.println("Undefined");
}
}
private static Integer checkForRecurringNumber(int[] arr1) {
try {
if (arr1 != null) {
Map<Integer, Integer> dataMap = new HashMap<>();
for (int i = 0; i < arr1.length; i++) {
if (dataMap.containsKey(arr1[i])) {
return arr1[i];
}
dataMap.put(arr1[i], i);
}
}
} catch (Exception e) {
System.out.println(e);
e.getStackTrace();
}
return null;
}
}
JS solution:
function solution(a) {
const map = {};
for(let i=0; i<a.length; i++) {
if(map[a[i]]) {
if(map[a[i]][0] === 1) {
map[a[i]][0]++ ;
map[a[i]][1] = i;
}
} else {
map[a[i]] = [1, i];
}
}
const data = Object.keys(map).filter(key => map[key][0] == 2).map(el => parseInt(el));
let smallest = Infinity;
let smallestData = -1;
for(let i=0; i<data.length; i++) {
if(map[data[i]][1] < smallest) {
smallest = map[data[i]][1];
smallestData = data[i];
}
}
return smallestData;
}
Solution in Javascript
function solution(a) {
let i = -1;
while (++i < a.length)
if (a.indexOf(a[i]) !== i)
return a[i];
return -1;
}
console.log(solution([2, 1, 3, 5, 3, 2])); // 3
console.log(solution([2, 2])); // 2
console.log(solution([2, 4, 3, 5, 1])); // -1

Find indexes of highest values in array. Can be 1-* values

I know how to find the highest value and index in a array(list). But I dont know how to get the indexes if there are multiple highest values in a array. I want to create a method/function that can two things: fill the array(list) with only one index if there is only one highest value, or create a arraylist if there are multiple highest values. For example I give two array's:
Array1={42,3,42,42,42,5,8};
I want to get the all the indexes of value 42 in a new array(list).
Array2={42,3,35,67};
I want to create a array(list) with only one index of value 42.
Try this for multiple indexes
List<Integer> list = new ArrayList<>();
int array[] = {1,1,2,4,5,3,1,5};
int max = array[0];
list.add(0);
for(int i=1;i<array.length;i++){
if(max<array[i]){
max = array[i];
list.clear();
list.add(i);
}else if(max==array[i])
list.add(i);
}
System.out.println(list);
For single index, use an extra variable, to store it it.
Using Java 8 features and assuming the array is not empty:
int maxValue = Arrays.stream(array)
.max()
.getAsInt();
int[] maxIndexes = IntStream.range(0, array.length)
.filter(i -> array[i] == maxValue)
.toArray();
That's 2 iterations where first you find the max value and then the indexes where an array element is equal to the max value.
Some documentation if you are not familiar with some classes/methods above:
IntStream, toArray(), getAsInt()
Depending on your scenario, having a small data set or a large data set, you might want to process the items sequentially or in parallel.
NOTE: the following code contains JUnit #Test annotation and AssertJ assertions.
Solution: sequential, one pass, small data set
This solution parses the array and keeps track of maximum and current maximum indexes. If a new maximum is found the indexes are cleared and the new maximum indexes are inserted.
#Test
public void sequential_algorithm_return_max_with_indexes() {
int[] values = new int[]{42, 3, 42, 42, 42, 5, 8};
int maxValue = Integer.MIN_VALUE;
List<Integer> maxValueIndexes = new ArrayList<>();
for (int index = 0; index < values.length; index++) {
int value = values[index];
if (value == maxValue) {
maxValueIndexes.add(index);
} else {
if (value > maxValue) {
maxValue = value;
maxValueIndexes.clear();
maxValueIndexes.add(index);
}
}
}
assertThat(maxValue).isEqualTo(42);
assertThat(maxValueIndexes).containsExactly(0, 2, 3, 4);
}
Solution: parallel, large data set
Streams are flexible and allow parallel processing.
Bellow data is represented as a pair of index-value instead of an array. This is done in order to transform the array of pairs into a stream and keep track of indexes.
Because this supposed to work in parallel, reduce method accepts 3 arguments - initial value, accumulator and combiner. This means that multiple buckets run in parallel. For each bucket there is an initial value and an accumulator used to process items sequentially. Then the parallel results of buckets are combined using the combiner argument.
#Test
public void parallel_algorithm_return_max_with_indexes() {
Pair<Integer, Integer>[] values = new Pair[]{
new Pair<>(0, 42),
new Pair<>(1, 3),
new Pair<>(2, 42),
new Pair<>(3, 42),
new Pair<>(4, 42),
new Pair<>(5, 5),
new Pair<>(6, 8),
};
ValueIndexes<Integer> maxValueIndexes = Arrays.stream(values)
.parallel()
.reduce(
new ValueIndexes<>(Integer.MIN_VALUE),
(ValueIndexes<Integer> valueIndexes, Pair<Integer, Integer> value) -> {
if (valueIndexes.getValue() == value.getValue()) {
valueIndexes.addIndex(value.getKey());
} else {
if (value.getValue() > valueIndexes.getValue()) {
valueIndexes = new ValueIndexes<>(value.getValue());
valueIndexes.addIndex(value.getKey());
}
}
return valueIndexes;
},
(valueIndexes1, valueIndexes2) -> {
if (valueIndexes1.getValue() == valueIndexes2.getValue()) {
ValueIndexes<Integer> valueIndexes = new ValueIndexes<>(valueIndexes1.getValue());
valueIndexes.addIndexes(valueIndexes1.getIndexes());
valueIndexes.addIndexes(valueIndexes2.getIndexes());
return valueIndexes;
} else {
if (valueIndexes1.getValue() > valueIndexes2.getValue()) {
return valueIndexes1;
} else {
return valueIndexes2;
}
}
}
);
assertThat(maxValueIndexes.getValue()).isEqualTo(42);
assertThat(maxValueIndexes.getIndexes()).containsExactlyInAnyOrder(0, 2, 3, 4);
}
private class ValueIndexes<T> {
private T value;
private List<Integer> indexes = new ArrayList<>();
public ValueIndexes(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public Iterable<Integer> getIndexes() {
return indexes;
}
public void addIndexes(Iterable<Integer> indexes) {
indexes.forEach(this::addIndex);
}
public void addIndex(int index) {
indexes.add(index);
}
}

Efficient way to check if individual arrays inside a 2-d array have equal lengths

Let's say we have a 2D array
int[][] arr = new int[][] { { 1, 2 }, { 3, 4, 5 }, { 6, 7 }, { 8, 9 } };
Here, arr[1] has a length of 3.
Is there any efficient way to check if all the 1-d arrays present within the 2-d array have equal lengths?
Say, without looping through the array or may be through using any data structure which can be preferred instead of a int[][]?
If you use java8, the below code (look at the inline comments) is much simpler, which uses basic stream (i.e., internal iteration) methods:
int[][] arr = new int[][] { { 1, 2 }, { 3, 4}, { 6, 7 }, { 8, 9 } };
final int[] firstSubArray = arr[0];//get the first array size
//Now with stream skip first element & check the size with rest
boolean isSubArraysSameSize = Arrays.stream(arr).//get stream from array
skip(1).//skip first element
allMatch(subArray ->
subArray.length == firstSubArray.length);//check rest all sizes match
System.out.println(isSubArraysSameSize);
Given you do not do some bookkeeping at construction time, etc. you cannot avoid turning this into an O(n) algorithm with n the number of rows. For instance:
public static boolean sameLengths(int[][] matrix) {
if(matrix == null) {
return false;
}
if(matrix.length > 0) {
if(matrix[0] == null) {
return false;
}
int n = matrix[0].length;
for(int[] row : matrix) {
if(row == null || row.length != n) {
return false;
}
}
}
return true;
}
The edge-cases are what to do with null values and what with a matrix with no rows. Here I decided that:
a null matrix returns false;
a matrix with a row equal to null returns false as well; and
a matrix with no rows, returns true (since in that case all rows have the same length).
It is easy to alter the implementation if you to handle these edge-cases differently.
In comments you said that we can use a List. So, check the difference in length at the time of adding a new list. After that, getting an answer will cost O(1):
class DataWrapper {
private List<List<Integer>> data = new ArrayList<>();
private List<Integer> lastAdded;
private boolean isDifferentLength;
public void add(List<Integer> newList) {
if (data.add(newList)) {
if (!isDifferentLength && isDifferentLengthWith(newList)) {
isDifferentLength = true;
}
lastAdded = newList;
}
}
private boolean isDifferentLengthWith(List<Integer> newList) {
return lastAdded != null && lastAdded.size() != newList.size();
}
public boolean isDifferentLength() {
return isDifferentLength;
}
}

How do I collect multiple maximum values from a List

How to get max from an ArrayList that has more than one max? For example, if an ArrrayList contains max = 20 stored at index 2, 3 and 6, how do you get all that indicies?
The obvious way is to first get maximum value by Collections.max(), then collect indicies where items are equal to max:
public <T extends Comparable<? super T>> List<Integer> maxIndicies(List<T> input) {
if (input.isEmpty()) // avoid exception thrown by Collections.max() if input is empty
return Collections.emptyList();
final T max = Collections.max(input);
return IntStream.range(0, input.size())
.filter(i -> input.get(i).compareTo(max) == 0)
.boxed()
.collect(Collectors.toList());
}
Additionally, I'd like to propose another solution where iteration is performed only once. During iteration, you need to check two things for each item: 1) if it is greater than current max, set a new max and reset result list, 2) if it is equal to current max, add its index to result list:
public <T extends Comparable<? super T>> List<Integer> maxIndicies(List<T> input) {
T max = null;
List<Integer> res = new ArrayList<>();
for (int i = 0; i < input.size(); i++) {
T item = input.get(i);
if (max == null || item.compareTo(max) > 0) { // item > max => reset
res.clear();
max = item;
res.add(i);
} else if (item.compareTo(max) == 0) // item equals current max
res.add(i);
}
return res;
}
This won't give you value of max item itself, but you can get it by any returned index, simply as:
List<Integer> maxInd = maxIndicies(list);
maxValue = maxInd.isEmpty() ? null : list.get(maxInd.get(0));
This sounds like a homework for your programming course. You should do it yourself but anyway here is the solution.
private List<Integer> getAllMaxIndices(List<Integer> aList) {
List<Integer> result = new ArrayList<Integer>();
// check argument
if (aList == null || aList.isEmpty()) {
return result;
}
// initialize the list with the index of the first element
result.add(0);
Integer tmpInt;
Integer tmpFirstIndexOfMaxInt;
Integer tmpMaxInt;
for (int i = 0; i < aList.size(); i++) {
// save the current integer and the currently maximum integer
tmpInt = aList.get(i);
tmpFirstIndexOfMaxInt = result.get(0);
tmpMaxInt = aList.get(tmpFirstIndexOfMaxInt);
// if the current element is greater than the last found
if (tmpInt > tmpMaxInt) {
// empty the result
result.clear();
// start collecting indices again
result.add(i);
}
// if the current element is equal to the last found
else if (tmpInt.intValue() == tmpMaxInt.intValue()) {
// insert the current index in the result
result.add(i);
}
}
return result;
}
I will leave it to you to write the code which tests this function.
Another approach using streams. That solution assumes that you want to know how often the max occurs (not the indices).
public static Map.Entry<Integer, Long> getMaxWithOccurrences(
List<Integer> list) {
return list
.stream()
.collect(
Collectors.groupingBy(i -> i, TreeMap::new,
Collectors.counting())).lastEntry();
}
I'd use a simple and easy to read for loop.
public List<Integer> getMaxIndices(List<Integer> values) {
Integer max = Collections.max(values);
List<Integer> maxIndices = new ArrayList<>();
for (int i = 0; i < values.size(); i++) {
if (values.get(i).equals(max)) {
maxIndices.add(Integer.valueOf(i));
}
}
return maxIndices;
}
Integer maxValue = Collections.max(list);
int numberofMax = Collections.frequency(list, maxValue);
this "numberofMax" will return how many maximum values the "list" has.
usual max finders only store the maximum met value, here you will have to maintain a list of indexes matching the maximum value.
You can do it in following way:
public void findMaxIndices() {
//Your list with numbers
List<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9));
//Sorted Map which will contain key as numbers and value as list of indices where your 'key' exists in the list
SortedMap<Integer, List<Integer>> indexMapping = new TreeMap<Integer, List<Integer>>();
for(int i = 0; i< list.size(); i++) {
//Get the number at index i
int number = list.get(i);
//Check if any index corresponding to 'number' as index has been added to your map
List<Integer> mapping = indexMapping.get(number);
if(mapping == null) {
//instantiate the list if no index has been added yet
mapping = new ArrayList<Integer>();
//Key as your 'number'
indexMapping.put(number, mapping);
}
//Add the index of the 'number' to the mapping list, which is mapped by key as 'number'
mapping.add(i);
}
//Last key in sorted map will be your highest number in the list, get the value corresponding to it. Following prints: [8,17]
int maxNumber = indexMapping.lastKey(); //Maximum number found
System.out.println(indexMapping.get(maxNumber)); //Indices where maximum number exists
}
This way, you can also find indices with lowest values easily:
indexMapping.get(indexMapping.firstKey());

Categories