0% found this document useful (0 votes)
90 views47 pages

Doubly Linked List: - Ed. 2 and 3.: Chapter 4 - Ed. 4: Chapter 3

This document describes how to implement a doubly linked list using Java. Key points: - Each node contains references to both the next and previous nodes, allowing bidirectional traversal. - Header and trailer sentinel nodes are used to mark the start and end of the list. - Inserting and deleting at the head/tail is easy without link hopping by updating the header/trailer. - Inserting and deleting in the middle requires finding the neighboring nodes by traversing links. - Common list operations like size, isEmpty, first, last, prev are supported in O(1) time.

Uploaded by

sh00008
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
90 views47 pages

Doubly Linked List: - Ed. 2 and 3.: Chapter 4 - Ed. 4: Chapter 3

This document describes how to implement a doubly linked list using Java. Key points: - Each node contains references to both the next and previous nodes, allowing bidirectional traversal. - Header and trailer sentinel nodes are used to mark the start and end of the list. - Inserting and deleting at the head/tail is easy without link hopping by updating the header/trailer. - Inserting and deleting in the middle requires finding the neighboring nodes by traversing links. - Common list operations like size, isEmpty, first, last, prev are supported in O(1) time.

Uploaded by

sh00008
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 47

Doubly Linked List

- Ed. 2 and 3.: Chapter 4


- Ed. 4: Chapter 3
Recall that the deletion of an element at the tail is not easy because we
have to find the node before the tail (the last node) by link hopping.
head tail

next next next next

element element element element


Baltimore Rome Seattle Toronto

This problem can be easily solved by using the double linked list.
A node in a doubly linked list: A com pound object that
stores a reference to an elem ent and tw o references, called
next and prev, to the next and previous nodes, respectively.

E lem ent

N ode
Reference to an
elem ent
Reference to next
next node
prev Referenc e to
previous node
F o r c o n v e n ie n c e , a d o u b ly lin k e d lis t h a s a h e a d e r n o d e a n d a
tr a ile r n o d e . T h e y a re a ls o c a lle d s e n tin e l n o d e s , in d ic a tin g
b o th th e e n d s o f a lis t.

header tra ile r

B a ltim o re Rom e S e a ttle

Difference from singly linked lists:


- each node contains two links.
- two extra nodes: header and trailer, which contain no
elements.
Example:

50B0
0 5110
5050
50A0 0

5100
5090
5060 50F0
5070
5080 50D0 Node
5080 50E0 Rome prev
0 next
5070 0
Element
5050 Seattle
50D0
5080
5060 50E0

50A0 Baltimore
5060 50C0
50C0
5050
header
50B0
0 5110
5050
50A0 0

5100
5090
5060
5070 50F0
Node
5080 50D0
prev
5080 50E0 Rome next
0
0 Element
5070
5050 Seattle
50D0
5080
5060 50E0

50A0 Baltimore
5060 50C0
5050 50C0
header
50B0
0 5110
5050
50A0 0

5100
5090
5060 Node
5070 50F0
5080 50D0 prev
next
5080 50E0 Rome
0 Element
5070 0
5050 Seattle
5080 50D0
5060 50E0

50A0 Baltim ore


5060 50C0
5050 50C0
header
50B0
0 5110
5050
50A0 0

5100
5090
5060
5070 50F0 Node
5080 50D0 prev
5080 Rome next
50E0
0 Element
5070 0
5050 Seattle
50D0
5080
50E0
5060
50A0 Baltimore
5060 50C0
5050 50C0
header
50B0
0 5110
5050
50A0 0

5100
5090
5060
5070 50F0 Node
5080 50D0
prev
5080 Rome next
50E0
0 Element
5070 0
5050 Seattle
50D0
5080
50E0
5060
50A0 Baltimore
5060 50C0
5050 50C0
header
50B0
0 5110
5050
50A0 0

5100
5090
5060 Node
5070 50F0
5080 50D0 trailer prev
next
5080 50E0 Rome Element
0
5070 0
5050 Seattle
5080 50D0
5060 50E0

