Close
Register
Close Window

Data Structures and Algorithms

Chapter 4 Linear Structures

Show Source |    | About   «  4.7. Implementing Maps using Lists   ::   Contents   ::   4.9. Dynamic Array-Based Stacks  »

4.8. Doubly Linked Lists (optional)

4.8.1. Doubly Linked Lists

The singly linked list allows for direct access from a list node only to the next node in the list. A 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.

The most common reason to use a doubly linked list is because it gives an additional possibility to move both forwards and backwards in the list, and to efficiently add and remove elements from both ends. 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 header pointer, but we also add a tail pointer to the end of the list.

Here is an implementation for the class variables and the internal list node class. The only real difference between single linked lists is that we have pointers to the previous node, and a pointer to the tail of the list.

class DoubleLinkedList<E> implements List<E> {
    private DNode head;     // Pointer to list header
    private DNode tail;     // Pointer to list tail
    private int listSize;   // Size of list

    DoubleLinkedList() {
        head = null;
        tail = null;
        listSize = 0;
    }

    private class DNode {
        E elem;       // Value for this node
        DNode prev;   // Pointer to previous node in list
        DNode next;   // Pointer to next node in list

        DNode(E elem, DNode prev, DNode next) {
            this.elem = elem;
            this.prev = prev;
            this.next = next;
        }
    }
class DoubleLinkedList(List):
    def __init__(self):
        self._head = None    # Pointer to list header
        self._tail = None    # Pointer to list tail
        self._listSize = 0   # Size of list

# Python does not have internal classes, so we have to make the list node standalone.
class DoubleLinkedListNode:
    def __init__(self, elem, prev, next):
        self.elem = elem   # Value for this node
        self.prev = prev   # Pointer to previous node in list
        self.next = next   # Pointer to next node in list

The main advantage with doubly linked lists are that we can implement more advanced iterators (ListIterator in the Java standard API) that can move forward and backward through a list. In fact, Java’s standard LinkedList is implemented as a doubly linked list.

4.8.2. Implementation of the list methods

Getting and setting are exactly the same as for normal linked lists, so we don’t show them here.

4.8.2.1. Adding/inserting elements

Adding elements becomes a bit trickier, because we have to make sure that all pointers are updated correctly. We get some special cases – when the list is empty, or when we add before the head or after the tail.

    public void add(int i, E x) {
        if (!(0 <= i && i <= listSize)) throw new IndexOutOfBoundsException("list index out of range");
        if (listSize == 0) {
            head = tail = new DNode(x, null, null);
        } else if (i == 0) {
            DNode newhead = new DNode(x, null, head);
            head.prev = newhead;
            head = newhead;
        } else if (i == listSize) {
            DNode newtail = new DNode(x, tail, null);
            tail.next = newtail;
            tail = newtail;
        } else {
            DNode prev = head;
            for (int k = 0; k < i-1; k++)
                prev = prev.next;
            DNode next = prev.next;
            DNode newnode = new DNode(x, prev, next);
            prev.next = newnode;
            next.prev = newnode;
        }
        listSize++;
    }
    def add(self, i, x):
        if not (0 <= i <= self._listSize): raise IndexError("list index out of range")
        if self._listSize == 0:
            self._head = self._tail = DoubleLinkedListNode(x, None, None)
        elif i == 0:
            newhead = DoubleLinkedListNode(x, None, self._head)
            self._head.prev = newhead
            self._head = newhead
        elif i == self._listSize:
            newtail = DoubleLinkedListNode(x, self._tail, None)
            self._tail.next = newtail
            self._tail = newtail
        else:
            prev = self._head
            for _ in range(i-1):
                prev = prev.next
            next  = prev.next
            newnode = DoubleLinkedListNode(x, prev, next)
            prev.next = newnode
            next.prev = newnode
        self._listSize += 1

4.8.2.2. Removing elements

The same goes for removing elements – we get special cases when we remove the head or the tail.

    public E remove(int i) {
        if (!(0 <= i && i < listSize)) throw new IndexOutOfBoundsException("list index out of range");
        DNode removed;
        if (i == 0) {
            removed = head;
            head = removed.next;
            head.prev = null;
        } else if (i == listSize-1) {
            removed = tail;
            tail = removed.prev;
            tail.next = null;
        } else {
            DNode prev = head;
            for (int k = 0; k < i-1; k++)
                prev = prev.next;
            removed = prev.next;
            prev.next = removed.next;
            prev.next.prev = prev;
        }
        removed.prev = removed.next = null;   // For garbage collection
        listSize--;
        return removed.elem;
    }
    def remove(self, i):
        if not (0 <= i < self._listSize): raise IndexError("list index out of range")
        if i == 0:
            removed = self._head
            self._head = removed.next
            self._head.prev = None
        elif i == self._listSize-1:
            removed = self._tail
            self._tail = removed.prev
            self._tail.next = None
        else:
            prev = self._head
            for _ in range(i-1):
                prev = prev.next
            removed = prev.next
            prev.next = removed.next
            prev.next.prev = prev
        removed.prev = removed.next = None   # For garbage collection
        self._listSize -= 1
        return removed.elem

4.8.3. Full implementation

Here is the full implementation of doubly linked lists.

class DoubleLinkedList<E> implements List<E> {
    private DNode head;     // Pointer to list header
    private DNode tail;     // Pointer to list tail
    private int listSize;   // Size of list

