实现双向链表

huangapple go评论99阅读模式
英文:

Implement doubly linked list

问题

我在这个论坛上查阅了关于双向链表实现的信息,但对下面的代码无法理解。

// DoublyLinkedList 的实例变量
private final Node<E> header;     // 头哨兵
private final Node<E> trailer;    // 尾哨兵
private int size = 0;       // 链表中元素的数量
private int modCount = 0;   // 链表的修改次数(添加或移除操作)

/**
 * 创建充当哨兵的两个元素
 */
public DoublyLinkedList() {
    header = new Node<>(null, null, null);      // 创建头哨兵
    trailer = new Node<>(null, header, null);   // 尾哨兵位于头哨兵之前
    header.setNext(trailer);                    // 头哨兵之后是尾哨兵
}

我看过关于链表和双向链表的视频,但没有看过这种实现。例如,trailer = new Node<> (null,header,null) 的背后逻辑是什么?

英文:

I've looked around on this forum regarding the implementation of doubly linked lists and I can't a grasp of the below code.

// instance variables of the DoublyLinkedList
    private final Node&lt;E&gt; header;     // header sentinel
    private final Node&lt;E&gt; trailer;    // trailer sentinel
    private int size = 0;       // number of elements in the list
    private int modCount = 0;   // number of modifications to the list (adds or removes)

    /**
     * Creates both elements which act as sentinels
     */
    public DoublyLinkedList() {

        header = new Node&lt;&gt;(null, null, null);      // create header
        trailer = new Node&lt;&gt;(null, header, null);   // trailer is preceded by header
        header.setNext(trailer);                    // header is followed by trailer
    }

I've seen videos about linked lists and doubly ones, but I haven't seen this kind of implementation. What's the logic behind, for example: trailer = new Node&lt;&gt;(null, header, null)?

答案1

得分: 0

你可能有一个类似以下的双向链表:

/**
 * 双向链表。
 */
public class DoubleLinkedList<E> {

    private final Node<E> header;      // 头哨兵
    private final Node<E> trailer;     // 尾哨兵
    private int size = 0;              // 链表中元素的数量
    private int modCount = 0;          // 对链表的修改次数(添加或移除操作)

    public DoubleLinkedList() {
        this.header = new Node<>(
            // 头哨兵的后继节点是尾哨兵。
            // 设置为:header.setNext(trailer);
            null,
            // 头哨兵的前驱节点始终为null,
            // 因为在第一个节点之前没有节点。
            null,
            // 节点的有效载荷为null。
            // 我猜这只是示例的一部分。
            null
        );

        this.trailer = new Node<>(
            // 尾哨兵的后继节点始终为null,
            // 因为在最后一个节点之后没有节点。
            null,
            // 尾哨兵的前驱节点是头哨兵,
            // 在构造该对象时设置。
            header,
            // 节点的有效载荷为null。
            // 我猜这只是示例的一部分。
            null
        );
        // 现在头哨兵的后继节点被设置为尾哨兵。
        header.setNext(trailer);
    }

    // 一些链表方法,如add、remove、get等……

    /**
     * 链表中的节点。
     *
     * @param <T> 链表中存储对象的类型。
     */
    static class Node<T> {

        /**
         * 该节点的前驱节点。
         */
        private Node<T> predecessor;

        /**
         * 该节点的后继节点。
         */
        private Node<T> successor;

        /**
         * 载荷(有效数据)
         */
        private final T payload;

        public Node(final Node<T> successor, final Node<T> predecessor, final T payload) {
            this.predecessor = successor;
            this.successor = successor;
            this.payload = payload;
        }

        // 获取器和设置器:

        private Node<T> getPredecessor() {
            return this.predecessor;
        }

        private void setNext(final Node<T> next) {
            this.predecessor = next;
        }

        private Node<T> getSuccessor() {
            return this.successor;
        }

        private void setPrevious(final Node<T> previous) {
            this.successor = previous;
        }

        private T getPayload() {
            return this.payload;
        }
    }
}

这个架构不太美观,但我认为这个解释适用于你的情况。

英文:

You have Probably some DoubleLinkedList like:

     /**
* A double linked list.
*
*/
public class DoubleLinkedList&lt;E&gt; {
private final Node&lt;E&gt; header;     // header sentinel
private final Node&lt;E&gt; trailer;    // trailer sentinel
private int size = 0;       // number of elements in the list
private int modCount = 0;   // number of modifications to the list (adds or removes)
public DoubleLinkedList() {
this.header = new Node&lt;&gt;(
// The successor of the header is the trailer.
// It will be set with: header.setNext(trailer);
null,
// The predecessor of the header is always null,
// because there there is no node before the first
null,
// The payload of the node is null.
// I guess it is just a part of the example.
null
);
this.trailer = new Node&lt;&gt;(
// The successor of the trailer is always null,
// because there there is no node after the last
null,
// The predecessor of the trailer is the header
// at construction of this object
header,
// The payload of the node is null.
// I guess it is just a part of the example.
null
);
// Now is the successor of the header set to the trailer.
header.setNext(trailer);
}
// Some list methods like add, remove, get, ...
/**
* The nodes of the List
*
* @param &lt;T&gt; The type of the stored objects in the list.
*/
static class Node&lt;T&gt; {
/**
* The predecessor of this node.
*/
private Node&lt;T&gt; predecessor;
/**
* The successor of this node.
*/
private Node&lt;T&gt; successor;
/**
* The payload
*/
private final T payload;
public Node(final Node&lt;T&gt; successor, final Node&lt;T&gt; predecessor, final T payload) {
this.predecessor = successor;
this.successor = successor;
this.payload = payload;
}
// Getter and Setter:
private Node&lt;T&gt; getPredecessor() {
return this.predecessor;
}
private void setNext(final Node&lt;T&gt; next) {
this.predecessor = next;
}
private Node&lt;T&gt; getSuccessor() {
return this.successor;
}
private void setPrevious(final Node&lt;T&gt; previous) {
this.successor = previous;
}
private T getPayload() {
return this.payload;
}
}
}

This is architectural not very beautiful, but I think this explanation matches your case.

答案2

得分: -1

给定一个列表(可以是任何类型),你至少需要知道如何访问第一个元素,以及如何判断是否已经遍历到最后一个元素。

有几种方法可以满足这些要求。

对于链表,为了知道列表从哪里开始,你可以简单地引用第一个节点,或者你可以有一个始终存在的完整的“虚拟”节点。

为了知道列表在哪里结束,你可以有一个空的“next”引用,或者你可以有一个始终存在的完整的“虚拟”节点。

使用虚拟节点的方法通常可以使代码更清晰,因为所有实际节点将始终有一个“previous”节点,而且所有实际节点将始终有一个“next”节点。

这似乎是你的代码片段中采取的方法。

英文:

Given a list (of any kind), you need to know at least how to get to the first element, and how to tell when you've seen the last element.

There are a few ways to arrange for these requirements to be satisfied.

For a linked list, to know where the list starts, you might have a simple references to the first node, or you might have a full 'dummy' node that always exists.

To know where the list ends, you might have a null 'next' reference, or you might have a full 'dummy' node that always exists.

The dummy-node approach can often result in cleaner code, because then all actual nodes will always have a 'previous' node, and all actual nodes will always have a 'next' node.

That seems to be the approach being taken in your code extract.

huangapple
  • 本文由 发表于 2020年10月21日 19:44:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/64462821.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定