50A0 Baltimore
5060 50C0
5050 50C0
Class DLNode
Here is an implementation of nodes for doubly
linked lists in Java:

public class DLNode {


private Object element;
private DLNode next, prev;

public DLNode() {
this( null, null, null );
}

public DLNode( Object e, DLNode p, DLNode n


) {
element = e;
next = n;
prev = p;
}
void setElement( Object newElem ) {
element = newElem;
}

void setNext( DLNode newNext ) {


next = newNext;
}

void setPrev( DLNode newPrev ) {


prev = newPrev;
}

Object getElement() {
return element;
}

DLNode getNext() {
return next;
}

DLNode getPrev() {
return prev;
}
}
Insertion of an Element at the
Head
B e fo re th e in se rtio n :

h ea d er tra iler

B a ltim ore Rom e S ea ttle


Have a new node:

header trailer

Baltimore Rome Seattle

Toronto

DLNode x = new DLNode();


x.setElement(new String(“Toronto”));
(x.element  new String(“Toronto”))
Update the links:

header trailer

Baltimore Rome Seattle

Toronto

x.setPrev(header); x.prev  header;


x.setNext(header.getNext()); x.next  header.next;
(header.getNext()).setPrev(x); header.next.prev  x;
header.setNext(x); header.next  x;
A fte r th e in s e rtio n :

header tra ile r

T o ro n to B a ltim o re Rom e S e a ttle


Deleting an Element at the Tail
B e f o r e th e d e le t io n :

header tr a ile r

T o ro n to B a ltim o re Rom e S e a ttle


Update the links:

header trailer

Toronto Baltimore Rome

Seattle

((trailer.getPrev()).getPrev).setNext(trailer);
trailer.setPrev((trailer.getPrev()).getPrev());
trailer.prev.prev.next  trailer;
trailer.prev  trailer.prev.prev;
A fte r th e d e le tio n :
header tr a ile r

T o ro n to B a ltim o re Rom e
Deleting an element at the head is similar to deleting an element at
the tail.

Inserting an element at the tail is similar to inserting an element at


the head.

No link hopping is needed in any of the operations.

However, for inserting a node into the middle of a double linked


