web-dev-qa-db-ja.com

Javaの循環リンクリスト

私は本を​​読んでデータ構造をブラッシュアップしていますが、質問の1つは、「最初」と「最後」のポインタを使用せずに、1つの参照を使用してアクセスを許可することで循環単一リンクリストを作成することです。電流"。質問を理解できるかどうかはわかりません。少なくとも最初か最後のどちらかが必要だといつも思っていました。これが私の実装ですが、それは「最初」であり、それを回避する方法がわかりません。最初に依存を排除​​するためにコードを調整する方法についてコメントしていただけますか?

class Link {
    public int iData;              
    public Link next;              

    public Link(int id) { // constructor
        iData = id;                         
    }                          

    public void displayLink() {
        System.out.print(iData + " ");
    }
}  // end class Link

次に、リスト自体を示します。

public class CircularLinkedList {
    private Link first;
    private Link current;    

    public Link getCurrent(){
        return current;
    }

    public void setCurrent(int data){

    }

    public void advance(){
        current = current.next;
    }

    public void insert(int data) {
        Link newLink = new Link(data);
        if (first == null) { 
            first = current = newLink;
        } else {
            current.next = newLink;
        }
        current = newLink;
        newLink.next = first;
    }
    ...
}
7
sam2015

循環リンクリストがある場合、各ノードは連続ループ内の次のノードを指します。したがって、最後も最初もありません。この状況では、開始した場所に戻るまでリスト全体をたどることができるため、データ構造(質問では「現在」と呼ばれています)へのポインターが1つだけ必要です。

1つのノードは次のようになります。

public class ListNode<T> {
    private T element;
    private ListNode<T> next;
}

したがって、この環境では、最初のノードをリストに追加すると、その「次の」ポインターがそれ自体を指します。 2番目のノードを追加すると、各「次の」ポインタは他のノードを指します。 3番目のノードを追加すると、それぞれが次のノードを指します。おそらく、これは何らかの方法で順序付けられています。追加された各ノードは、ループ内の適切な場所に挿入されます。

このようなデータ構造の適切な使用法は、毎日または1時間ごとに繰り返されるスケジュールです。すべてのイベントは循環リストに保存でき、「現在の」ポインタは常に次にスケジュールされている方を指します。

明らかに、このようなリストを検索するときは、最初のノードへの参照を保持する必要があります。これは、リスト全体がnullの「次の」ポインタで終わっていないため、リスト全体を検索したかどうかを知る唯一の方法です。

編集:以下の(広範な)コメントで説明されているように、ここでは「テール」ポインタが挿入操作に非常に適しているように思われます。これが私が投稿した元のメソッドで、名前がinsertAfterに変更されました。

public void insertAfter(int data) {
    Link newLink = new Link(data);
    if (current == null) { 
        newLink.next = newLink;
        current = newLink;
    } else {
        newLink.next = current.next;
        current.next = newLink;
    }
}

テールポインタは常にリストの最後のノードを指します。リストは循環しているため、これは最初のノードの前のノードにもなります。したがって、ポインタを操作するときは、必ず(tail.next == current)を維持してください。新しい挿入メソッドは次のとおりです。

public void insert(int data) {
    Link newLink = new Link(data);
    if (current == null) { 
        current = newLink;
        tail = newLink;
        newLink.next = newLink; // tail.next = current!
    } else {
        tail.next = newLink; // tail.next = current!
        newLink.next = current;
        current = newLink; // tail is unchanged, newLink is current
    }
}

値を挿入すると、正しく順序が狂うはずです。追加時に順序を維持するには、addメソッドを使用する必要があります。

public void add(int data) {
    this.insert(data);
    this.advance();
}

advanceのコードは次のとおりです。

public void advance() {
    if (current != null) {
        tail = current;
        current = tail.next; // tail.next = current!
    }
}

このように使用してリストを作成すると...

list1.add(1);
list1.add(2);
list1.add(3);
list2.insert(3);
list2.insert(2);
list2.insert(1);

...両方のリストは1-2-3の順序で、1が現在のものです。

7
Erick Robertson

使用する Node current, int currentIndex, int size。最後のノードのnextがリストの最初のノード(=循環)を指すようにします。現在のインデックスを使用すると、要素をウォーク(サイズ-1)して、currentの前のノードに到達できます。

0
Joop Eggen