Changing 2D ArrayList code to 2D array code - java

I found this code online and it works well to permute through the given array and return all possible combinations of the numbers given. Does anyone know how to change this code to incorporate a 2D array instead?
public static ArrayList<ArrayList<Integer>> permute(int[] numbers) {
ArrayList<ArrayList<Integer>> permutations = new ArrayList<ArrayList<Integer>>();
permutations.add(new ArrayList<Integer>());
for ( int i = 0; i < numbers.length; i++ ) {
ArrayList<ArrayList<Integer>> current = new ArrayList<ArrayList<Integer>>();
for ( ArrayList<Integer> p : permutations ) {
for ( int j = 0, n = p.size() + 1; j < n; j++ ) {
ArrayList<Integer> temp = new ArrayList<Integer>(p);
temp.add(j, numbers[i]);
current.add(temp);
}
}
permutations = new ArrayList<ArrayList<Integer>>(current);
}
return permutations;
}
This is what I have attempted:
public static int[][] permute(int[] numbers){
int[][] permutations = new int[24][4];
permutations[0] = new int[4];
for ( int i = 0; i < numbers.length; i++ ) {
int[][] current = new int[24][4];
for ( int[] permutation : permutations ) {
for ( int j = 0; j < permutation.length; j++ ) {
permutation[j] = numbers[i];
int[] temp = new int[4];
current[i] = temp;
}
}
permutations = current;
}
return permutations;
}
However this returns all zeroes. I chose 24 and 4 because that is the size of the 2D array that I need.
Thanks

It’s not really that easy. The original code exploits the more dynamic behaviour of ArrayList, so a bit of hand coding will be necessary. There are many correct thoughts in your code. I tried to write an explanation of the issues I saw, but it became too long, so I decided to modify your code instead.
The original temp.add(j, numbers[i]); is the hardest part to do with arrays since it invloves pushing the elements to the right of position j one position to the right. In my version I create a temp array just once in the middle loop and shuffle one element at a time in the innermost loop.
public static int[][] permute(int[] numbers) {
// Follow the original here and create an array of just 1 array of length 0
int[][] permutations = new int[1][0];
for (int i = 0; i < numbers.length; i++) {
// insert numbers[i] into each possible position in each array already in permutations.
// create array with enough room: when before we had permutations.length arrays, we will now need:
int[][] current = new int[(permutations[0].length + 1) * permutations.length][];
int count = 0; // number of new permutations in current
for (int[] permutation : permutations) {
// insert numbers[i] into each of the permutation.length + 1 possible positions of permutation.
// to avoid too much shuffling, create a temp array
// and use it for all new permutations made from permutation.
int[] temp = Arrays.copyOf(permutation, permutation.length + 1);
for (int j = permutation.length; j > 0; j--) {
temp[j] = numbers[i];
// remember to make a copy of the temp array
current[count] = temp.clone();
count++;
// move element to make room for numbers[i] at next position to the left
temp[j] = temp[j - 1];
}
temp[0] = numbers[i];
current[count] = temp.clone();
count++;
}
assert count == current.length : "" + count + " != " + current.length;
permutations = current;
}
return permutations;
}
My trick with the temp array means I don’t get the permutations in the same order as in the origianl code. If this is a requirement, you may copy permutation into temp starting at index 1 and shuffle the opposite way in the loop. System.arraycopy() may do the initial copying.