list or deleting a node in the middle, link hopping is always needed.
public class NodeList implements List {

protected int numElts; // Number of elements in the list


protected DNode header, trailer; // Special sentinels
/** Constructor that creates an empty list; O(1) time */
public NodeList() {
numElts = 0;
header = new DNode(null, null, null); // create header
trailer = new DNode(header, null, null); // create trailer
header.setNext(trailer); // make header and trailer point to each other
}
/** Checks if position is valid for this list and converts it to
* DNode if it is valid; O(1) time */
public DNode checkPosition(Position p) throws InvalidPositionException {
if (p == null)
throw new InvalidPositionException
("Null position passed to NodeList");
if (p == header)
throw new InvalidPositionException
("The header node is not a valid position");
if (p == trailer)
throw new InvalidPositionException
("The trailer node is not a valid position");
try {
DNode temp = (DNode)p;
if ((temp.getPrev() == null) || (temp.getNext() == null))
throw new InvalidPositionException
("Position does not belong to a valid NodeList");
return temp;
} catch (ClassCastException e) {
throw new InvalidPositionException
("Position is of wrong type for this list");
}
}
/** Returns the number of elements in the list; O(1) time */
public int size() { return numElts; }
/** Returns whether the list is empty; O(1) time */
public boolean isEmpty() { return (numElts == 0); }
/** Returns the first position in the list; O(1) time */
public Position first()
throws EmptyListException {
if (isEmpty())
throw new EmptyListException("List is empty");
return header.getNext();
}

public Position last() throws EmptyContainerException {


if (isEmpty())
throw new EmptyContainerException("List is empty");
return trailer.getPrev();
}
/** Returns the position before the given one; O(1) time */
public Position prev(Position p)
throws InvalidPositionException, BoundaryViolationException {
DNode v = checkPosition(p);
DNode prev = v.getPrev();
if (prev == header)
throw new BoundaryViolationException
("Cannot advance past the beginning of the list");
return prev;
}
/** Insert the given element before the given position, returning
* the new position; O(1) time */
public Position insertBefore(Position p, Object element)
throws InvalidPositionException { //
DNode v = checkPosition(p);
numElts++;
DNode newNode = new DNode(v.getPrev(), v, element);
v.getPrev().setNext(newNode);
v.setPrev(newNode);
return newNode;
}
public Position insertLast(Object element) {numElts++;
DNode newNode = new DNode(trailer.getPrev(), trailer, element);
// System.out.println(((ONode)newNode.element()).inorderNum());
trailer.getPrev().setNext(newNode);
trailer.setPrev(newNode);
//trailer.getPrev().setNext(newNode);
return newNode;
}

public Position insertAfter(Position p, Object element) {


DNode v = checkPosition(p);
numElts++;
DNode newNode = new DNode(v, v.getNext(), element);
v.getNext().setPrev(newNode);
v.setNext(newNode);
return newNode;}
/** Insert the given element at the beginning of the list, returning
* the new position; O(1) time */
public Position insertFirst(Object element) {
numElts++;
DNode newNode = new DNode(header, header.getNext(), element);
header.getNext().setPrev(newNode);
header.setNext(newNode);
return newNode;
}
/**Remove the given position from the list; O(1) time */
public Object remove(Position p)
throws InvalidPositionException {
DNode v = checkPosition(p);
numElts--;
DNode vPrev = v.getPrev();
DNode vNext = v.getNext();
vPrev.setNext(vNext);
vNext.setPrev(vPrev);
Object vElem = v.element();
// unlink the position from the list and make it invalid
v.setNext(null);
v.setPrev(null);
return vElem;
}
/** Replace the element at the given position with the new element
* and return the old element; O(1) time */
public Object replace(Position p, Object element)
throws InvalidPositionException {
DNode v = checkPosition(p);
Object oldElt = v.element();
v.setElement(element);
return oldElt;
}

public void swapElements(Position a, Position b)


throws InvalidPositionException {

//System.out.println("swapElement is executed!!!");
DNode pA = checkPosition(a);
DNode pB = checkPosition(b);
Object temp = pA.element();
pA.setElement(pB.element());
pB.setElement(temp);
}
public Position next(Position p)
throws InvalidPositionException, BoundaryViolationException {
DNode v = checkPosition(p);
DNode next = v.getNext();
if (next == trailer)
throw new BoundaryViolationException
("Cannot advance past the beginning of the list");
return next;
}

public Iterator positions() {


return new PositionIterator(this);
}
public Iterator elements(){return new PositionIterator(this);}

}
Data Structure Exercises 6.1
Double-Ended Queues

R e c a ll th a t a q u e u e h a s a fr o n t a n d a r e a r . E le m e n ts c a n b e
a d d e d a t th e re a r a n d c a n b e re m o v e d a t th e fro n t.

F ro n t R ear

N o w w e a ls o w a n t to a d d e le m e n ts a t th e fro n t a n d re m o v e
th e m a t th e re a r.
Definition: A double-ended queue is a queue that supports
insertion and deletion at both the front and the rear of the
queue.

deque: pronounciated as “deck”


The Deque Abstract Data Type
A deque D is an abstract data type that supports the following
four fundamental methods:

insertFirst(e): Insert a new element e at the beginning of D.


Input: Object; Output: None.
insertLast(e): Insert a new element e at the end of D.
Input: Object; Output: None.
removeFirst(): Remove and return the first element of D; an
error occurs if D is empty.
Input: None; Output: Object
removeLast(): Remove and return the last element of D; an
error occurs if D is empty.
Input: None; Output: Object
Other supporting methods:

first(): Return the first element of D; an error occurs if D is


