.. _ListDouble: .. raw:: html .. |--| unicode:: U+2013 .. en dash .. |---| unicode:: U+2014 .. em dash, trimming surrounding whitespace :trim: .. odsalink:: DataStructures/DoubleLinkList.css .. odsalink:: AV/List/dlistCON.css .. This file is part of the OpenDSA eTextbook project. See .. http://algoviz.org/OpenDSA for more details. .. Copyright (c) 2012-2016 by the OpenDSA Project Contributors, and .. distributed under an MIT open source license. .. avmetadata:: :author: Cliff Shaffer :requires: linked list :satisfies: doubly linked list :topic: Lists Doubly Linked Lists =================== Doubly Linked Lists ------------------- The :ref:`singly linked list ` allows for direct access from a list node only to the next node in the list. A :term:`doubly linked list` allows convenient access from a list node to the next node and also to the preceding node on the list. The doubly linked list node accomplishes this in the obvious way by storing two pointers: one to the node following it (as in the singly linked list), and a second pointer to the node preceding it. .. _DblListFig: .. inlineav:: dlistDiagramCON dgm :align: center A doubly linked list. The most common reason to use a doubly linked list is because it is easier to implement than a singly linked list. While the code for the doubly linked implementation is a little longer than for the singly linked version, it tends to be a bit more "obvious" in its intention, and so easier to implement and debug. Whether a list implementation is doubly or singly linked should be hidden from the ``List`` class user. Like our singly linked list implementation, the doubly linked list implementation makes use of a :term:`header node`. We also add a tailer node to the end of the list. The tailer is similar to the header, in that it is a node that contains no value, and it always exists. When the doubly linked list is initialized, the header and tailer nodes are created. Data member ``head`` points to the header node, and ``tail`` points to the tailer node. The purpose of these nodes is to simplify the ``insert``, ``append``, and ``remove`` methods by eliminating all need for special-case code when the list is empty, or when we insert at the head or tail of the list. In our implementation, ``curr`` will point to the :term:`current position` (or to the :term:`trailer node` if the current position is at the end of the list). Here is the complete implementation for a ``Link`` class to be used with doubly linked lists. This code is a little longer than that for the singly linked list node implementation since the doubly linked list nodes have an extra data member. .. codeinclude:: Lists/DLink :tag: DLink Insert ~~~~~~ The following slideshows illustrate the ``insert`` and ``append`` doubly linked list methods. The class declaration and the remaining member functions for the doubly linked list class are nearly identical to the singly linked list version. While the code for these methods might be a little longer than their singly linked list counterparts (since there is an extra pointer in each node to deal with), they tend to be easier to understand. .. inlineav:: dlistInsertCON ss :points: 0.0 :required: False :threshold: 1.0 :id: 179781 :long_name: Doubly Linked List Insert :output: show Append ~~~~~~ .. inlineav:: dlistAppendCON ss :points: 0.0 :required: False :threshold: 1.0 :id: 179782 :long_name: Doubly Linked List Append :output: show Remove ~~~~~~ .. inlineav:: dlistRemoveCON ss :points: 0.0 :required: False :threshold: 1.0 :id: 179783 :long_name: Doubly Linked List Remove :output: show Prev ~~~~ .. inlineav:: dlistPrevCON ss :points: 0.0 :required: False :threshold: 1.0 :id: 179784 :long_name: Doubly Linked List Prev :output: show The only disadvantage of the doubly linked list as compared to the singly linked list is the additional space used. The doubly linked list requires two pointers per node, and so in the implementation presented it requires twice as much overhead as the singly linked list. Mangling Pointers ~~~~~~~~~~~~~~~~~ There is a space-saving technique that can be employed to eliminate the additional space requirement, though it will complicate the implementation and be somewhat slower. Thus, this is an example of a space/time tradeoff. It is based on observing that, if we store the sum of two values, then we can get either value back by subtracting the other. That is, if we store :math:`a + b` in variable :math:`c`, then :math:`b = c - a` and :math:`a = c - b`. Of course, to recover one of the values out of the stored summation, the other value must be supplied. A pointer to the first node in the list, along with the value of one of its two link fields, will allow access to all of the remaining nodes of the list in order. This is because the pointer to the node must be the same as the value of the following node's ``prev`` pointer, as well as the previous node's ``next`` pointer. It is possible to move down the list breaking apart the summed link fields as though you were opening a zipper. The principle behind this technique is worth remembering, as it has many applications. The following code fragment will swap the contents of two variables without using a temporary variable (at the cost of three arithmetic operations). .. codeinclude:: Lists/DList :tag: XOR A similar effect can be had by using the exclusive-or operator. This fact is widely used in computer graphics. A region of the computer screen can be highlighted by XORing the outline of a box around it. XORing the box outline a second time restores the original contents of the screen. .. odsascript:: DataStructures/DoubleLinkList.js .. odsascript:: AV/List/dlist.js .. odsascript:: AV/List/dlistDiagramCON.js .. odsascript:: AV/List/dlistInsertCON.js .. odsascript:: AV/List/dlistAppendCON.js .. odsascript:: AV/List/dlistRemoveCON.js .. odsascript:: AV/List/dlistPrevCON.js