Doubly-Linked Lists 4-02-2013 -...
Transcript of Doubly-Linked Lists 4-02-2013 -...
Doubly-linked list
Implementation of List
ListIterator
Reading: Maciel, Chapter 13
HW#4 due: Wednesday, 4/03 (new due date)
Quiz on Thursday, 4/04, on nodes & pointers
Review Session on pointers – tonight,
Tuesday, 4/02, 7:00 to 8:00 in the ITL
Exam#2: Wednesday, April 10th, 7:00 pm, Science Center 162
Limitations of a singly-linked list include:
can insert only after a referenced node
removing node requires pointer to previous node
can traverse list only in the forward direction
We can remove these limitations:
Add a pointer in each node to the previous node:
This is called a doubly-linked list
DNode* sharon = new DNode("Sharon"); // Link new DNode to its neighbors sharon->next = sam; // Step 1 sharon->prev = sam->prev; // Step 2
// Link old predecessor of sam to new predecessor.
sam->prev->next = sharon; // Step 3 // Link to new predecessor.
sam->prev = sharon; // Step 4
represent a list with three data members:
head of the list
tail of the list
current size of the list
represent contents with DNodes
another simplification:
add a dummy first node
// dnode.h
#ifndef DNODE_H_
#define DNODE_H_
/** A DNode is the building block for a double-linked list. */
struct DNode {
T data;
DNode* next; // pointer to next DNode
DNode* prev; // pointer to previous DNode
DNode(const T& data_item,
DNode* prev_val = NULL
DNode* next_val = NULL) :
data(data_item), next(next_ptr), prev(prev_val) {}
};
#endif
template<typename T> // cf list.h
class List {
public:
#include "list_iterator.h"
// Give list access to internal values in iterator.
friend class iterator;
#include "list_const_iterator.h"
// Give list access to internal values in const_iterator.
friend class const_iterator;
private:
// Insert definition of nested class DNode here.
#include “DNode.h"
DNode* head;
DNode* tail;
int num_items;
void push_front(const T& item) {
head = new DNode(item, NULL, head); // Step 1
if (head->next != NULL)
head->next->prev = head; // Step 2
if (tail == NULL) // List was empty.
tail = head;
num_items++;
}
void push_back(const T& item) {
if (tail != NULL) {
// Step 1 tail->next = new DNode(item, tail, NULL);
// Step 2 tail = tail->next;
num_items++;
} else { // List was empty.
push_front(item);
}
}
iterator insert(iterator pos, const T& item) {
/* Check for special cases */
if (pos.current == head) { // insert at head
push_front(item);
return begin();
} else if (pos.current == NULL) {
// Past the last node.
push_back(item);
return iterator(this, tail);
}
/* continued on next slide */
/* continued from previous slide */
/* Create a new node linked before the node
referenced by pos (insert in middle) */
DNode* new_node =
new DNode(item, pos.current->prev,
pos.current); // Step 1
pos.current->prev->next = new_node; // Step 2
pos.current->prev = new_node; // Step 3
num_items++;
return iterator(this, new_node);
}
iterator insert(iterator pos, const T& item) { // Check for special cases if (pos.current == head) { push_front(item); return begin(); } else if (pos.current == NULL) { // Past the last node. push_back(item); return iterator(this, tail); } // Create a new node linked before node referenced by pos. DNode* new_node = new DNode(item, pos.current->prev, pos.current); // Step 1 // Update links pos.current->prev->next = new_node; // Step 2 pos.current->prev = new_node; // Step 3 num_items++; return iterator(this, new_node); }
Variation: circular list
link the last node to the first node
can be singly-linked or doubly-linked
Another simplification
add a dummy first node
This is the implementation in Maciel, Chapter 13
/* representation for nodes */
template <class T> class ListNode
// T is the type of element stored in the list.
{
friend class List<T>;
private:
T element;
ListNode<T> * next;
ListNode<T> * previous;
};
Figure 13.1: A class of nodes, Maciel, p. 228
template <class T>
class List
// T is the type of element stored in the list.
{
public: // on next slide
private:
ListNode<T> * p_head_node;
};
Figure 13.2: The class List, Maciel, p. 228
// public interface for class List
public:
List() {
p_head_node = new ListNode<T>;
p_head_node->next
= p_head_node->previous
= p_head_node;
}
// continued on next slide
Figure 13.3: A first version of List, Maciel, p. 230
// public interface for class List, continued
T & back() { return p_head_node->previous->element;}
const T & back() const { return p_head_node->previous->element; }
void push_back( const T & new_element );
void pop_back();
void test_print() const; // for testing only
private: // as in Figure 13.2
Figure 13.3: A first version of List, Maciel, p. 230
template<class T> void List<T>::test_print() const { for ( ListNode<T> * p_node = p_head_node->next; p_node != p_head_node; p_node = p_node->next ) { cout << p_node->element << ' '; } cout << endl; }
Figure 13.4: The internal test driver, Maciel, p. 230
Properties of List iterator, itr
dereferencing a List iterator should yield an element of the list,
so, (*itr) should be of type T
incrementing a List iterator should advance the iterator to the next node on the list
so, (++itr) should move the iterator to the next node
iterators are similar to, but not the same as pointers!
template <class T>
class ListIterator {
friend class List<T>;
public: // on next slide
private:
ListIterator( ListNode<T> * p ) {
p_current_node = p;
}
ListNode<T> * p_current_node;
/* points to the node that contains the element
that the iterator currently “points” to
}; Figure 13.7: declaration of ListIterator, Maciel, p. 235
// template <class T> class ListIterator, continued
public:
ListIterator() { p_current_node = NULL; }
T & operator*() { return p_current_node->element; }
bool operator!=( const ListIterator & rhs ) const {
return (p_current_node != rhs.p_current_node); }
ListIterator & operator++(); // prefix version (++itr)
ListIterator & operator--();
ListIterator operator++(int); // postfix version (itr++)
ListIterator operator--(int);
Figure 13.7: public interface for ListIterator, Maciel, p. 235
add a typedef declaration to class List<T>
template <class T>
class List {
public:
typedef ListIterator<T> iterator;
modify ListNode to grant friendship to ListIterator
template <class T>
class ListNode {
friend class List<T>;
friend class ListIterator<T>;
// template <class T> class ListIterator, continued
public:
ListIterator() { p_current_node = NULL; }
T & operator*() { return p_current_node->element; }
bool operator!=( const ListIterator & rhs ) const {
return (p_current_node != rhs.p_current_node); }
ListIterator & operator++(); // prefix version (++itr)
ListIterator & operator--();
ListIterator operator++(int); // postfix version (itr++)
ListIterator operator--(int);
Figure 13.7: public interface for ListIterator, Maciel, p. 235
// Assumption on T: values can be printed using <<
template <typename T>
void print( List<T> & ls ) {
for ( typename List<T>::iterator itr = ls.begin();
itr != ls.end(); ++itr ) {
cout << *itr << ' ';
}
cout << endl;
}
Figure 13.10: A function that prints a List, Maciel, p. 237
/* Note: the argument really should be passed by constant reference, but we haven't implemented constant iterators. */
*/
template<typename T> // cf list.h
class List {
public:
#include "list_iterator.h"
// Give list access to internal values in iterator.
friend class iterator;
#include "list_const_iterator.h"
// Give list access to internal values in const_iterator.
friend class const_iterator;
private:
// Insert definition of nested class DNode here.
#include “DNode.h"
DNode* head;
DNode* tail;
int num_items;
/* class List, continued */
// Member Functions
public:
/* Default: construct an empty list. */
List() : head(NULL), tail(NULL), num_items(0) {}
/* class List, continued */
// Member Functions, continued
/* Copy Constructor. */
List(const List<Item_Type>& other) List()
: head(NULL), tail(NULL), num_items(0) {
for (const_iterator itr = other.begin();
itr != other.end();
++itr) {
push_back(*itr);
}
}
/* class List, continued */
// Member Functions, continued
/* Destructor. */
~List() {
while (head != NULL) {
DNode* current = head;
head = head->next;
delete current;
}
tail = NULL;
num_items = 0;
}
/* class List, continued */
// Member Functions, continued
/* Swap this list contents with another one */
void swap(List<T>& other) {
std::swap(head, other.head);
std::swap(tail, other.tail);
std::swap(num_items, other.num_items);
}
/* class List, continued */
// Member Functions, continued
/* Assignment Operator. */
List<T>& operator=(const List<T>& other) {
// Make a copy of the other list.
List<T> temp_copy(other);
// Swap contents of self with the copy.
swap(temp_copy);
// Return -- upon return the copy will be destroyed.
return *this;
}