The problem here is that you really need to implement properly the array version of the ArrayList.add(int,value) command. Which is to say you do an System.arraycopy() and push all the values after j, down one and then insert the value at j. You currently set the value. But, that overwrites the value of permutation[j], which should actually have been moved to permutations[j+1] already.
So where you do:
permutation[j] = numbers[i];
It should be:
System.arraycopy(permutation,j, permutations, j+1, permutations.length -j);
permutation[j] = numbers[i];
As the ArrayList.add(int,value) does that. You basically wrongly implemented it as .set().
Though personally I would scrap the code and go with something to dynamically make those values on the fly. A few more values and you're talking something prohibitive with regard to memory. It isn't hard to find the nth index of a permutation. Even without allocating any memory at all. (though you need a copy of the array if you're going to fiddle with such things without incurring oddities).
public static int[] permute(int[] values, long index) {
int[] returnvalues = Arrays.copyOf(values,values.length);
if (permutation(returnvalues, index)) return returnvalues;
else return null;
}
public static boolean permutation(int[] values, long index) {
return permutation(values, values.length, index);
}
private static boolean permutation(int[] values, int n, long index) {
if ((index == 0) || (n == 0)) return (index == 0);
int v = n-(int)(index % n);
int temp = values[n];
values[n] = values[v];
values[v] = temp;
return permutation(values,n-1,index/n);
}

Related

Twin array Find a Minimum value from each Array index must be different

You are given two arrays A and B each containing n, integers. You need to choose exactly one number from A and exactly one number from B such that the index of the two chosen numbers is not same and the sum of the 2 chosen values is minimum.
Your objective is to find and print this minimum value.
For example in the image shown below is the minimum sum.
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
static int twinArrays(int[] ar1, int[] ar2){
// Complete this function
int minAr1 = ar1[0];
int minAr2;
int index = 0;
for(int i =0; i<ar1.length;i++) {
if(ar1[i]<minAr1) {
minAr1 = ar1[i];
index = i;
}
}
if(index == 0) {
minAr2 = ar2[1];
}else {
minAr2 =ar2[0];
}
for(int j=0;j<ar2.length;j++) {
if(j!=index && minAr2>ar2[j]) {
minAr2 =ar2[j];
}
}
return minAr1+minAr2;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
if(n>=2 && n<=1000000) {
int[] ar1 = new int[n];
for(int ar1_i = 0; ar1_i < n; ar1_i++){
int temp1 = in.nextInt();
if(temp1>=1&&temp1<=1000000) {
ar1[ar1_i] = temp1;
}
}
int[] ar2 = new int[n];
for(int ar2_i = 0; ar2_i < n; ar2_i++){
int temp2 = in.nextInt();
if(temp2>=1&&temp2<=1000000) {
ar2[ar2_i] = temp2;
}
}
int result = twinArrays(ar1, ar2);
System.out.println(result);
}
}
}
i have Implemented that is working fine but still something is missing so some test case failing could anyone give me some clue and hint to optimize my code if you find any defect in this code
Your code works by finding the minimum value from the first array, then finding the minimum value from the second array that isn't at the same position of the minimum in the first array. The problem with this approach is that it isn't guaranteed to find the overall minimum sum. For example, consider these small arrays:
[0, 1], [0, 100]
Here, if you take the minimum value of the first array (0) and the minimum value of the second array that isn't at the same position (100), you get the sum 100. However, the correct sum to pick is the smallest value from the second array (0) and the second-smallest value from the first array (1) for a total of 1.
One observation that's useful here is that the minimum-sum pair must be one of the following:
The sum of the smallest values from each array.
The sum of the smallest value from one array and the second-smallest value from the other.
Your solution is on the right track here, except you don't consider taking the smallest from the second array and the second smallest from the first. Try editing your code to account for that case.
One simple observation is that if minimum elements from both arrays are at different position, Sum of both elements would be an answer so after sorting answer in this case would be A[0]+B[0], but if both are at the same position, there are two possibilities for answer after sorting both the arrays.
Suppose we have two arrays A and B which are sorted in increasing order. Then there are two possible answers and the actual answer would be minimum of them.
Take minimum possible number from array A so that number would be A[0] and now find the minimum possible number from array B such that index of that element is not equal to 0 so best possible choice would be B[1]. Therefore answer in this case is A[0]+B[1].
Another possibility is that we consider minimum possible number from array B first and then go to choose a number from array A so here those numbers would be A[1] and B[0] respectively and thus answer in this case is A[1]+B[0].
Now for the final answer, we can take minimum of both these possible answers.
Final_Answer = min(A[0]+B[1],A[1]+B[0]);
If you don't want to sort the Arrays due to tight time constraint. You can just keep track of First and Second minimum element of both arrays and use it in place of A[0],A[1] in above equation respectively.
A sample code using first and second minimum,
static int twinArrays(int[] ar1, int[] ar2){
int first_minimum_ar1 = Integer.MAX_VALUE;
int second_minimum_ar1 = Integer.MAX_VALUE;
int index_ar1=-1;
int first_minimum_ar2 = Integer.MAX_VALUE;
int second_minimum_ar2 = Integer.MAX_VALUE;
int index_ar2=-1;
for(int i=0;i<ar1.length;i++)
{
int element = ar1[i];
if(first_minimum_ar1>=element)
{
second_minimum_ar1=first_minimum_ar1;
first_minimum_ar1=element;
index_ar1=i;
}
else if(second_minimum_ar1>element)
{
second_minimum_ar1=element;
}
}
for(int i=0;i<ar2.length;i++)
{
int element = ar2[i];
if(first_minimum_ar2>=element)
{
second_minimum_ar2=first_minimum_ar2;
first_minimum_ar2=element;
index_ar2=i;
}
else if(second_minimum_ar2>element)
{
second_minimum_ar2=element;
}
}
if(index_ar2!=index_ar1)
return first_minimum_ar1+first_minimum_ar2;
return Math.min(first_minimum_ar1+second_minimum_ar2,first_minimum_ar2+second_minimum_ar1);
}
What I did was, sort one array(ar1) in ascending order(take ar[0]) and the other in descending order(ar2) and then take (ar2[ar2.length-1]).
class TwinArray{
static int twinArrays(int[] ar1, int[] ar2){
Arrays.sort(ar1);
Integer[] Ar2 = new Integer[ar2.length];
for (int i=0;i<ar2.length;i++)
Ar2[i]=ar2[i];
Arrays.sort(Ar2,Collections.reverseOrder());
System.out.println(Ar2[Ar2.length-1]);
return (ar1[0]+Ar2[Ar2.length-1]);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] ar1 = new int[n];
for(int ar1_i = 0; ar1_i < n; ar1_i++){
ar1[ar1_i] = in.nextInt();
}
int[] ar2 = new int[n];
for(int ar2_i = 0; ar2_i < n; ar2_i++){
ar2[ar2_i] = in.nextInt();
}
int result = twinArrays(ar1, ar2);
System.out.println(result);
}
}
This one will probably pass all of your test cases.
Runtime Complexity ~ o(n)
static int twinArrays(int[] ar1, int[] ar2){
int ar1Minimum = Integer.MAX_VALUE , ar1MinIndex = -1;
int ar1SecondMin = Integer.MAX_VALUE ;
//Get the element with minimum value and index
for(int i = 0 ; i < ar1.length ; ++i){
if(ar1Minimum > ar1[i]){
ar1Minimum = ar1[i]; ar1MinIndex = i;
}
}
//Get the second minimum
for(int i = 0 ; i < ar1.length ; ++i){
if(ar1SecondMin > ar1[i] && i!=ar1MinIndex){ // i != ar1MinIndex = Important to avoid duplicate minimum values [1,1,2,4] min = 1 , secondMin = 2 and not 1
ar1SecondMin = ar1[i];
}
}
int ar2Minimum = Integer.MAX_VALUE , ar2MinIndex = -1;
int ar2SecondMin = Integer.MAX_VALUE ;
for(int i = 0 ; i < ar2.length ; ++i){
if(ar2Minimum > ar2[i]){
ar2Minimum = ar2[i]; ar2MinIndex = i;
}
}
for(int i = 0 ; i < ar2.length ; ++i){
if(ar2SecondMin > ar2[i] && i != ar2MinIndex){
ar2SecondMin = ar2[i];
}
}
if(ar1MinIndex != ar2MinIndex){
return ar1Minimum + ar2Minimum;
}
return Math.max((ar1Minimum + ar2SecondMin), (ar1SecondMin + ar2Minimum));
}