empty
Input: None; Output: Object
last(): Return the last element of D; an error occurs if D is
empty
Input: None; Output: Object
size(): Return the number of elements of D.
Input: None; Output: integer
isEmpty(): Determine if D is empty.
Input: None; Output: Boolean
public interface Deque {
void insertFirst(Object e);
void insertLast(Object e);
Object removeFirst();
Object removeLast();
Object first();
Object last();
int size();
boolean isEmpty();

This table shows a series of operations and their effects. The deque is
empty initially.

Operation Output D
insertFirst(3) - (3)
insertFirst(5) - (5,3)
removeFirst() 5 (3)
insertLast(7) - (3,7)
removeFirst() 3 (7)
removeLast() 7 ()
removeFirst() “error” ()
isEmpty() true ()
Implementing a Deque with a
Doubly Linked List
Recall that elements can be inserted and deleted easily at both
the head and tail of a doubly linked list. Therefore, a deque can
be implemented with a doubly linked list.

Example:

header trailer

Toronto Baltimore Rome Seattle


Class MyDeque
public class MyDeque implements Deque {
DLNode header, trailer;
int size;
public MyDeque() {
header = new DLNode();
trailer = new DLNode();
header.setNext( trailer );
trailer.setPrev( header );
size = 0;
}

header trailer
public Object first() throws DequeEmptyException {
if( isEmpty() )
throw new DequeEmptyException( "Deque is empty." );
return header.getNext().getElement();
}

link hopping
header

Baltimore
public void insertFirst( Object o ) {
DLNode second = header.getNext();
DLNode first = new DLNode( o, header, second );
second.setPrev( first );
header.setNext( first );
size++;
}

second
header first second
header
……
first …

Object o Object o
public Object removeLast() {
if( isEmpty() )
throw new DequeEmptyException( "Deque is empty." );
DLNode last = trailer.getPrev();
Object o = last.getElement();
DLNode secondtolast = last.getPrev();
trailer.setPrev( secondtolast );
secondtolast.setNext( trailer );
size--;
return o;
}
secondtolast
trailer
secondtolast last
trailer
… last

Object o Object o
public Object last() throws DequeEmptyException {
if( isEmpty() )
throw new DequeEmptyException( "Deque is empty." );
return trailer.getPrev().getElement();
}

public void insertLast( Object o ) {


DLNode secondLast = trailer.getPrev();
DLNode last = new DLNode( o, secondLast, trailer );
secondLast.setNext( last ); secondtolast
trailer.setPrev( last ); trailer
size++;
} … last

public int size( ) {return size;}

public boolean isEmpty( ) {


return header.getNext() == trailer;}
Object o
public Object removeFirst() {
if( isEmpty() )
throw new DequeEmptyException( "Deque is empty." );
DLNode first = header.getNext();
Object o = first.getElement();
DLNode second = first.getNext();
header.setNext( second ); first second
header
second.setPrev( header );
size--;
return o; …
}
}
public class GenerateDeque {
public static void main(String args[]) {
MyDeque D = new MyDeque(); int i;
for (i = 0; i < 10; i++) {
D.insertFirst(new Integer(i));}
for (i = 0; i < 10; i++)
System.out.print(((Integer) D.removeFirst()).intValue());
}
}
Implementing Stacks and Queues
with Deques
Stacks and queues can be easily implemented with a deque.
All we need to do is to map the operations.

Implementation of a stack with a deque:

Stack Method Deque Implementation


size() size()
isEmpty() isEmpty()
top() last()
push(e) insertLast(e)
pop() removeLast()
Implementation of a queue with a deque:

Queue Method Deque Implementation


size() size()
isEmpty() isEmpty()
front() first()
enqueue(e) insertLast()
dequeue() removeFirst()
Class DequeStack
public class DequeStack implements Stack {
private Deque D;
public DequeStack() {
D = new MyDeque();
}
public int size() {
return D.size();
}
public boolean isEmpty() {
return D.isEmpty();
}
public void push( Object obj ) {
D.insertLast( obj );
}
public void top() throws StackEmptyException {
try {
return D.last();
}
catch( DequeEmptyException ece ) {
throw new StackEmptyException( "Stack is empty." );
}
}
public Object pop() throws StackEmptyException {
try {
return D.removeLast();
}
catch( DequeEmptyException ece ) {
throw new StackEmptyException( "Stack is empty." );
}
}
}
Data Structure Exercises 6.2

You might also like