使用函数样式合并两个数组

时间:2016-08-10 07:01:17

标签: java scala clojure functional-programming java-8

我正在查看其中一个问题How to merge two sorted arrays,并努力使用Java 8流转换解决方案。但仍然没有成功。实际上,我在这里没有什么用处。

必须有一种方法来使用函数式编程中的索引来处理这样的循环。在不改变时间复杂度的情况下,如何在Scala,Clojure等其他语言中完成这项工作?可能那时我可以尝试用Java复制它。

编辑:代码中提到的问题是最有效的,我不想在此上妥协。

8 个答案:

答案 0 :(得分:6)

事实上,到处都有相同的方法:你重复两个集合,将最少的集合头部添加到结果中,然后重复使用其余集合,直到其中一个集合(或两者)为空。在clojure:

(defn merge-sorted [pred coll1 coll2]
  (loop [coll1 coll1 coll2 coll2 res []]
    (cond (or (empty? coll1) (empty? coll2)) (concat res coll1 coll2)
          (pred (first coll1) (first coll2)) (recur (rest coll1)
                                                    coll2
                                                    (conj res (first coll1)))
          :else (recur coll1 (rest coll2) (conj res (first coll2))))))

user> (merge-sorted < [1 3 5 6 7 8 9 40 50] [1 2 5 10 100])
(1 1 2 3 5 5 6 7 8 9 10 40 50 100)

答案 1 :(得分:5)

我认为这很容易用尾递归表达:

def merge(x: List[Int], y: List[Int]): List[Int] = {
  def loop(xss: List[Int], yss: List[Int], acc: List[Int]) = (xss, yss) match {
    case (x :: xs, y :: ys) if x < y => loop(xs, yss, x :: acc)
    case (_, y :: ys) => loop(xss, ys, y :: acc)
    case (x :: xs, Nil) => loop(xs, Nil, x :: acc)
    case (Nil, Nil) => acc.reverse
  }

  loop(x, y, Nil)
}

答案 2 :(得分:3)

也许这样的事情?可能做得更漂亮,但总体思路应该有效。

public static <T extends Comparable<T>> Stream<T> foldSorted(Stream<T> left, Stream<T> right) {
    Iterator<T> leftIterator = left.iterator();
    Iterator<T> rightIterator = right.iterator();

    Iterator<T> iterator = new Iterator<T>() {
        private T nextLeft = getNextLeft();
        private T nextRight = getNextRight();

        private T getNextLeft() {
            return leftIterator.hasNext() ? leftIterator.next():null;
        }

        private T getNextRight() {
            return rightIterator.hasNext() ? rightIterator.next():null;
        }

        @Override
        public boolean hasNext() {
            return nextLeft != null || nextRight != null;
        }

        @Override
        public T next() {
            T result = null;

            if(nextLeft != null) {
                if(nextRight != null) {
                    if(nextLeft.compareTo(nextRight) < 0) {
                        result = nextLeft;
                        nextLeft = getNextLeft();
                    } else {
                        result = nextRight;
                        nextRight = getNextRight();
                    }
                } else {
                    result = nextLeft;
                    nextLeft = getNextLeft();
                }
            } else {
                result = nextRight;
                nextRight = getNextRight();
            }

            return result;
        }
    };

    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
}

现在,你可以这样做:

@Test
public void verifyFoldSorted() {
    List<Integer> result = StreamUtils.foldSorted(Stream.of(1, 5, 7, 9), Stream.of(2, 5)).collect(Collectors.toList());
    assertEquals(Arrays.asList(1, 2, 5, 5, 7, 9), result);

    result = StreamUtils.foldSorted(Stream.of(8, 10), Stream.of(1, 2, 7, 9)).collect(Collectors.toList());
    assertEquals(Arrays.asList(1, 2, 7, 8, 9, 10), result);
}

答案 3 :(得分:2)

您可以要合并的数组包装成二维数组,然后执行:

IntStream result = Arrays.stream(new int[][]{{3, 2, 1}, {5, 2, 4}})
                         .flatMapToInt(Arrays::stream)
                         .sorted();

请注意,数组是否已预先排序并不重要。

答案 4 :(得分:2)

<强> Scala的

如果你想要一个懒惰的评估,即每个元素只在需要时进行排序和检索,那么一种方法就是将<p ng-repeat="s in mytest.specialities"> {{s.name}} / {{s._id}} / {{s.description}} </p> 转换为Array s。

Iterator

class ArrayMerger[A:Ordering](arrA:Array[A], arrB:Array[A]) extends Iterator[A] { import math.Ordering.Implicits._ private val a: BufferedIterator[A] = arrA.toIterator.buffered private val b: BufferedIterator[A] = arrB.toIterator.buffered override def hasNext: Boolean = a.hasNext || b.hasNext override def next(): A = if (!a.hasNext) b.next() else if (!b.hasNext) a.next() else if (a.head < b.head) a.next() else b.next() } val am = new ArrayMerger(Array('a','c','t','y'),Array('b','w','z'))//Iterator[Char] am.toArray // Array(a, b, c, t, w, y, z) <-- iterator consumed 也有惰性评估,但也可以编入索引。

Stream

答案 5 :(得分:2)

@leetwinski的尾递归解决方案效果很好,但它并不懒惰。这是Clojure中的一个懒惰解决方案:

(defn merge-sorted [pred coll1 coll2]
  (lazy-seq
   (if (some empty? [coll1 coll2])
     (concat coll1 coll2)
     (let [[head1 & tail1] coll1
           [head2 & tail2] coll2]
       (if (pred head1 head2)
         (cons head1 (merge-sorted pred tail1 coll2))
         (cons head2 (merge-sorted pred coll1 tail2)))))))

示例:

(letfn [(multiples [x]
          (iterate (partial +' x) x))]
  (take 10 (merge-sorted < (multiples 2) (multiples 3))))
;;=> (2 3 4 6 6 8 9 10 12 12)

答案 6 :(得分:0)

您还可以使用折叠在Scala中完成它,

val l1 = List(1,2,3,4)
val l2 = List(1, 2, 3, 4, -1, 2, 3, 5, 6, 7, 8)

val merged = l2.foldLeft(l1)((acc,i) => acc :+ i).sorted

答案 7 :(得分:0)

可以使用第三方库完成:

int[] a = { 1, 3, 5, 7 };
int[] b = { 2, 4, 6 };
int[] c = IntStream.merge(a, b, (v1, v2) -> v1 < v2 ? Nth.FIRST : Nth.SECOND).toArray();
N.println(c); // output: [1, 2, 3, 4, 5, 6, 7]

我无法在此提及图书馆,因为我的帐户可能会因为自我推销而被暂停。但是图书馆可以在我的其他答案中找到。