Дважды связанные по кругу списки в Java

#java

#java

Вопрос:

Я пытаюсь реализовать список с двойной круговой связью и немного заблудился; у меня есть свой список с двойной связью, но я не совсем уверен в том, как сделать его круговым. Я прочитал, что только последний узел указывает на первый узел, так что это будет что-то вроде:

 public void addLast(DNode v) {
addAfter(header, v);

}
  

Вот код для моего дважды связанного списка:

 public class Dlist {

protected int size;
protected DNode header, trailer;

public Dlist() {
    size = 0;
    header = new DNode(null, null, null);
    trailer = new DNode(null, header, null);
    header.setNext(trailer);

}//end DList()

public int size() { return size; }

public boolean isEmpty() { return (size == 0); }


public DNode getFirst() throws IllegalStateException {
    if (isEmpty()) throw new IllegalStateException("List is empty");
    return header.getNext();
}//end isEmpty


public DNode getLast() throws IllegalStateException {
    if (isEmpty()) throw new IllegalStateException("List is empty");
    return trailer.getPrev();

}//end getLast


public DNode getPrev(DNode v) throws IllegalArgumentException {
    if (v == header) throw new IllegalArgumentException
        ("Cannot move back past the header of the list");
    return v.getPrev();
}


public DNode getNext(DNode v) throws IllegalArgumentException {
    if (v == trailer) throw new IllegalArgumentException
        ("Cannot move forward past the trailer of the list");
    return v.getNext();
}

public void addBefore(DNode v, DNode z) {
    DNode u = getPrev(v);
    z.setPrev(u);
    z.setNext(v);
    v.setPrev(z);
    u.setNext(z);
    size  ;

}

public void addAfter(DNode v, DNode z) {
    DNode w = getNext(v);
    z.setPrev(v);
    z.setNext(w);
    w.setPrev(z);
    v.setNext(z);
    size  ;
}

public void addFirst(DNode v) {
    addAfter(header, v);
}

public void addLast(DNode v) {
    addBefore(trailer, v);

}

public void remove(DNode v) {
    DNode u = getPrev(v);
    DNode w = getNext(v);

    w.setPrev(u);
    u.setNext(w);
    v.setPrev(null);
    v.setNext(null);
    size--;

}

public boolean hasPrev(DNode v) { return v != header;}

public boolean hasNext(DNode v) { return v != trailer; }

public String toString() {
    String s = "[";
    DNode v = header.getNext();
    while (v != trailer ) {
        s  = v.getElement();
        v = v.getNext();
        if (v != trailer) {
            s  = ",";

        }
        s = "]";
        return s;
    }
    return s;
}
  

РЕДАКТИРОВАТЬ: DNode;

 public class DNode {
protected String element;
protected DNode next, prev;

public DNode(String e, DNode p, DNode n) {
    element = e;
    prev = p;
    next = n;

}

public String getElement() {return element;}

public DNode getPrev() { return prev; }

public DNode getNext() { return next; }

public void setElement(String newElem) { element = newElem; }

public void setPrev(DNode newPrev) { prev = newPrev; }

public void setNext(DNode newNext) {next = newNext;}
}
  

Комментарии:

1. Нам нужно увидеть ваш класс DNode. Вот где происходит волшебство

2. @Joe Phillips, я только что отредактировал в своем DNode

3. @ Thomas Jungblut как мне сделать этот список с двойной круговой связью

Ответ №1:

Теперь у вас есть header и trailer псевдо-узел:

 header <--> first <--> second <--> ... <--> last <--> trailer
  

Вместо этого вам пришлось бы соединить первый и последний в обоих направлениях.

         .-> first  <--> second <--> ... <--> last <-.
        |                                           |
        '-------------------------------------------'
  

В качестве альтернативы вы также можете объединить заголовок и трейлер в одном узле, но тогда это не будет «чистым» списком с круговой связью, поскольку при обходе вам придется перешагивать через узел заголовка / трейлера.

         .-> first  <--> second <--> ... <--> last <--> header/trailer <-.
        |                                                               |
        '---------------------------------------------------------------'
  

Комментарии:

1. Итак, в моем коде, где он указывает на заголовок и трейлер, должен ли он указывать на первый / последний узел? итак, пример будет похож на тот первый фрагмент кода в моем вопросе?

2. Вам нужен только один из first и last , поскольку last является предшественником first , и, таким образом, вы можете получить один из другого.

3. А, ладно, это имеет смысл. собираюсь попробовать

Ответ №2:

Циклический список не имеет заголовка, трейлера, первого или последнего элемента. Это накладывает на вас особые требования.

У вас может быть либо псевдоэлемент, либо нет.

Если у вас есть псевдоэлемент, то вам нужно обрабатывать его как особый случай в getNext и getPrevious , поскольку он должен быть пропущен, но вам не нужно беспокоиться о том, что произойдет, когда список пуст или если вы удаляете что-то важное.

Если у вас нет псевдоэлемента, то нет особых случаев для next и previous, но есть особый случай для add, когда список пуст, и remove, если вы удаляете узел, с помощью которого вы связываетесь со списком.