    DoubleLinkedList() {
        head = null;
        tail = null;
        listSize = 0;
    }

    private class DNode {
        E elem;       // Value for this node
        DNode prev;   // Pointer to previous node in list
        DNode next;   // Pointer to next node in list

        DNode(E elem, DNode prev, DNode next) {
            this.elem = elem;
            this.prev = prev;
            this.next = next;
        }
    }

    public E get(int i) {
        if (!(0 <= i && i < listSize)) throw new IndexOutOfBoundsException("list index out of range");
        DNode current = head;
        for (int k = 0; k < i; k++)
            current = current.next;
        return current.elem;
    }

    public E set(int i, E x) {
        if (!(0 <= i && i < listSize)) throw new IndexOutOfBoundsException("list index out of range");
        DNode current = head;
        for (int k = 0; k < i; k++)
            current = current.next;
        E old = current.elem;
        current.elem = x;
        return old;
    }

    public void add(int i, E x) {
        if (!(0 <= i && i <= listSize)) throw new IndexOutOfBoundsException("list index out of range");
        if (listSize == 0) {
            head = tail = new DNode(x, null, null);
        } else if (i == 0) {
            DNode newhead = new DNode(x, null, head);
            head.prev = newhead;
            head = newhead;
        } else if (i == listSize) {
            DNode newtail = new DNode(x, tail, null);
            tail.next = newtail;
            tail = newtail;
        } else {
            DNode prev = head;
            for (int k = 0; k < i-1; k++)
                prev = prev.next;
            DNode next = prev.next;
            DNode newnode = new DNode(x, prev, next);
            prev.next = newnode;
            next.prev = newnode;
        }
        listSize++;
    }

    public E remove(int i) {
        if (!(0 <= i && i < listSize)) throw new IndexOutOfBoundsException("list index out of range");
        DNode removed;
        if (i == 0) {
            removed = head;
            head = removed.next;
            head.prev = null;
        } else if (i == listSize-1) {
            removed = tail;
            tail = removed.prev;
            tail.next = null;
        } else {
            DNode prev = head;
            for (int k = 0; k < i-1; k++)
                prev = prev.next;
            removed = prev.next;
            prev.next = removed.next;
            prev.next.prev = prev;
        }
        removed.prev = removed.next = null;   // For garbage collection
        listSize--;
        return removed.elem;
    }

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

    public int size() {
        return listSize;
    }


    public Iterator<E> iterator() {
        return new DoubleLinkedListIterator();
    }

    private class DoubleLinkedListIterator implements Iterator<E> {
        private DNode current;
        DoubleLinkedListIterator() {
            current = head;
        }
        public boolean hasNext() {
            return current != null;
        }
        public E next() {
            E x = current.elem;
            current = current.next;
            return x;
        }
    }
}
class DoubleLinkedList(List):
    def __init__(self):
        self._head = None    # Pointer to list header
        self._tail = None    # Pointer to list tail
        self._listSize = 0   # Size of list

    def get(self, i):
        if not (0 <= i < self._listSize): raise IndexError("list index out of range")
        current = self._head
        for _ in range(i):
            current = current.next
        return current.elem

    def set(self, i, x):
        if not (0 <= i < self._listSize): raise IndexError("list index out of range")
        current = self._head
        for _ in range(i):
            current = current.next
        old = current.elem
        current.elem = x
        return old

    def add(self, i, x):
        if not (0 <= i <= self._listSize): raise IndexError("list index out of range")
        if self._listSize == 0:
            self._head = self._tail = DoubleLinkedListNode(x, None, None)
        elif i == 0:
            newhead = DoubleLinkedListNode(x, None, self._head)
            self._head.prev = newhead
            self._head = newhead
        elif i == self._listSize:
            newtail = DoubleLinkedListNode(x, self._tail, None)
            self._tail.next = newtail
            self._tail = newtail
        else:
            prev = self._head
            for _ in range(i-1):
                prev = prev.next
            next  = prev.next
            newnode = DoubleLinkedListNode(x, prev, next)
            prev.next = newnode
            next.prev = newnode
        self._listSize += 1

    def remove(self, i):
        if not (0 <= i < self._listSize): raise IndexError("list index out of range")
        if i == 0:
            removed = self._head
            self._head = removed.next
            self._head.prev = None
        elif i == self._listSize-1:
            removed = self._tail
            self._tail = removed.prev
            self._tail.next = None
        else:
            prev = self._head
            for _ in range(i-1):
                prev = prev.next
            removed = prev.next
            prev.next = removed.next
            prev.next.prev = prev
        removed.prev = removed.next = None   # For garbage collection
        self._listSize -= 1
        return removed.elem

    def isEmpty(self):
        return self._listSize == 0

    def size(self):
        return self._listSize

    def __iter__(self):
        current = self._head
        while current is not None:
            yield current.elem
            current = current.next


# Python does not have internal classes, so we have to make the list node standalone.
class DoubleLinkedListNode:
    def __init__(self, elem, prev, next):
        self.elem = elem   # Value for this node
        self.prev = prev   # Pointer to previous node in list
        self.next = next   # Pointer to next node in list

   «  4.7. Implementing Maps using Lists   ::   Contents   ::   4.9. Dynamic Array-Based Stacks  »

nsf
Close Window