Java 双链表哨兵法
我正在浏览Java中的双链接列表,我正在阅读来自Java的双链接列表中的哨兵。哪个国家 为了避免在附近操作时出现一些特殊情况 双链接列表的边界,它有助于在 列表的两端:列表开头的头节点,以及 列表末尾的拖车节点。这些“虚拟”节点是已知的 作为哨兵(或卫兵),他们不存储 一级序列Java 双链表哨兵法,java,linked-list,doubly-linked-list,Java,Linked List,Doubly Linked List,我正在浏览Java中的双链接列表,我正在阅读来自Java的双链接列表中的哨兵。哪个国家 为了避免在附近操作时出现一些特殊情况 双链接列表的边界,它有助于在 列表的两端:列表开头的头节点,以及 列表末尾的拖车节点。这些“虚拟”节点是已知的 作为哨兵(或卫兵),他们不存储 一级序列 那些特殊情况是什么?为什么我们需要哨兵方法?是强制性的吗?如果我们对双链表使用正常的方法(没有哨兵),这不会节省这些额外节点的内存吗?当以循环方式制作双链表时,我们必须移除哨兵吗 维基百科注释简要提到使用sentinel
那些特殊情况是什么?为什么我们需要哨兵方法?是强制性的吗?如果我们对双链表使用正常的方法(没有哨兵),这不会节省这些额外节点的内存吗?当以循环方式制作双链表时,我们必须移除哨兵吗 维基百科注释简要提到使用sentinel节点简化链表的实现 sentinel节点是位于列表前面的虚拟节点 在双链接列表中,sentinel节点指向列表的第一个和最后一个元素。我们不再需要像处理单链接列表那样,为列表的开头和结尾保留单独的指针 我们也不必担心头指针和尾指针的更新,因为正如我们将看到的,如果我们在sentinel节点之后插入,从而在列表中预先插入一个项,或者在sentinel节点之前插入,从而在列表中追加一个项,那么这种情况就会自动发生 我们可以消除用于单链接列表的容器对象,因为sentinel节点可以跟踪列表中的第一个和最后一个元素。如果我们这样做了,那么我们将向用户返回指向sentinel节点的指针 但是,数据结构通常使用容器对象进行设计,容器对象调解数据结构用户与数据结构实现之间的通信,因此我们将保留容器对象 @6502 on的回答非常有用 以下是双链接节点列表中删除节点的代码,其中NULL用于标记列表的结尾,first和last两个指针用于保存第一个和最后一个节点的地址:
// Using NULL and pointers for first and last
if (n->prev) n->prev->next = n->next;
else first = n->next;
if (n->next) n->next->prev = n->prev;
else last = n->prev;
这是相同的代码,其中有一个特殊的虚拟节点来标记列表的结束,列表中第一个节点的地址存储在特殊节点的下一个字段中,列表中的最后一个节点存储在特殊虚拟节点的prev字段中:
// Using the dummy node
n->prev->next = n->next;
n->next->prev = n->prev;
同样的简化也适用于节点插入;例如,要在节点x之前插入节点n(x==NULL或x==&dummy表示插入到最后一个位置),代码如下:
// Using NULL and pointers for first and last
n->next = x;
n->prev = x ? x->prev : last;
if (n->prev) n->prev->next = n;
else first = n;
if (n->next) n->next->prev = n;
else last = n;
及
正如您所看到的,对于双链接列表,所有特殊情况和所有条件都删除了虚拟节点方法。维基百科注释简要提到使用sentinel节点简化链接列表的实现 sentinel节点是位于列表前面的虚拟节点 在双链接列表中,sentinel节点指向列表的第一个和最后一个元素。我们不再需要像处理单链接列表那样,为列表的开头和结尾保留单独的指针 我们也不必担心头指针和尾指针的更新,因为正如我们将看到的,如果我们在sentinel节点之后插入,从而在列表中预先插入一个项,或者在sentinel节点之前插入,从而在列表中追加一个项,那么这种情况就会自动发生 我们可以消除用于单链接列表的容器对象,因为sentinel节点可以跟踪列表中的第一个和最后一个元素。如果我们这样做了,那么我们将向用户返回指向sentinel节点的指针 但是,数据结构通常使用容器对象进行设计,容器对象调解数据结构用户与数据结构实现之间的通信,因此我们将保留容器对象 @6502 on的回答非常有用 以下是双链接节点列表中删除节点的代码,其中NULL用于标记列表的结尾,first和last两个指针用于保存第一个和最后一个节点的地址:
// Using NULL and pointers for first and last
if (n->prev) n->prev->next = n->next;
else first = n->next;
if (n->next) n->next->prev = n->prev;
else last = n->prev;
这是相同的代码,其中有一个特殊的虚拟节点来标记列表的结束,列表中第一个节点的地址存储在特殊节点的下一个字段中,列表中的最后一个节点存储在特殊虚拟节点的prev字段中:
// Using the dummy node
n->prev->next = n->next;
n->next->prev = n->prev;
同样的简化也适用于节点插入;例如,要在节点x之前插入节点n(x==NULL或x==&dummy表示插入到最后一个位置),代码如下:
// Using NULL and pointers for first and last
n->next = x;
n->prev = x ? x->prev : last;
if (n->prev) n->prev->next = n;
else first = n;
if (n->next) n->next->prev = n;
else last = n;
及
正如您所看到的,双链表的虚拟节点方法删除了所有特殊情况和所有条件。这个答案对您有帮助吗?是否必须使用带有双链表的sentinels节点?否。这只是存储第一个和最后一个节点的技巧。它有助于编写更简洁的代码。您可以使用o(1)操作轻松地在第一个和最后一个位置插入节点。@SSP时间复杂度与(o(1))相同,无论是否使用伪/哨兵头/尾。拥有它们的唯一好处是简化插入/删除代码。你最后一次对O(1)的评论可能会让人感到困惑。这个答案对你有帮助吗?是否必须使用带有双链接列表的sentinels节点?没有。这只是存储第一个和最后一个节点的技巧。它有助于编写更简洁的代码。您可以使用o(1)操作轻松地在第一个和最后一个位置插入节点。@SSP时间复杂度与(o(1))相同,无论是否使用伪/哨兵头/尾。拥有它们的唯一好处是简化插入/删除代码。你最后一次对O(1)的评论可能会让人们感到困惑,以为你不是这样想的。