2013-02-11 88 views
4

我需要在java中合併兩個字符串列表,我不太確定最好的方式來完成它。我必須使用迭代器和compareTo()方法。例如...使用迭代器合併列表

實施例:L1:A,B,C,d L2:B,d,F,G的結果:A,B,B,C,d,d,F,G

我可以假設輸入列表已經排序,我不能使用contains()方法。我有一些初步檢查,但while循環是什麼我堅持。

public static ListADT<String> merge(ListADT<String> L1,ListADT<String> L2) throws BadListException { 
ListADT<String> L3 = new ArrayList<String>; 
if(L1 == null || L2 == null) { 
    throw new BadListException(); 
} 
Iterator<String> itr1 = new L1.iterator(); 
Iterator<String> itr2 = new L2.iterator(); 
if(L1.size() == 0 && L2.size() == 0) { 
    return L3; 
} 
if(L1.size() == 0 && L2.size() != 0) { 
    for(int i = 0; i < L2.size(); i++) { 
     return L3.add(L2.get(i)); 
    } 
} 
if(L2.size() == 0 && L1.size() != 0) { 
    for(int i = 0; i < L1.size(); i++) { 
     return L3.add(L1.get(i)); 
    } 
} 
while(itr1.hasNext() || irt2.hasNext()) { 
    //merge the lists here? 
} 

}

任何幫助,將不勝感激。

回答

0

不要試圖管理一個空列表的合併與非空單作爲一種特殊情況,只是循環,直到迭代器的至少一個是有效的,做你的工作,直接出現:

public static ListADT<String> merge(ListADT<String> L1,ListADT<String> L2) throws BadListException { 
    ListADT<String> L3 = new ArrayList<String>; 

    Iterator<String> itr1 = new L1.iterator(), itr2 = new L2.iterator(); 

    while (itr1.hasNext() || itr2.hasNext()) { 
    if (!itr1.hasNext()) 
     L3.add(itr2.next()); 
    else if (!itr2.hasNext()) 
     L3.add(itr1.next()); 
    else { 
     String s1 = peek from itr1 
     String s2 = peek from itr2; 

     if (s1.compareTo(s2) < 0) { 
     L3.add(itr1.next()); 
     L3.add(itr2.next()); 
     } 
     else { 
     L3.add(itr2.next()); 
     L3.add(itr1.next()) 
     } 
    } 
    } 
} 
+0

這是不正確的:'L3.add(S1); L3.add(s2);''L1'中的下一項可能小於's2',但您已經將's2'加到輸出中!每循環迭代,合併循環只需要推進一個,而不是兩個迭代器。考慮合併「{1,2}」和「{3}」。你的算法會產生「{1,3,2}」,因爲它會在相同的迭代中加入1和3,然後纔有機會看「2」。 – dasblinkenlight 2013-02-11 02:02:21

+0

是的,沒有注意到衝動寫道。實際上,'Iterator '沒有辦法在不推進迭代器IIRC的情況下獲取元素。這完全違背了使用迭代器的目的。 – Jack 2013-02-11 02:04:18

+0

我的下一個問題是如何在不推進迭代器的情況下獲得下一個值,以便我可以compareTo()另一個列表中的另一個值。 – user1874239 2013-02-11 02:25:44

2

下面是一些僞代碼基本算法:

while(itr1 && itr2) 
{ 
    if(itr1 value < it2 value) 
     add itr1 to list 
     increment itr1 

    else 
     add itr2 to list 
     increment itr2 
} 

check if itr1 or itr2 still have more elements 
while itr1 or itr2 has more elements, add those elements to the list 

我們知道列表進行排序,所以在每一個階段,我們只是抓住從每個列表中的最小元素,並將其添加到合併列表。如果最後一個迭代器耗盡而另一個不是,那麼我們可以簡單地遍歷仍然有元素的迭代器,然後將每個元素依次附加到合併列表中。

正如你所看到的,在Java中使用迭代器做這件事有點讓人費解,因爲next()刪除了這個元素。解決此問題的一種方法是利用兩個Queue,每個迭代器一個,將來自該調用的值存儲到next()。然後,您需要比較每個隊列的頭部,將最小值添加到合併列表中,然後將其從各自的Queue中刪除。

+1

我認爲你不必要地通過引入另一個數據結構來使問題複雜化。您所需要的只是變量來保存每個列表中的當前值。請參閱我的答案:http://stackoverflow.com/a/14805301/44737 – rob 2013-02-11 03:29:06

4

如果您只是使用變量來保存來自每個迭代器的當前值,這是相當直接的。此解決方案假定您的列表不包含null,但添加空處理並不困難,因爲列表已排序。

package com.example; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Iterator; 
import java.util.List; 

public class IteratorMerge { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     List<String> list1 = Arrays.asList(new String[]{"A", "B", "C", "D"}); 
     List<String> list2 = Arrays.asList(new String[]{"B", "D", "F", "G"}); 

