Related
I have an exercise to sort an array (type int) so I can get the largest number possible.
An example:
1,3,9 ==> 931
1,3,9,60 ==> 96031
So here is my idea: It is impossible to just sort the array to form the number that I wanted. So I can check the first number of each element in array, using the very same idea as bubble sort, just one small difference is that i use the first element to check instead of just arr[i]. But I still want to know beside using my idea, are there any other way (more efficiency). Even if your idea are the very same with my idea but you have something upgrade.
Thank you very much
It is impossible to just sort the array to form the number that I wanted.
Actually, it isn't impossible.
What you need is to design and implement an ordering that will result in the (decimal) numbers that will make the final number to be largest to sort first; e.g. for the numbers in your question, the ordering is:
9 < 60 < 3 < 1
You just need to work out exactly what the required ordering is for all possible non-negative integers. Once you have worked it out, code a Comparator class that implements the ordering.
Hint: You would be able to specify the ordering using recursion ...
Solution
For the descending order we multiply here by -1 each value in the array then sort the array and then multiply back with -1.
Ultimately we build the result string with string concatenation and print it out
import java.util.Arrays;
public class MyClass {
public static void main(String args[]) {
int[] array = {3,1,9};
for (int l = 0; l < array.length; l++){
array[l] = array[l]*-1;
}
Arrays.sort(array);
for (int l = 0; l < array.length; l++){
array[l] = array[l]*-1;
}
String res = "";
for(int i = 0; i < array.length; i++){
res+=array[i];
}
System.out.println(res);
}
}
Output
931
Alternatively
Or as #Matt has mentioned in the comments you can basically concat the string in reverse order. Then there is no need anymore for the ascending to descending transformation with *-1
import java.util.Arrays;
public class MyClass {
public static void main(String args[]) {
int[] array = {
9,
1,
3
};
String res = "";
Arrays.sort(array);
for (int l = array.length - 1; l >= 0; l--) {
res += array[l];
}
System.out.println(res);
}
}
Hope it will work as per your requirement->
public static void main(String[] args) {
Integer[] arr = {1,3,3,9,60 };
List<Integer> flat = Arrays.stream(arr).sorted((a, b) -> findfirst(b) - findfirst(a)).collect(Collectors.toList());
System.out.println(flat);
}
private static int findfirst(Integer a) {
int val = a;
if(val>=10) {
while (val >= 10) {
val = val / 10;
}
}
return val;
}
A lot of problems become easier when using Java streams. In this case you could convert all numbers to String and then sort in an order which picks the higher String value of two pairings, then finally join each number to one long one.
This works for your test data, but does NOT work for other data-sets:
List<Integer> list1 = List.of(1,3,9,60);
Comparator<String> compare1 = Comparator.reverseOrder();
String num1 = list1.stream().map(String::valueOf).sorted(compare1).collect(Collectors.joining(""));
The comparator Comparator.reverseOrder() does not work for numbers of different length which start with same values, so a more complex compare function is needed which concatenates values to decide ordering, something like this which implies that "459" > "45" > "451"
List<Integer> list2 = List.of(1,45,451,449,450,9, 4, 459);
Comparator<String> compare = (a,b) -> (b+a).substring(0, Math.max(a.length(), b.length())).compareTo((a+b).substring(0, Math.max(a.length(), b.length())));
String num2 = list2.stream().map(String::valueOf).sorted(compare).collect(Collectors.joining(""));
... perhaps.
compare first number of each int then if it is the biggest put it at beginning then you continue, if first char is equal step into the second etc etc the bigest win, if it same at max-char-size. the first selected would be pushed then the second one immediatly after as you already know.
In that maneer 9 > 60 cuz 960>609 the first char will always be the biggest in that case when u concat.
it's > 9 < not > 09 <
I want to split a list into a given number n sublists in all possible ways in Java.
For example [1, 2, 3, 4] where n = 3 would include the following lists (but would not be a complete solution - complete would require much more space):
([], [], [1,2,3,4])
([],[1],[2,3,4])
([],[1,2],[3,4])
([],[1,2,3],[4])
([],[1,2,3,4],[])
([1],[2,3,4], [])
([1],[2,3],[4])
([2,3],[4],[1])
([4],[],[1,2,3])
...
etc
I adapted a solution from another similar question (Split a list into two sublists in all possible ways) however it only works for creating lists of 2 sublists and I am struggling to grasp how to implement it for a flexible rather than hardcoded number of sublists.
Here is my code:
public List<List<EGroup>> permutation(List<E> list) {
List<List<E>> sublists = new ArrayList<List<E>>();
for (int i = 0; i <= list.size(); i++) {
permutationSplit(list, sublists, i, new ArrayList<E>(), 0);
}
List<List<EGroup>> listOfEGroupPairs = new ArrayList<List<EGroup>>();
for (List<E> subList : sublists) {
List<E> listCopy = new ArrayList<E>(list);
listCopy.removeAll(subList);
EGroup e1 = new EGroup(subList);
EGroup e2 = new EGroup(listCopy);
List<EGroup> egr = new ArrayList<EGroup>();
egr.add(e1);
egr.add(e2);
listOfEGroupPairs.add(egr);
}
return listOfEGroupPairs;
}
public void permutationSplit(List<E> list, List<List<E>> subLists, int sublistSize, List<E> currentSubList,
int startIndex) {
if (sublistSize == 0) {
subLists.add(currentSubList);
} else {
sublistSize--;
for (int i = startIndex; i < list.size(); i++) {
List<E> newSubList = new ArrayList<E>(currentSubList);
newSubList.add(list.get(i));
permutationSplit(list, subLists, sublistSize, newSubList, i + 1);
}
}
}
I need to create n number of EGroup objects to add to listOfEGroupPairs rather than the hardcoded 2, but how to always get the right number (n) of sublists of varied size each loop?
You have K elements and each might fall into any of N lists.
So there are N^K variants and we can just map integer values from 0 to N^K-1 to distributions like N-ary numeral system.
Another approach - recursively insert every element into N lists.
I can demonstrate approaches with Python code recursive, N-ary and hope it might be translated to Java
def recdistr(K, N, level, ls):
if level == K:
print(ls)
else:
for i in range(N):
ls[i].append(level)
recdistr(K, N, level + 1, ls) #recursive call with changed list
ls[i].pop() #remove last added element to revert list to previous state
K = 4
N = 3
lst = [[] for _ in range(N)]
recdistr(K, N, 0, lst)
def mapdistr(K, N):
for x in range(N**K):
t = x
l = [[] for _ in range(N)]
for i in range(K):
id = t % N
t = t // N #integer division
l[id].append(i)
print(l)
mapdistr(K, N)
If I understand the question correctly, each element of the original list can end up in any of the n sublists. That means, there are n^s possible sublists (s being the number of elements in the original list), which can be enumerated in a simple loop. With a bit of modulo and integer division you can then get the proper "bucket" for each element and prepare the results accordingly.
public <T> List<List<List<T>>> partition(List<T> lst, int n) {
var result = new ArrayList<List<List<T>>>();
// k = SUM ( pos of lst[i] * n^i )
for (int k = 0; k < Math.pow(n, lst.size()); k++) {
// initialize result
List<List<T>> res = IntStream.range(0, n)
.mapToObj(i -> new ArrayList<T>())
.collect(Collectors.toList());
// distribute elements to sub-lists
int k2 = k;
for (int i = 0; i < lst.size(); i++) {
res.get(k2 % n).add(lst.get(i));
k2 /= n;
}
result.add(res);
}
return result;
}
Use recursion.
For n equal to 0 or negative the task is impossible. Either throw an exception or return an empty lists of lists of sublists. Corner case: if n is 0 and the list is empty, you may argue that the empty list of sublists is a valid response.
If n is 1, the only solution is trivially the entire list.
For n > 1:
If the length of the list is 4 (for example [1, 2, 3, 4]), there are 5 possible first lists. In general there are list.length + 1 possible first sublists. Find them. For each such sublist make a recursive call passing the remainder of the list and n - 1 as arguments to find all possible combinations of sublists made from the remainder of the list. Combine each first sublist with each combination of remaining sublists to form a full solution.
PS The solution as sketched will only produce sublists in the order they come in the original list. So the solution will include ([],[1],[2,3,4]) and ([1],[2,3,4], []), but not ([4],[],[1,2,3]). To regard the last one as a separate solution, you will additionally need to find all permutations of each solution, in turn taking into account that some sublists may be equal and hence swapping them won’t make a distinct solution.
I would like to write a function insertAndSort() that will take as parameter an Integer "num" and a List of integers "list". Let's assume the List lust is already sorted.
The algorithm is supposed to work this way :
Add "num" to L and keep the List sorted after "num" has been added.
Return the sorted List.
WARNING : We do not know whether the list is sorted ASC or DESC. And that is precisely my problem !
Example :
if "num" = 4 and L = {1, 3, 5, 7}, then the final sorted List is {1, 3, 4, 5, 7}
if "num" = 4 and L = {7, 5, 3, 1}, then the final sorted List is {7, 5, 4, 3, 1}
I can not use sorting API such as Collections.sort or Collections.reverseOrder etc...
So far, I've produced this code :
public static void main(String[] args) {
int num = 4;
List<Integer> myList = Arrays.asList(1, 3, 5, 7);
List<Integer> newList = new ArrayList<Integer>();
newList = insertAndSort(myList, num);
System.out.println(newList);
}
public static List<Integer> insertAndSort(List<Integer> list, int num) {
List<Integer> listSecond = new ArrayList<Integer>(list.size()+1);
for(int i = 0; i <= list.size() ; i++) {
if(num < list.get(i)) {
if(!listSecond.contains(num)){
listSecond.add(num);
} else {
listSecond.add(list.get(i-1));
listSecond.add(list.get(i));
break;
}
} else {
listSecond.add(list.get(i));
}
}
return listSecond;
}
The problem is that this seems to be working with a single type of List: the ascending one.
When I take a sorted descending List, it does not work anymore.
Do you have any idea to make this work with both type of List ?
Thanks and Regards.
First, you need to detect the sort order in the existing list.
If the list is empty, you don’t need to care, just add your element, you cannot break any existing sort order.
If the list contains one element, I cannot tell you what to do. Options include tossing a coin and throwing an exception, but there are more.
If the list contains two or more elements, iterate through them except for the last element, each time comparing the current element with the element in the next higher index. As soon as you encounter a pair of elements that are not equal, you know the sort order. If you only encounter equal elements, all the elements in the list are equal, and again I cannot tell you what to do.
Once you’ve detected a sort order, I suggest a big if-else statement to handle the two cases separately.
You already have the code for the ascending case. Except it doesn’t always work. If I try to insert 4 into { 1, 3, 5 }, I get an ArrayIndexOutOfBoundsException. If I try with { 1, 3, 5, 7, 9 }, the 9 is lost. You should probably find a fix for that.
You should be able to handle the descending case similarly to the ascending case. Only use num > list.get(i) instead of num < list.get(i).
First, you need to check whether the list is sorted in ASC or DESC. That's easy: compare the 1st two elements look for two consecutive non-identical elements and see whether the first one is greater or the second one (thanks tom for correcting the mistake).
Now, for a list sorted in ascending order, convert list to an ArrayList. Then, add num to the ArrayList and convert it to a TreeSet, since TreeSets sort their elements in ascending order. Finally, reassign the ArrayList to hold the contents of the TreeSet and return it.
For a list sorted in descending order, first, convert the TreeSet to an ArrayList, then iterate over this ArrayList in reverse order and add the elements to a new ArrayList. Return the second ArrayList.
public List<Integer> insertAndSort(List<Integer> list, int num){
ArrayList<Integer> a = new ArrayList<Integer>(list);
a.add(new Integer(num));
TreeSet t = new TreeSet(a);
a = new ArrayList<Integer>(t);
int l = list.size();
for(int i=1; i<l; i++){
if(list.get(i) != list.get(i-1))
break;
}
if(list.get(i) > list.get(i-1)) //ASC
return a;
else{ //DESC
ArrayList<Integer> a2 = new ArrayList<Integer>();
for(int i = a.size() - 1; i >= 0; i--)
a2.add(a.get(i));
return a2;
}
}
I would proceed with something like the code below. A few remarks :
it is possible to decide sort order if and only if there is at least 1 element AND first and last elements have different values.
the line int oo = list.get(p2) - list.get(p1); compute the
difference between the last and first element (negative = DESC,
positive = ASC)
the variable ox is positive if and only if the
added element is after the element pointed by the variable p3
whatever the order.
the position is found by a binary search algorithm by choosing p3 between p1 and p2 and deciding if the element is before or after p3.
This is not fully tested bu works with the examples you gave :
// static List<Integer> list = new ArrayList<>(Arrays.asList(7, 5, 3, 1));
static List<Integer> list = new ArrayList<>(Arrays.asList(1, 3, 5, 7));
static void add(int x)
{
// fixed code below (see Ole V.V. comment)
}
static public void main(String [ ] args)
{
add(4);
for(int x: list)
System.out.println(x);
}
EDIT: to be complete (see Ole V.V. comments)
When the new element is to be inserted before the first or after the last, two simple O(1) tests may be performed ; the general case has O(log N) complexity, this is more efficient than traversing the entire list which is O(N).
Be carefull too when comparing elements to deduce sort order, there may be several equal values ; the best is to compare the first and the last elements, this is O(1) - again better than O(N).
Algorithmic complexity is an important matter (to me) and was the primary aim of my post. The code of the add function becomes :
static void add(int x)
{
int p1 = 0;
int p2 = list.size() - 1;
if(list.size() == 0 || list.get(p1) == list.get(p2))
throw new IllegalStateException("Unable to decide sort order");
int oo = list.get(p2) - list.get(p1);
if(oo * (x - list.get(p1)) <= 0) // new element is before first
list.add(0, x);
else if(oo * (x - list.get(p2)) >= 0) // new element is after last
list.add(x);
else
{
while (p1 + 1 < p2)
{
int p3 = (p1 + p2) / 2;
int ox = (x - list.get(p3)) * oo;
if(ox >= 0) // new element is after p3
p1 = p3;
if(ox <= 0) // new element is before p3
p2 = p3;
}
list.add(p2, x);
}
}
Note : there may be still some undealed side effects. If the asker is interested, I am ready to give further help - just ask.
Your code and what you say is two different things.
Based on code you made, I see that List can't contain more than one instance of same value.
If thats the case, main method should look like this:
public static void main(String[] args){
int num = 4;
List<Integer> myList = Arrays.asList(1, 3, 4, 5, 7);
List<Integer> newList;
if (!myList.contains(num)) {
newList = insertAndSort(myList, num);
} else {
newList = copyList(myList);
}
System.out.println(newList);
}
if thats not the case:
public static void main(String[] args){
int num = 4;
List<Integer> myList = Arrays.asList(1, 3, 4, 5, 7);
List<Integer> newList;
newList = insertAndSort(myList, num);
System.out.println(newList);
}
Methods that main method use:
Assuming we can only know how list is sorted by values in list, we have to decide default sort method when there is only 1 value or all values are same. I picked ASC as default. If elements can be of same value and we are certain list is sorted its best to compare lowest value in list with the highest one. With this approach we have to compare 2 elements only once.
So method to see if list is sorted DESC would look like this:
private static boolean isDesc(List<Integer> list) {
return list.get(0) > list.get(list.size() - 1);
}
If method returns true its sorted DESC. With returning false its ASC. If we want to change default value, when values don't tell us how its sorted and we want it to be DESC we would replace '>' sign with '>='
private static boolean isDesc(List<Integer> list) {
return list.get(0) >= list.get(list.size() - 1);
}
Code for as you called it insertAndSort:
public static List<Integer> insertAndSort(List<Integer> list, int num) {
List<Integer> listSecond = new ArrayList<Integer>(list.size() + 1);
boolean isDescSortOrder = isDesc(list);
if (isDescSortOrder) {
int i = 0;
while ((i < list.size()) && (list.get(i) > num)) {
listSecond.add(list.get(i));
i++;
}
listSecond.add(num);
while (i < list.size()) {
listSecond.add(list.get(i));
i++;
}
} else { // is asc sort order
int i = 0;
while ((i < list.size()) && (list.get(i) < num)) {
listSecond.add(list.get(i));
i++;
}
listSecond.add(num);
while (i < list.size()) {
listSecond.add(list.get(i));
i++;
}
}
return listSecond;
}
Depending on how its sorted code got devided into 2 blocks. As we don't know on each iteration will be right place to insert our num value its better to use while loop. Using for loop and checking everytime if num value already exists in list is counterproductive. On top of that I dunno if your application should allow same values in the list. If I assume it should, you can't add num value if it already existed in the list with for loop and checking with contains every iteration.
And just to copy the table when list already has the element and we don't want to add elements that are already incuded:
public static List<Integer> copyList(List<Integer> list) {
List<Integer> listSecond = new ArrayList<Integer>(list.size());
for (int i = 0; i < list.size(); i++) {
listSecond.add(list.get(i));
}
return listSecond;
}
Also based on good programming practices I'd recommend to name methods based on what they do.
Calling method insertAndSort is asking for trouble. If I seen it, I'd say it alters list we are giving in parameter. Then it sorts it.
So putting unsorted list in it would give us unwanted outcome.
And I'd still ask myself why does it return a List when we are inserting an item to already existing list? I'd rather name it:
public static List<Integer> copySortedWithNewValue(List<Integer> sortedList, int newValue)
Try to use PriorityQueue. This collection contains a sorted sequence of elements which could be duplicated.
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)
I faced this problem many times during various situations. It is generic to all programming languages although I am comfortable with C or Java.
Let us consider two arrays (or collections):
char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};
How do I get the common elements between the two arrays as a new array?
In this case, the intersection of array A and B is char[] c = {'c', 'd'}.
I want to avoid the repeated iteration of one array inside the other array which will
increase the execution time by (length of A times length of B) which is too much in the case of huge arrays.
Is there any way we could do a single pass in each array to get the common elements?
foreach element e in array A
insert e into hash table H
foreach element e in array B
if H contains e
print e
This algorithm is O(N) in time and O(N) in space.
To avoid the extra space, you can use the sorting based approach.
The lower bound on efficiency is O(n) - you need to at least read all the elements.
Then there are several apporaches:
Dumb simplest approach
Search for every element from array one in array two. Time complexity O(n^2).
Sorting approach
You need to sort only array one, then search for elements from array two using binary search. Time complexity: sorting O(nlogn), searching O(n * logn) = O(nlogn), total O(nlogn).
Hash approach
Create a hash table from array one elements. Search for elements form second table in the hash table. The time complexity depends on the hash function. You can achieve O(1) for searches in the optimal case (all elements will have different hash value), but O(n) in the worst case (all elements will have the same hash value). Total time complexity: O(n^x), where x is a factor of hash function efficiency (between 1 and 2).
Some hash functions are guaranteed to build a table with no collisions. But the building no longer takes strictly O(1) time for every element. It will be O(1) in most cases, but if the table is full or a collision is encountered, then the table needs to be rehashed - taking O(n) time. This happens not so often, much less frequently than clean adds. So the AMORTISED time complexity is O(1). We don't care about some of the adds taking O(n) time, as long as the majority of adds takes O(1) time.
But even so, in an extreme case, the table must be rehashed every single insertion, so the strict time complexity would be O(n^2)
There are a few methods in some languages that I'm aware of that do exactly what you want, have you considered looking at some of these implementations?
PHP - array_intersect()
$array1 = array("a" => "green", "red", "blue");
$array2 = array("b" => "green", "yellow", "red");
$result = array_intersect($array1, $array2);
print_r($result);
>> green
red
Java - List.retainAll
Collection listOne = new ArrayList(Arrays.asList("milan","dingo", "elpha", "hafil", "meat", "iga", "neeta.peeta"));
Collection listTwo = new ArrayList(Arrays.asList("hafil", "iga", "binga", "mike", "dingo"));
listOne.retainAll( listTwo );
System.out.println( listOne );
>> dingo, hafil, iga
Since this looks to me like a string algorithm, I'll assume for a moment that its not possible to sort this sequence (hence string) then you can use Longest Common Sequence algorithm (LCS)
Assuming the input size is constant, then the problem has a complexity of O(nxm), (length of the two inputs)
public static void main(String[] args) {
char[] a = {'a', 'b', 'c', 'd'};
char[] b = {'c', 'd', 'e', 'f'};
System.out.println(intersect(a, b));
}
private static Set<Character> intersect(char[] a, char[] b) {
Set<Character> aSet = new HashSet<Character>();
Set<Character> intersection = new HashSet<Character>();
for (char c : a) {
aSet.add(c);
}
for (char c : b) {
if (aSet.contains(c)) {
intersection.add(c);
}
}
return intersection;
}
int s[256] // for considering all ascii values, serves as a hash function
for(int i=0;i<256;i++)
s[i]=0;
char a[]={'a','b','c','d'};
char b[]={'c','d','e','f'};
for(int i=0;i<sizeof(a);i++)
{
s[a[i]]++;
}
for(int i=0;i<sizeof(b);i++)//checker function
{
if(s[b[i]]>0)
cout<<b[i];
}
complexity O(m+n);
m- length of array a
n- length of array b
Google Guava
There are already many good answers to this, but if you want the one-liner approach using a library for lazy-coding, I'd go with Google Guava (for Java) and its Sets.intersection method.
(no compiler at hand, bear with me)
char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};
Set<Character> intersection = Sets.intersection(
Sets.newHashSet<Character>(Chars.asList(a)),
Sets.newHashSet<Character>(Chars.asList(b))
);
Obviously, this is assuming both arrays wouldn't have duplicates, in which case using a set data structure would make more sense and allow for this sort of operation more efficiently, especially if you don't start from an array of primitives from the start.
May or may not fit your use case, but sort of the no-brainer approach for the general case.
Sort both the arrays.
Then do loop until they have have elements common Or one of the arrays reaches its end.
Asymptotically, this takes the complexity of sorting. i.e. O(NlogN) where N is the length of longer input array.
If you care about duplicates, use a hash map to index list A, with the key being the element, and the value being a number of how many times that element has been seen.
You iterate through the first and for every element in A and if it does not exist in the map, put it in there with a value of 1, if it already exists in the map, add one to that value.
Next, iterate through B, and if the value exists, subtract 1. If not, put -1 in the value on the table for that element.
Finally, iterate through the map and for any element that has a value != 0, print out as a difference.
private static <T> List<T> intersectArrays(List<T> a, List<T> b) {
Map<T, Long> intersectionCountMap = new HashMap<T, Long>((((Math.max(a.size(), b.size()))*4)/3)+1);
List<T> returnList = new LinkedList<T>();
for(T element : a) {
Long count = intersectionCountMap.get(element);
if (count != null) {
intersectionCountMap.put(element, count+1);
} else {
intersectionCountMap.put(element, 1L);
}
}
for (T element : b) {
Long count = intersectionCountMap.get(element);
if (count != null) {
intersectionCountMap.put(element, count-1);
} else {
intersectionCountMap.put(element, -1L);
}
}
for(T key : intersectionCountMap.keySet()) {
Long count = intersectionCountMap.get(key);
if (count != null && count != 0) {
for(long i = 0; i < count; i++) {
returnList.add(key);
}
}
}
return returnList;
}
This should run in O(n), as we're only iterating the Lists each once, and the Map once. The Data structures here used in Java should be efficient, as the HashMap is constructed with a capacity that can handle the largest size of the lists.
I'm using a LinkedList for the return as it provides us a way of adding and iterating through a list for our unknown sized intersection.
The best way is not to start with arrays at all. Arrays are optimal for random access to elements, but not optimal for searching (which is what finding the intersection is all about). As you are talking about intersection, you must be regarding the arrays as sets. So use a more appropriate data structure (in Java, a Set). Then the task is much more efficient.
You can use tree, but time will be O(n(log n)) and elements must be comparable
First, sort the two arrays using best sorting algorithm.
Then, with linear search, you can get the common elements.
If an extra space is provided then we can use hash table to do that.
in ruby you can just say
a = ['a', 'b', 'c', 'd']
b = ['c', 'd', 'e', 'f']
c = a & b
c contains ['c','d']
Sort two arrays first, then iterate them, if they are the same element, add to to be returned array.
Code is here:
public static void printArr(int[] arr){
for (int a:arr){
System.out.print(a + ", ");
}
System.out.println();
}
public static int[] intersectionOf(int[] arr1, int[] arr2){
Arrays.sort(arr1);
Arrays.sort(arr2);
printArr(arr1);
printArr(arr2);
int i=0, j=0, k=0;
int[] arr = new int[Math.min(arr1.length, arr2.length)];
while( i < arr1.length && j < arr2.length){
if(arr1[i] < arr2[j]){
i++;
} else if(arr1[i] > arr2[j]){
j++;
} else {
arr[k++] = arr1[i++];
j++;
}
}
return Arrays.copyOf(arr, k);
}
public static void main(String[] args) {
int[] arr1 = {1, 2, 6};
int[] arr2 = {10, 2, 5, 1};
printArr(intersectionOf(arr1,arr2));
}
outputs:
arr1: 1, 2, 6,
arr2: 1, 2, 5, 10,
arr: 1, 2,
Assuming you are dealing with ANSI characters. The approach should be similar for Unicode, just change the range.
char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};
int[] charset = new int[256]
for(int i=0; i<A.length; i++) {
charset[A[i]]++;
}
Now iterate over the B and you can check if the corresponding charset value for the character being iterated is greater than 0. You can store them in a list or any other collection.
This approach takes O(n) time complexity and a constant space for your checks not taking into account your new array/list being used to hold the common elements.
This is better than the HashSet/Hashtable approach in terms of space complexity.
You could use HashSet in .NET 3.5 or later. Example c# code:
HashSet<int> set1 = new HashSet<int>(new int[]{8, 12, 13, 15});
HashSet<int> set2 = new HashSet<int>(new int[] { 15, 16, 7, 8, 9 });
set1.IntersectWith(set2);
foreach (int i in set1)
Console.Write(i+ " ");
//output: 8 15
Sort one of the arrays (m Log(m) )
now Pick each element from other array and
do a binary search in first array(the sorted one) ->n Log(m)
Total Time Complexity :- (n+m)Log(m).
I hope the following would be useful.
These are two different approached:
Simple Intersection where you compare all the elements from one array
to another array.
Sorting and searching based approach which sorts one array and search second array element in first array using binary
search.
//
public class IntersectionOfUnsortedArrays {
public static void main(String[] args) {
int[] arr1 = { 12, 4, 17 };
int[] arr2 = { 1, 12, 7, 17 };
System.out.println("Intersection Using Simple Comparision");
printArray(simpleIntersection(arr1, arr2));
System.out.println("Intersection Using Sort and Binary Search");
printArray(sortingBasedIntersection(arr1, arr2));
}
/*
* Simple intersection based on the comparison without any sorting.
* Complexity O(n^2)
*/
public static int[] simpleIntersection(int[] a, int[] b) {
int minlen = a.length > b.length ? b.length : a.length;
int c[] = new int[minlen];
int k=0;
for(int i=0;i<a.length;i++){
for(int j=0;j<b.length;j++){
if(a[i]==b[j]){
c[k++]=a[i];
}
}
}
int arr[] = new int[k];
// copy the final array to remove unwanted 0's from the array c
System.arraycopy(c, 0, arr, 0, k);
return arr;
}
/*
* Sorting and Searching based intersection.
* Complexity Sorting O(n^2) + Searching O(log n)
*/
public static int[] sortingBasedIntersection(int[] a, int[] b){
insertionSort(a);
int minlen = a.length > b.length ? b.length : a.length;
int c[] = new int[minlen];
int k=0;
for(int i=0;i<b.length;i++){
int result = binarySearch(a,0,a.length,b[i]);
if(result > -1){
c[k++] = a[result];
}
}
int arr[] = new int[k];
// copy the final array to remove unwanted 0's from the array c
System.arraycopy(c, 0, arr, 0, k);
return arr;
}
public static void insertionSort(int array[]) {
for (int i = 1; i < array.length; i++) {
int j = i;
int b = array[i];
while ((j > 0) && (array[j - 1] > b)) {
array[j] = array[j - 1];
j--;
}
array[j] = b;
}
}
static int binarySearch(int arr[], int low, int high, int num) {
if (high < low)
return -1;
int mid = (low + high) / 2;
if (num == arr[mid])
return mid;
if (num > arr[mid])
return binarySearch(arr, (mid + 1), high, num);
else
return binarySearch(arr, low, (mid - 1), num);
}
public static void printArray(int[] array) {
for (int value : array) {
System.out.print(" "+value);
}
System.out.println("\n");
}
}
If the collections are already sorted, as shown in the question, then the best solution (not mentioned yet) is a merge-sort-like algorithm that runs in O(n+m).
Compare the first elements of each collection. If they're the same, add the element to the intersection set and pop both elements from their collections. If the elements are different, pop the element that's greater, in comparison, to the other element. Repeat until one collection is empty.
Using Java 8 features, here is an algorithm which honours duplicates within a list instead of turning a list into a set. No sorting, so no n log n.
Convert one of the lists into a map, with value being number of occurrences (cost: O(n)).
For each item in the other list, if the item exists in the map, decrease occurrence by one (cost: O(n)).
Therefore, overall cost is O(n). Code:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Dup {
public static void main(String[] args) {
List<Integer> listA = Arrays.asList(3, 1, 4, 1, 9, 5, 9);
List<Integer> listB = Arrays.asList(2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3);
findCommons(listA, listB);
}
static void findCommons(List<Integer> listA, List<Integer> listB) {
Map<Integer, Long> mapA =
listA.stream().collect(
Collectors.groupingBy(Integer::intValue, Collectors.counting()));
List<Integer> commons = new ArrayList<>();
listB.stream()
.filter(e -> mapA.get(e) != null)
.filter(e -> mapA.get(e) > 0)
.forEach(e -> {
mapA.put(e, mapA.get(e) - 1);
commons.add(e);
});
System.out.println(commons);
}
}
Code above will give this output: [5, 3, 9, 9].
import java.util.Scanner;
public class arraycommon {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
// display common element in two diffrent array
int sizea,sizeb,i=0,j=0,k=0;
int count=0;
System.out.println("enter the size array A:"+'\n');
sizea=sc.nextInt();
System.out.println("enter the size array B"+'\n');
sizeb=sc.nextInt();
int a[]=new int[sizea];
int b[]=new int[sizeb];
int c[]=new int[sizea];
System.out.println("enter the element in array A:"+'\n');
for (i = 0; i < sizea; i++) {
a[i]=sc.nextInt();
}
System.out.println("enter the element in array B:"+'\n');
for (i = 0; i < sizeb; i++) {
b[i]=sc.nextInt();
}
System.out.println("the element in array A:"+'\n');
for (i = 0; i < sizea; i++) {
System.out.print(a[i]+" ");
}
System.out.println('\n');
System.out.println("the element in array B:"+'\n');
for (i = 0; i < sizeb; i++)
{
System.out.print(b[i]+" ");
}
for (i = 0; i <sizea; i++)
{
for (j = 0; j < sizeb; j++)
{
if(a[i]==b[j])
{
count++;
c[k]=a[i];
k=k+1;
}
}
}
System.out.println('\n');
System.out.println("element common in array is");
if(count==0)
{
System.out.println("sorry no common elements");
}
else
{
for (i = 0; i <count; i++)
{
System.out.print(c[i]+" ");
}
}
}
}
simply search each element of first array with each element of second array and stored matched result in third array
class Union
{
public static void main(String[] args) {
char a[] ={'f','g','d','v','a'};
char b[] ={'a','b','c','d','e'};
char temp[] = new char[5];
int p=0;
for(int i=0;i<a.length;i++)
{
for(int j=0;j<b.length;j++)
{
if(a[i]==b[j]) //searches if both array has common element
{
temp[p] = a[i]; //if match found store it in a new array
p++;
}
}
}
for(int k=0;k<temp.length;k++)
{
System.out.println(temp[k]);
}
}
}