Sorting an array from least to greatest

I'm trying to sort an array from greatest to least using compareTo(), and the results aren't quite right.
public void insert(C data){
arr[currentSize++] = data; // <-- currentSize is initialized to 0 before this
C temp;
for(int i = 0; i < currentSize-1; i++) {
if(arr[i].compareTo(arr[i+1]) < 0) {
temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
}
An example is an input of:
[0.32371583205781573, 0.8906211866288234, 0.5288455608245172, 0.9170569745456318, 0.5103102992069214]
With an output of:
[0.9170569745456318, 0.5288455608245172, 0.5103102992069214, 0.32371583205781573, 0.8906211866288234]
I tried running through it backwards below, and the result was still incorrect:
public void insert(C data){
arr[currentSize++] = data;
C temp;
for(int i = currentSize-1; i > 0; i--) {
if(arr[i].compareTo(arr[i-1]) < 0) {
temp = arr[i];
arr[i] = arr[i-1];
arr[i-1] = temp;
}
}
}
The code is tested with:
int n = 5;
PQ<Double> pq1 = new PQasSortedArray<Double>(n);
double[] arr1 = new double[n];
for(int i = 0; i < n; i++){
Random num = new Random();
arr1[i] = num.nextDouble();
}
for (int i=0; i < arr1.length; i++){
pq1.insert(arr1[i]);
}
The way you've written this is to always "insert" the new item at the end of the array, then attempt to bubble-sort it into place, however, your bubble sort only makes one pass, and at best the new item, which starts at the last position, will move into the second-to-last position.
If you really want to add the new element into the last position, then you should change the for loop so it starts at the last element and works backward to the first. That will cause it to keep bubbling the new element up until it reaches the correct position.
However, a far more efficient way to do this is to find the right position for the new element first, then insert it there.
Easy way: just loop through the existing elements starting at index 0 until you find one that's less than the new value (remember we're sorting greatest to least), then insert the new element before that one.
Faster way: implement a binary search by starting in the middle. If the element in the middle is greater than the new element, then you know the new element should be in the second half of the array, otherwise it's in the first half. Then go to the middle of the selected half and go through the same process again, until you have found the right place.
Java has a function for performing a binary search: Arrays.binarySearch. However, you'll have to give it a custom comparator that reverses the normal comparison logic for C.
Update: This implementation of the less-efficient first strategy works. I implemented in terms of Comparable since we don't really care what C is, only that it's Comparable.
public void insert(Comparable data){
arr[currentSize] = data; // <-- currentSize is initialized to 0 before this
for(int i = currentSize; i > 0; i--) {
if(arr[i].compareTo(arr[i-1]) > 0) {
Comparable temp = arr[i];
arr[i] = arr[i-1];
arr[i-1] = temp;
}
}
currentSize++;
}
Much faster implementation:
public void insert(Comparable data) {
int position = Arrays.binarySearch(arr, 0, currentSize, data, (left, right) -> right.compareTo(left));
if (position < 0)
position = -position - 1;
System.arraycopy(arr, position, arr, position + 1, currentSize - position);
arr[position] = data;
currentSize++;
}

Finding how many different values there are in an array

Let's say I have an array in the length of n, and the only values that can appear in it are 0-9. I want to create a recursive function that returns the number of different values in the array.
For example, for the following array: int[] arr = {0,1,1,2,1,0,1} --> the function will return 3 because the only values appearing in this array are 0, 1 and 2.
The function receives an int array and returns int
something like this:
int numOfValues(int[] arr)
If you are using Java 8, you can do this with a simple one-liner:
private static int numOfValues(int[] arr) {
return (int) Arrays.stream(arr).distinct().count();
}
Arrays.stream(array) returns an IntStream consisting of the elements of the array. Then, distinct() returns an IntStream containing only the distinct elements of this stream. Finally, count() returns the number of elements in this stream.
Note that count() returns a long so we need to cast it to an int in your case.
If you really want a recursive solution, you may consider the following algorithm:
If the input array is of length 1 then the element is distinct so the answer is 1.
Otherwise, let's drop the first element and calculate the number of distinct elements on this new array (by a recursive call). Then, if the first element is contained in this new array, we do not count it again, otherwise we do and we add 1.
This should give you enough insight to implement this in code.
Try like this:
public int myFunc(int[] array) {
Set<Integer> set = new HashSet<Integer>(array.length);
for (int i : array) {
set.add(i);
}
return set.size();
}
i.e, add the elements of array inside Set and then you can return the size of Set.
public int f(int[] array) {
int[] counts = new int[10];
int distinct = 0;
for(int i = 0; i< array.length; i++) counts[array[i]]++;
for(int i = 0; i< counts.length; i++) if(counts[array[i]]!=0) distinct++;
return distinct;
}
You can even change the code to get the occurrences of each value.
You can try following code snippet,
Integer[] arr = {0,1,1,2,1,0,1};
Set<Integer> s = new HashSet<Integer>(Arrays.asList(arr));
Output: [0, 1, 2]
As you asked for a recursive implementation, this is one bad way to do that. I say bad because recursion is not the best way to solve this problem. There are other easier way. You usually use recursion when you want to evaluate the next item based on the previously generated items from that function. Like Fibonacci series.
Ofcourse you will have to clone the array before you use this function otherwise your original array would be changed (call it using countDistinct(arr.clone(), 0);)
public static int countDistinct(int[] arr, final int index) {
boolean contains = false;
if (arr == null || index == arr.length) {
return 0;
} else if (arr.length == 1) {
return 1;
} else if (arr[index] != -1) {
contains = true;
for (int i = index + 1; i < arr.length; i++) {
if (arr[index] == arr[i]) {
arr[i] = -1;
}
}
}
return countDistinct(arr, index + 1) + (contains ? 1 : 0);
}
int numOfValues(int[] arr) {
boolean[] c = new boolean[10];
int count = 0;
for(int i =0; i < arr.length; i++) {
if(!c[arr[i]]) {
c[arr[i]] = true;
count++;
}
}
return count;
}

How can you form unique sets of integers by combining integer arrays with variable sizes in Java?

I have any amount of arrays of integers like:
[1,9]
[5]
[7]
And I want to combine them in such a way that I get sets of numbers like:
[1,5,7]
[9,5,7]
Another Example INPUT:
[1,9]
[3,5]
[7]
[10]
OUTPUT:
[1,3,7,10]
[9,3,7,10]
[1,5,7,10]
[9,5,7,10]
I have tried nesting "for" loops but I always seem to get lost and can't get the right iterators I need to pull the right numbers when building the final array. There can be any number of integers in each array, and any number of arrays.
I have tried something like this, but it seems like a deadend:
int[][] allIndexes = {{1, 9},{5},{7}};
List<Integer> dataset1 = new ArrayList<Integer>();
//int[] dataset2 = {};
int i = 0;
for (int[] indexSet : allIndexes){
if(indexSet.length > i){
dataset1.add(indexSet[i]);
}else{
dataset1.add(indexSet[0]);
}
i++;
}
System.out.println(dataset1.toString());
//System.out.println(dataset2);
Any help would be greatly appreciated. I tried searching for others, but I really am not sure if I am defining this correctly.
You need a variable number of nested loops to enumerate all cases. Thus, recursion is your friend here. The code below will do what you're asking.
public static void main(String[] args)
{
int[][] allIndexes = {{1, 9},{3,5},{7},{10}};
List<Integer> dataset1;
if( allIndexes.length > 0)
{
int[] firstIndexes = allIndexes[0];
for( int i = 0; i < firstIndexes.length; i++)
{
dataset1 = new ArrayList<Integer>();
dataset1.add( firstIndexes[i]);
foo( dataset1, allIndexes, 1);
}
}
}
public static void foo( List<Integer> dataset1, int[][] allIndexes, int index)
{
if( index < allIndexes.length)
{
int[] indexes = allIndexes[index];
for( int i = 0; i < indexes.length; i++)
{
List<Integer> dataset = new ArrayList<Integer>();
for( Integer integer : dataset1)
dataset.add( integer);
dataset.add( indexes[i]);
foo( dataset, allIndexes, index+1);
}
}
else
{
StringBuilder sb = new StringBuilder();
sb.append( "[");
for( int i = 0; i < dataset1.size() - 1; i++)
sb.append( dataset1.get( i) + ",");
sb.append( dataset1.get( dataset1.size()-1));
sb.append( "]");
System.out.println( sb.toString());
}
}
Edit seems uoyilmaz was faster with his answer
Edit2 fixed a typo
I think a recursive approach might be worth a try.
You have your first input array and all following input arrays.
You want to get all combinations of your following input arrays and combine each of them with every element from your first input array
[1,9] // first input array
[5] // following input arrays
[7] // following input arrays
.
void GetCombinations(int[][] arrays, int startIndex, LinkedList<LinkedList<Integer>> outLists) {
// startIndex to high
if (startIndex >= arrays.length) return;
int[] firstArray = arrays[startIndex]
LinkedList<LinkedList<Integer>> subLists = new LinkedList<LinkedList<Integer>>();
// get sub-results
GetCombinations(arrays, startIndex + 1, subLists);
// combine with firstArray
if (subLists.size() == 0) {
subLists.add(new LinkedList<Integer>());
}
for (int i = 0; i < subLists.size(); ++i) {
for (int j = 0; j < firstArray.length; ++j) {
LinkedList<Integer> temp = new LinkedList<Integer>(subLists.get(i));
temp.addFirst(firstArray[j]);
outLists.add(temp);
}
}
}
You then call the function with
int[][] yourInputArrays = { ... };
LinkedList<LinkedList<Integer>> outputLists = new LinkedList<LinkedList<Integer>>();
GetCombinations(yourInputArrays, 0, outputLists);
If you are new to programming, the recursive approach might not be intuitive at first, but it is definitely worth looking into it.

For N equally sized arrays with integers in ascending order, how can I select the numbers common to arrays?

I was asked an algorithmic question today in an interview and i would love to get SO members' input on the same. The question was as follows;
Given equally sized N arrays with integers in ascending order, how would you select the numbers common to all N arrays.
At first my thought was to iterate over elements starting from the first array trickling down to the rest of the arrays. But then that would result in N power N iterations if i am right. So then i came up with a solution to add the count to a map by keeping the element as the key and the value as the counter. This way i believe the time complexity is just N. Following is the implementation in Java of my approach
public static void main(String[] args) {
int[] arr1 = { 1, 4, 6, 8,11,15 };
int[] arr2 = { 3, 4, 6, 9, 10,16 };
int[] arr3 = { 1, 4, 6, 13,15,16 };
System.out.println(commonNumbers(arr1, arr2, arr3));
}
public static List<Integer> commonNumbers(int[] arr1, int[] arr2, int[] arr3) {
Map<Integer, Integer>countMap = new HashMap<Integer, Integer>();
for(int element:arr1)
{
countMap.put(element, 1);
}
for(int element:arr2)
{
if(countMap.containsKey(element))
{
countMap.put(element,countMap.get(element)+1);
}
}
for(int element:arr3)
{
if(countMap.containsKey(element))
{
countMap.put(element,countMap.get(element)+1);
}
}
List<Integer>toReturn = new LinkedList<Integer>();
for(int key:countMap.keySet())
{
int count = countMap.get(key);
if(count==3)toReturn.add(key);
}
return toReturn;
}
I just did this for three arrays to see how it will work. Question talks about N Arrays though i think this would still hold.
My question is, is there a better approach to solve this problem with time complexity in mind?
Treat as 3 queues. While values are different, "remove" (by incrementing the array index) the smallest. When they match, "remove" (and record) the matches.
int i1 = 0;
int i2 = 0;
int i3 = 0;
while (i1 < array1.size && i2 < array2.size && i3 < array3.size) {
int next1 = array1[i1];
int next2 = array2[i2];
int next3 = array3[i3];
if (next1 == next2 && next1 == next3) {
recordMatch(next1);
i1++;
i2++;
i3++;
}
else if (next1 < next2 && next1 < next3) {
i1++;
}
else if (next2 < next1 && next2 < next3) {
i2++;
}
else {
i3++;
}
}
Easily generalized to N arrays, though with N large you'd want to optimize the compares somehow (NPE's "heap").
I think this can be solved with a single parallel iteration over the N arrays, and an N-element min-heap. In the heap you would keep the current element from each of the N input arrays.
The idea is that at each step you'd advance along the array whose element is at the top of the heap (i.e. is the smallest).
You'll need to be able to detect when the heap consists entirely of identical values. This can be done in constant time as long as you keep track of the largest element you've added to the heap.
If each array contains M elements, the worst-case time complexity of the would be O(M*N*log(N)) and it would require O(N) memory.
try
public static Set<Integer> commonNumbers(int[] arr1, int[] arr2, int[] arr3) {
Set<Integer> s1 = createSet(arr1);
Set<Integer> s2 = createSet(arr2);
Set<Integer> s3 = createSet(arr3);
s1.retainAll(s2);
s1.retainAll(s3);
return s1;
}
private static Set<Integer> createSet(int[] arr) {
Set<Integer> s = new HashSet<Integer>();
for (int e : arr) {
s.add(e);
}
return s;
}
This is how I learned to do it in an algorithms class. Not sure if it's "better", but it uses less memory and less overhead because it iterates straight through the arrays instead of building a map first.
public static List<Integer> commonNumbers(int[] arr1, int[] arr2, int[] arr3, ... , int[] arrN) {
List<Integer>toReturn = new LinkedList<Integer>();
int len = arr1.length;
int j = 0, k = 0, ... , counterN = 0;
for (int i = 0; i < len; i++) {
while (arr2[j] < arr1[i] && j < len) j++;
while (arr3[k] < arr1[i] && k < len) k++;
...
while (arrN[counterN] < arr1[i] && counterN < len) counterN++;
if (arr1[i] == arr2[j] && arr2[j] == arr3[k] && ... && arr1[i] == arrN[counterN]) {
toReturn.add(arr1[i]);
}
}
return toReturn;
}
This may be solved in O(M * N) with M being the length of arrays.
Let's see what happens for N = 2, this would be a sorted-list intersection problem, which has a classic merge-like solution running in O(l1 + l2) time. (l1 = length of first array, l2 = length of second array). (Find out more about Merge Algorithms.)
Now, let's re-iterate the algorithm N times in an inductive matter. (e.g. i-th time we will have the i-th array, and the intersection result of previous step). This would result in an overall O(M * N) algorithm.
You may also observe that this worst case upper-bound is the best achievable, since all the numbers must be taken into account for any valid algorithm. So, no deterministic algorithm with a tighter upper-bound may be founded.
Okay - maybe a bit naive here, but I think the clue is that the arrays are in ascending order. My java is rusty, but here is some pseduocode. I haven't tested it, so it's probably not perfect, but it should be a fast way to do this:
I = 1
J = 1
K = 1
While I <= Array1Count and J <= Array2Count and K <= Array3Count
If Array1(I) = Array2(J)
If Array1(I) = Array3(K)
=== Found Match
I++
J++
K++
else
if Array1(I) < Array3(K)
I++
end if
end if
else
If Array1(I) < Array2(J)
I++
else
if Array2(J) < Array3(K)
J++
else
K++
end if
end if
end if
Wend
This is Option Base 1 - you'd have to recode to do option base 0 (like java and other languages have)
I think another approach is to do similar thing to what we do in Mergesort: walk through all the arrays at the same time, getting identical numbers. This would take advantage of the fact that the arrays are in sorted order, and would use no additional space other than the output array. If you just need to print the common numbers, no extra space is used.
public static List<Integer> commonNumbers(int[] arrA, int[] arrB, int[] arrC) {
int[] idx = {0, 0, 0};
while (idxA<arrA.length && idxB<arrB.length && idxC<arrC.length) {
if ( arrA[idx[0]]==arrB[idx[1]] && arrB[idx[1]]==arrC[idx[2]] ) {
// Same number
System.out.print("Common number %d\n", arrA[idx[0]]);
for (int i=0;i<3;i++)
idx[i]++;
} else {
// Increase the index of the lowest number
int idxLowest = 0; int nLowest = arrA[idx[0]];
if (arrB[idx[1]] < nLowest) {
idxLowest = 1;
nLowest = arrB[idx[1]];
}
if (arrC[idx[2]] < nLowest) {
idxLowest = 2;
}
idx[idxLowest]++;
}
}
}
To make this more general you may want to take an arrays of arrays of ints, this will let you make the code more pretty. The array indeces must be stored in an array, otherwise it is hard to code the "increment the index that points to the lowest number" code.
public static List<Integer> getCommon(List<List<Integer>> list){
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int c=0;
for (List<Integer> is : list) {
c++;
for (int i : is) {
if(map.containsKey(i)){
map.put(i, map.get(i)+1);
}else{
map.put(i, 1);
}
}
}
List<Integer>toReturn = new LinkedList<Integer>();
for(int key:map.keySet())
{
int count = map.get(key);
if(count==c)toReturn.add(key);
}
return toReturn;
}
Your solution is acceptable, but it uses NxM space. You can do it with O(N) space (where N is the number of arrays), or in O(1) space.
Solution #1 (By Luigi Mendoza)
Assuming there are many small arrays (M << N), this can be useful, resulting in O(M*N*Log M) time, and constant space (excluding the output list).
Solution #2
Scan the arrays in ascending order, maintaining a min-heap of size N, containing the latest visited values (and indices) of the arrays. Whenever the heap contains N copies of the same value, add the value to the output collection. Otherwise, remove the min value and advance with the corresponding list.
The time complexity of this solution is O(M*N*Log N)

Categories

Resources