     System.out.println(merge(list1, list2)); 
    } 

    public static List<String> merge(List<String> L1,List<String> L2) { 
     List<String> L3 = new ArrayList<String>(); 

     Iterator<String> it1 = L1.iterator(); 
     Iterator<String> it2 = L2.iterator(); 

     String s1 = it1.hasNext() ? it1.next() : null; 
     String s2 = it2.hasNext() ? it2.next() : null; 
     while (s1 != null && s2 != null) { 
      if (s1.compareTo(s2) < 0) { // s1 comes before s2 
       L3.add(s1); 
       s1 = it1.hasNext() ? it1.next() : null; 
      } 
      else { // s1 and s2 are equal, or s2 comes before s1 
       L3.add(s2); 
       s2 = it2.hasNext() ? it2.next() : null; 
      } 
     } 

     // There is still at least one element from one of the lists which has not been added 
     if (s1 != null) { 
      L3.add(s1); 
      while (it1.hasNext()) { 
       L3.add(it1.next()); 
      } 
     } 
     else if (s2 != null) { 
      L3.add(s2); 
      while (it2.hasNext()) { 
       L3.add(it2.next()); 
      } 
     } 

     return L3; 
    } 
} 
2

正如您發現的那樣,使用迭代器合併是一種痛苦。讓我們明確說明了原因:

  • 對於合併的每一步,你要檢查序列的第一個元素,但你只能通過一個提前
  • Iterator#next()捆綁這些檢查預先操作到一個操作中,所以不可能檢查兩個序列的頭部,而不還通過兩個前進。

你需要的是一種方法,在一個Iterator的第一個元素偷看沒有推進它。如果你有這種能力,那麼合併看起來是這樣的:

public <T extends Comparable<T>> List<T> merge(Iterator<T> it1, Iterator<T> it2) { 
    PeekableIterator<T> seq1 = new PeekableIterator<T>(it1); 
    PeekableIterator<T> seq2 = new PeekableIterator<T>(it2); 
    List<T> merged = new ArrayList<T>(); 
    while (seq1.hasNext() && seq2.hasNext()) { 
     if (seq1.peekNext().compareTo(seq2.peekNext()) < 0) { 
      merged.add(seq1.next()); 
     } else { 
      merged.add(seq2.next()); 
     } 
    } 
    while (seq1.hasNext()) { 
     merged.add(seq1.next()); 
    } 
    while (seq2.hasNext()) { 
     merged.add(seq2.next()); 
    } 
    return merged; 
} 

而且事實證明,這是不是太難以創造這個PeekableIterator!你只需要跟蹤你目前是否有一個窺視元素,以及那個元素是什麼。

public class PeekableIterator<T> implements Iterator<T> { 
    private final Iterator<T> backing; 
    private boolean havePeek = false; 
    private T peek; 

    public PeekableIterator(Iterator<T> backing) { 
     this.backing = backing; 
    } 

    @Override 
    public boolean hasNext() { 
     return havePeek || backing.hasNext(); 
    } 

    @Override 
    public T next() { 
     if (havePeek) { 
      havePeek = false; 
      return peek; 
     } else { 
      return backing.next(); 
     } 
    } 

    public T peekNext() { 
     if (!havePeek) { 
      peek = backing.next(); 
      havePeek = true; 
     } 
     return peek; 
    } 

    @Override 
    public void remove() { 
     throw new UnsupportedOperationException(); 
    } 
} 

編輯

我沒注意上面的谷歌的番石榴庫,這是因爲這裏的PeekableIterator或多或少相同的參照PeekingIterator評論。如果你有權訪問第三方庫,這對你自己來說肯定會更可取。

0

公共類MergeIterator {

public static void main(String[] args) { 
     List<String> s1 = new ArrayList<String>(); 
    s1.add("a"); 
    s1.add("z"); 
    s1.add("b"); 
    s1.add("k"); 
    s1.add("c"); 
    Collections.sort(s1); 
    List<String> s2 = new ArrayList<String>(); 
    s2.add("p"); 
    s2.add("a"); 
    s2.add("d"); 
    s2.add("n"); 
    s2.add("m"); 
    Collections.sort(s2); 
    Iterator<String> it1 = s1.iterator(); 
    // sortIterator(it1); 
    Iterator<String> it2 = s2.iterator(); 
    System.out.println(); 
    combine(it1, it2); 
} 

private static Iterator<String> combine(Iterator<String> it1, 
     Iterator<String> it2) { 
    Iterator<String> it3 = null; 
    List<String> l1 = new ArrayList<>(); 
    String s1 = null, s2 = null; 
    while (it1.hasNext() || it2.hasNext()) { //line 1 

    s1 = (s1 == null ? (it1.hasNext() ? it1.next() : null) : s1); //line 2 
    s2 = (s2 == null ? (it2.hasNext() ? it2.next() : null) : s2); // line 3 
     if (s1 != null && s1.compareTo(s2) < 0) { // line 4 
      l1.add(s1); 
      s1 = null; 
     } else { 
      l1.add(s2); 
      s2 = null; 
     } 

    } 
    it3 = l1.iterator(); 
    return it3; 
} 

}

+0

@Idos我假設你用什麼解釋來結合方法。 it1&it2是一個排序數組。現在跳到第1行,我對它的代碼進行了評論,直到我的ant迭代器(it1或it2)具有值。第2行:它將檢查s1是否爲null,如果是,則爲it1賦值,並且如果s1不爲null,則將it1內的指針遞增到下一個元素,它將分配相同的值,s1對於第3行反之亦然。第4行跳轉:比較s1中的值是否小於s1,然後是將s1添加到列表l1中,否則將s2添加到l1。而已。 – bhuvnesh 2016-01-17 05:59:53

+0

@Idos示例:itr1 = {a,c}; itr2 = {b,d};
循環1 s1 = a s2 = b a cycle2 s1 = c s2 = b li.add(s2)s2 = null;
循環3 s1 = c s2 = d c cycle4 s1 = null s2 = d li.add(s2) – bhuvnesh 2016-01-17 06:07:47