I was asked the following question in an interview:
Combine two iterators over their sorted contents such that the resulting iterator should iterate over the combination of these 2 iterators in sorted order in O(1) time (these iterators iterate over a String).
I wrote the below code but I'm sure it doesn't perform in O(1) time. What advice do you have for matching the constraints set by the interview question?
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class iteratorCombine {
// assumption1: elements are hardcoded
// assumption2: both iterators have equal number of elements
public static void main(String[] args) {
iteratorCombine testObj = new iteratorCombine();
Set<String> firstSet = new TreeSet<String>();
Set<String> secondSet = new TreeSet<String>();
Set<String> combinedSet;
firstSet = testObj.storeElements1(firstSet);
secondSet = testObj.storeElements2(secondSet);
Iterator<String> it1 = firstSet.iterator();
Iterator<String> it2 = secondSet.iterator();
combinedSet = testObj.combine(it1, it2);
// output
Iterator<String> itComb = combinedSet.iterator();
while(itComb.hasNext()){
System.out.println(itComb.next());
}
}
public Set<String> storeElements1(Set<String> firstSet){
firstSet.add("first3");
firstSet.add("first1");
firstSet.add("first2");
return firstSet;
}
public Set<String> storeElements2(Set<String> secondSet){
secondSet.add("second3");
secondSet.add("second1");
secondSet.add("second2");
return secondSet;
}
public Set<String> combine(Iterator<String> it1, Iterator<String>it2){
String firstEle, secondEle;
Set<String> combinedSet = new TreeSet<String>();
while (it1.hasNext() && it2.hasNext()) {
firstEle = it1.next();
secondEle = it2.next();
combinedSet.add(firstEle+secondEle);
}
return combinedSet;
}
}
The complexity is O(m log n).
If you are given two Iterators in Java, how are you supposed to merge them into one list if both iterators are sorted? The Java's iterator has two important methods you can use: hasNext() and next(). The hasNext() returns a boolean telling if this iterator reaches the end.
merge the two arrays using MergeSort (this takes O(n) runtime)
Efficient Space Optimized Approach: Refer to Efficiently merging two sorted arrays with O (1) extra space to merge the two given array without using any extra memory. Partition – based Approach: The idea is to consider the (N + 1)th element of the final sorted array as a pivot element and perform the quick sort partition around the pivot element.
Given two sorted arrays, arr [], brr [] of size N, and M, the task is to merge the two given arrays such that they form a sorted sequence of integers combining elements of both the arrays. Explanation: The merged sorted array obtained by taking all the elements from the both the arrays is {2, 3, 10}. Therefore, the required output is 2 3 10.
Just merge the two arrays as we do in merge sort, while simultaneously using Euclidean Division Lemma, i.e. ( ( (Operation on array) % x) * x). And at least after merging divide both the arrays with x. Where x needs to be a number greater than all elements of array.
If you can modify the existing lists, then you can ask List1 for it's last element (which is O (1) is the list header has a pointer to the end of the list) and then it's simply a matter of List1->last->next = List2->head. Afterwards, iterating over List1 will iterate over all elements.
I believe that you can't do it if you don't extend iterator
and support a peek
function. Such an iterator is not that hard. Here is a way for doing it.
static class PeekingIterator<T> implements Iterator<T> {
private final Iterator<T> iterator;
private T temp;
public PeekingIterator(Iterator<T> iterator) {
this.iterator = iterator;
}
public T peek() {
//if there is no peek, advance the iterator and store its value, return the peek otherwise
if(temp==null){
temp = this.iterator.next();
}
return temp;
}
@Override
public T next() {
//if we already have a peek,return it and nullify it, otherwise do normal next()
if(temp!=null){
T t = temp;
temp = null;
return t;
}else{
return this.iterator.next();
}
}
@Override
public boolean hasNext() {
return this.iterator.hasNext() || temp!=null;
}
}
Once you can peek, the rest is easy, you can build SortedIterator
using two peeking iterators, peek both iterators and advance the iterator that has the smaller element.
static class SortedIterator<T extends Comparable<T>> implements Iterator<T>{
private final PeekingIterator<T> peekingIterator1;
private final PeekingIterator<T> peekingIterator2;
SortedIterator(Iterator<T> source1, Iterator<T> source2){
peekingIterator1 = new PeekingIterator<>(source1);
peekingIterator2 = new PeekingIterator<>(source2);
}
@Override
public boolean hasNext() {
return peekingIterator1.hasNext() || peekingIterator2.hasNext();
}
@Override
public T next() {
if(!peekingIterator1.hasNext()){
return peekingIterator2.next();
}
if(!peekingIterator2.hasNext()){
return peekingIterator1.next();
}
T peek1 = peekingIterator1.peek();
T peek2 = peekingIterator2.peek();
if(peek1.compareTo(peek2)<0){
return peekingIterator1.next();
}
return peekingIterator2.next();
}
}
The analysis are obvious here, SortedIterator.next
and SortedIterator.hasNext
run in constant time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With