web-dev-qa-db-ja.com

バイナリツリーの順序反復子

バイナリツリーのルートを取得し、ノードのノードを反復処理するJavaイテレータ(つまり、nextおよびhasNextメソッドが必要)を記述する方法in-order fashionのバイナリツリー?

24
Paul S.

サブツリーの最初の要素は常に左端の要素です。要素の後の次の要素は、その右サブツリーの最初の要素です。要素に適切な子がない場合、次の要素は要素の最初の右祖先です。要素に正しい子も右の祖先もない場合、それは右端の要素であり、反復の最後になります。

私のコードが人間が読めるものであり、すべてのケースをカバーすることを願っています。

public class TreeIterator {
    private Node next;

    public TreeIterator(Node root) {
        next = root;
        if(next == null)
            return;

        while (next.left != null)
           next = next.left;
    }

    public boolean hasNext(){
        return next != null;
    }

    public Node next(){
        if(!hasNext()) throw new NoSuchElementException();
        Node r = next;

        // If you can walk right, walk right, then fully left.
        // otherwise, walk up until you come from left.
        if(next.right != null) {
            next = next.right;
            while (next.left != null)
                next = next.left;
            return r;
        }

        while(true) {
            if(next.parent == null) {
                next = null;
                return r;
            }
            if(next.parent.left == next) {
                next = next.parent;
               return r;
            }
            next = next.parent;
        }
     }
 }

次のツリーを検討してください。

     d
   /   \
  b     f
 / \   / \
a   c e   g
  • 最初の要素は「完全にルートから離れている」
  • aには右の子がないため、次の要素は「左から来るまでアップ」です
  • bには適切な子があるため、bの右サブツリーを反復処理します
  • cには正しい子がありません。親はbで、これはトラバースされています。次の親はdであり、これは走査されていないため、ここで終了します。
  • dには正しいサブツリーがあります。その左端の要素はeです。
  • ...
  • gには正しいサブツリーがないため、上に歩きます。右から来たので、fにアクセスしました。 dが訪問されました。 dには親がないため、さらに上に移動することはできません。私たちは右端のノードから来て、繰り返し処理を完了しました。
39
John Dvorak

イテレータの次のエントリ 'nextEntry()'を取得するために、Java.util.TreeMap以下に貼り付けました。平易な英語では、ルートノードが最初にnullでないことを確認してから、nullを返します。そうでない場合、nullでない場合は正しいノードをビストします。次に、左(nullでない場合)にアクセスし、nullがヒットするまでwhileループでその左に繰り返しアクセスします。元の右ノードがnullである場合、すべてではなく、nullでない場合は親ノードにアクセスします。次に、nullになるか、現在アクセスしているノードの右(子)ノードが最後の位置に等しくなるまで、親をビストするwhileループに入ります。座っているエントリを返します。これらすべてのオプションに失敗すると、(元の)ルートノードを返します。 'HasNext()'は、返す「次のエントリ」がnullかどうかを単にチェックします。

public final boolean hasNext() {
     return next != null;
}

final TreeMap.Entry<K,V> nextEntry() {
    TreeMap.Entry<K,V> e = next;
    if (e == null || e.key == fenceKey)
        throw new NoSuchElementException();
    if (m.modCount != expectedModCount)
        throw new ConcurrentModificationException();
    next = successor(e);
    lastReturned = e;
    return e;
}

static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
    if (t == null)
        return null;
    else if (t.right != null) {
        Entry<K,V> p = t.right;
        while (p.left != null)
            p = p.left;
        return p;
    } else {
        Entry<K,V> p = t.parent;
        Entry<K,V> ch = t;
        while (p != null && ch == p.right) {
            ch = p;
            p = p.parent;
        }
        return p;
    }
}
2
apcris