在O(1)时间内组合两个已排序的迭代器

时间:2016-02-18 21:40:13

标签: java iterator

在接受采访时我被问到以下问题:

  

在它们的排序内容上组合两个迭代器,使得   结果迭代器应该迭代这些2的组合   O(1)时间内按顺序排列的迭代器(这些迭代器遍历a   字符串)。

我写了下面的代码,但我确信它在O(1)时间内没有执行。您对匹配面试问题设置的约束有什么建议?

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;
  }
}

2 个答案:

答案 0 :(得分:5)

如果您不延长iterator并支持peek功能,我相信您无法做到这一点。这样的迭代器并不那么难。这是一种方法。

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;
    }
}

一旦你可以窥视,其余的很容易,你可以使用两个偷看迭代器构建SortedIterator,查看两个迭代器并推进具有较小元素的迭代器。

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();
    }
}

分析显而易见,SortedIterator.nextSortedIterator.hasNext在恒定时间内运行。

答案 1 :(得分:0)

我有一个类似的用例,但不是只有 2 个迭代器,我必须合并动态数量的迭代器。迭代器的数量可以超过2个。例如3个迭代器、4个迭代器或更多。

我使用了与 Sleiman Jneidi 建议的相同的解决方案。我修改了 SortedIterator 以支持多个迭代器,还支持根据需要按升序或降序排序。

对于 PeekingIterator,我使用了 Apache 公共集合的 Peeking 迭代器。 这是我的排序迭代器,如果有人可能需要合并多个排序迭代器,他们可以参考这个:

import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.collections4.iterators.PeekingIterator;

/**
 * This class is special implementation of Iterator.
 * It lets multiple sorted iterators to be merged to a single sorted iterator.
 * It will use given comparator to compare values from multiple iterators. 
 * 
 */
public class SortedMergedIterator<T> implements Iterator<T> {

    /**
     * Comparator that will be used to compare values from across the iterators.
     */
    private final Comparator<? super T> comparator;
    
    
    /**
     * List of sorted iterators which are required to be merged to a single sorted iterator.
     */
    private final List<PeekingIterator<T>> peekingIterators;
    
    
    /**
     * By default the sort order will be considered as ascending order.
     *
     * It this flag is set to true, the elements will be sorted in descending order.
     * In this case, it is pre-requisite that all the iterators being passed should be sorted in ascending order.
     */
    private final boolean sortOrderDescending;

    
    public SortedMergedIterator(final Comparator<? super T> comparator, final List<Iterator<T>> iterators) {
        this(comparator, iterators, false);
    }
    
    
    public SortedMergedIterator(final Comparator<? super T> comparator, final List<Iterator<T>> iterators, final boolean sortOrderDescending) {
        this.comparator = comparator;           
        this.peekingIterators = iterators.stream().map(iterator -> new PeekingIterator<>(iterator)).collect(Collectors.toList());
        this.sortOrderDescending = sortOrderDescending;
    }

    @Override
    public boolean hasNext() {

        // If at least one of the child iterator has next element.
        return this.peekingIterators.stream().anyMatch(Iterator::hasNext);
    }

    @Override
    public T next() {

        // Peek next value from all the iterators.
        final List<T> peekedValues = this.peekingIterators.stream().map(PeekingIterator::peek).collect(Collectors.toList());

        // Find the minimum value from all the peeked values.
        final T minElement = peekedValues
                .stream()
                .filter(Objects::nonNull)
                .min(this.sortOrderDescending ? this.comparator.reversed() : this.comparator)
                .orElse(null);
        
        // Return the next element from an iterator for which minimum value is found.
        return this.peekingIterators.get(peekedValues.indexOf(minElement)).next();
    }

}

使用这个迭代器的例子:

    public static void main(String[] args) {

        // Example of ascending order sorted iterators.
        final List<Integer> list1 = Lists.newArrayList(4,7,11,12,16);
        final List<Integer> list2 = Lists.newArrayList(1,3,5,10,15);
        final List<Integer> list3 = Lists.newArrayList(6,8,13,18,20);
        final List<Integer> list4 = Lists.newArrayList(2,9,14,17,19);
        
        final SortedMergedIterator<Integer> sortedIterator =
                new SortedMergedIterator<>(Comparator.comparingInt(a -> a), Arrays.asList(list1.iterator(), list2.iterator(), list3.iterator(), list4.iterator()));

        while (sortedIterator.hasNext()) {
            System.out.println(sortedIterator.next());
        }
        
        System.out.println();
        
        // Example of descending order sorted iterators.
        final List<Integer> list5 = Lists.newArrayList(16,12,11,7,4);
        final List<Integer> list6 = Lists.newArrayList(15,10,5,3,1);
        final List<Integer> list7 = Lists.newArrayList(20,18,13,8,6);
        final List<Integer> list8 = Lists.newArrayList(19,17,14,9,2);
        
        final SortedMergedIterator<Integer> descSortedIterator =
                new SortedMergedIterator<>(Comparator.comparingInt(a -> a), Arrays.asList(list5.iterator(), list6.iterator(), list7.iterator(), list8.iterator()), true);

        while (descSortedIterator.hasNext()) {
            System.out.println(descSortedIterator.next());
        }
    }