Doubly-Linked Lists 4-02-2013 -...

37
Doubly-Linked Lists 4-02-2013

Transcript of Doubly-Linked Lists 4-02-2013 -...

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

harry->prev->next = harry->next; // Step 1 harry->next->prev = harry->prev; // Step 2 delete harry;

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;

}

Implementation of List

code for Maciel, Chapter 13

List 1.0

List 1.1 (iterators)

List 1.2 (destructor & copying)

Generic Algorithms

Maciel: Chapter 12