chapter 7: the list adt

Post on 24-Feb-2016

66 Views

Category:

Documents

6 Downloads

Preview:

Click to see full reader

DESCRIPTION

Chapter 7: The List ADT. Chapter 7 Lists Overview The List ADT and its uses; dynamic memory allocation; programming with linked lists. Objectives. 1. Understanding and applying the List ADT. 2. Implementing a List Class using an array. 3. Implementing a List Class using a linked list. - PowerPoint PPT Presentation

TRANSCRIPT

Chapter 7: The List ADT

• Chapter 7

– Lists• Overview

– The List ADT and its uses; dynamic memory allocation; programming with linked lists.

Objectives• 1. Understanding and applying the List ADT.• 2. Implementing a List Class using an array.• 3. Implementing a List Class using a linked list.• 4. Using dynamic allocation and pointers in C++.• 5. Variations on the linked list.• 6. Creating a class with overloaded operators.

The List ADT• Characteristics:• A List L stores items of some type, called

ListElementType.• Operations:• void L.insert(ListElementType elem)• Precondition: None.• Postcondition:Lpost = Lpre with an instance

of elem added to Lpost.

List ADT, first• bool L.first(ListElementType &elem)• Precondition: None• Postcondition:If the list is empty, none.

Otherwise, the variable elem contains the first item in L; the “next” item to be returned is the second in L.

• Return: true if and only if there is at least one element in L.

List ADT, next• bool L.next(ListElementType &elem)• Precondition: The “first” operation has

been called at least once.• Postcondition:Variable elem contains the

next item in L, if there is one, and the next counter advances by one; if there is no next element, none.

• Return: true if and only if there is a next item.

A useful exercise

• Define some additional operations that might be useful for a List ADT.

List traversal• The process of accessing each item

in the list• Can be defined in terms of two other

operations– Accessing the first element in a list– Accessing the next element in a list

Implementing lists• A header file for the list ADT

– cx7-1.h (on author’s web page)– See next slide

• Must include List ADT– characteristics– operations

Code Example 7-1• // Code Example 7-1: List ADT header file• #include "dslib.h"• // the type of the individual elements in list is defined here• typedef int ListElementType;• // implementation specific stuff here• class List {• public:• List();• void insert(const ListElementType & elem);• bool first(ListElementType & elem);• bool next(ListElementType & elem);• private:• // implementation specific stuff here • };

List();

• Is the list copy constructor• With no parameters or body it is a

‘default constructor’

void insert(const ListElementType & elem);

• & means pass by reference– Value parameters should only be used for

simple types (int, char, etc.) which have simple copy constructors.

– For more complex data types, avoid the copy constructor by passing it by address

• const means the element cannot be modified in this function

Lists using arrays• The simplest method to implement a

List ADT is to use an array• “linear list”, “contiguous list”• Characteristics are

– Array for storing entries (listArray)– numberOfElements– currentPosition

Header file for array list• // cx7-2.h• #include "dslib.h"• // the type of the individual elements in the list is

defined here

• typedef int ListElementType;

• // the maximum size for lists is defined here

• const int maxListSize = 1000;

Code Example 7-2• class List {• public:• List();• void insert(const ListElementType & elem);• bool first(ListElementType & elem);• bool next(ListElementType & elem);• private:• ListElementType listArray[maxListSize];• int numberOfElements;• int currentPosition;• };

Array List Constructor• // cx7-3.cpp• #include "cx7-2.h"

• List::List()• {• // initialize to an empty list• numberOfElements = 0;• currentPosition = -1;• }

Insertion into linear list• void List::insert(const ListElementType & elem)• {• assert(numberOfElements < maxListSize);• listArray[numberOfElements] = elem;• numberOfElements++; }

Iterator function: first• bool List::first(ListElementType & elem)• {• if (numberOfElements == 0)• return false;• else {• currentPosition = 0;• elem = listArray[currentPosition];• return true;• }• }

Iterator function: next• bool List::next(ListElementType & elem)• {• // currentPosition should always be• // greater than or equal to zero• assert(currentPosition >= 0);• if (currentPosition >= numberOfElements - 1)• return false;• else {• currentPosition++;• elem = listArray[currentPosition];• return true; }• }

Simple List Client• // cx7-4.cpp• #include "cx7-2.h" // header for Linear List;

ListElementType is int• int main()• { List l;• ListElementType i; // header defines this as int• cout << "Enter items to add to list, or 0 to stop: ";• cin >> i;• while (i != 0) {• l.insert(i);• cin >> i; }

Client main continued• cout << "Here are the items in the list.\n";• ListElementType elem;• bool notEmpty(l.first(elem));• while (notEmpty) {• cout << elem << endl;• notEmpty = l.next(elem);• }• return 0;• }

Problems with arrays• Array implementations of lists use a static data

structure. Often defined at compile-time. Cannot be altered while program is running.

• This means we usually waste space rather than have program run out.

• It also means that data must be added to the end. If inserted in front, others must shuffle down. This is slow and inefficient.

Figure 7-1

2 4 5 8 11 13 6

2 4 5 8 11 13 61

insert 1 here

0 1 2 3 4 5 6 7 8 9 10111213indices

Linked list implementation• Data storage must now contain both

item and pointer to next item.• These are called ‘nodes’• This can be made dynamic• Much more efficient for insertion and

deletion

Figure 7-2

7 12 5

Figure 7-3

7 12 5

head

Adding a node (insertion)• A four step process• Add at front of list

– Create new node– Copy data into it– Copy head into its link field– Copy node pointer to head

Figure 7-4

7 12 5

9

head

Figure 7-5

7 12 5

9

head

Adding to end of list• A five step process

– Create new node– Copy data into it– Assign new node ptr to tail->link – Assign 0 to new node link (not NULL)– Assign new node ptr to tail

Figure 7-6

7 12 5

9

headtail

Figure 7-7

7 12 5

9

head

tail

Algorithm 7-1: List Traversal

• Comment: Assume that “head” is the name of the external link to the list.

• current = head;• while current is not NULL {• process the node current points to;• current = the link field of the node

current points to;• }

Linked list example• typedef int ListElementType;• class List {• // Use L to mean "this List"• public:• List();• // Precondition: None• // Postcondition: L is an empty List• void insert(const ListElementType & elem);• // Precondition: None• // Postcondition: Lpost = Lpre with an • // instance of elem added to Lpost

First()• bool first(ListElementType & elem);• // Precondition: None• // Postcondition: If the list is empty, none. • // Otherwise, the variable • // elem contains the first item in L; • // the "next" item returned is the second in L.• // Returns: true, if and only if,• // there is at least one element in L.

List class, public (con’t)• bool next(ListElementType & elem);• // Precondition: The "first" operation has been

called at least once.

• // Postcondition: Variable elem contains the next item in L, if there is one, and the next counter advances by one; if there is no next element, none.

• // Returns: true if and only if there was a next item.

7.5 (con’t) private of next• private:• struct Node; // declaration without definition• typedef Node *Link; // use declaration of Node• struct Node { // now we define Node• ListElementType elem;• Link next;• };• Link head;• Link tail;• Link current;• };

Linked list constructor

• List::List()• {• // Initialize an empty list• head = 0;• tail = 0;• current = 0;• }

Insert for linked list• void List::insert(const ListElementType & elem)• {• Link addedNode(new Node);• assert(addedNode); // check whether node was allocated• addedNode->elem = elem;• if (head == 0) // list was empty -- set head• head = addedNode;• else• tail->next = addedNode;• tail = addedNode;• addedNode->next = 0;• }

An easy mistake to make• //unsafe test of pointer with 0• int main() {• int * p; //p is a pointer to an int, initialized to 0• if (p = 0) //obviously, == was intended• cout << “zero pointer\n”;• else• cout << “non-zero pointer\n”;• return 0;• }

Dynamic memory allocation• Use the ‘new’ operator instead of old C

calloc, malloc and realloc• This draws from the “free store”• Dynamic allocation occurs at run-time, not

compile time• To check on availability of memory use an

assertion.– link newNode = new Node;– assert(newNode);

Linked list implementation

• List::List()• {• // Initialize an empty List• head = 0;• tail = 0;• current = 0;• }

Code Example 7-8• void List::insert(const ListElementType & elem)• {• Link addedNode = new Node;• assert(addedNode); // check whether node was allocated• addedNode->elem = elem;• if (head == 0) // list was empty -- set head• head = addedNode;• else• tail->next = addedNode;• tail = addedNode;• addedNode->next = 0;• }

First() method• bool List::first(ListElementType & elem)• {• // After calling first, current points to // first item in list• if (head == 0)• return false;• else {• elem = head->elem;• current = head;• return true;• }• }

Next() method• bool List::next(ListElementType & elem)• {• // current should always be nonzero• assert(current); • // After each call, current points to the item• // that next has just returned.• if (current->next == 0)• return false;• else {• current = current->next;• elem = current->elem;• return true;• }• }

Figure 7-8

7 12 5

7 12 5

head

head

current

currentelem next

elem next

The Inorder List ADT• Many applications require that lists

be maintained in some order– Address books– File names– County records– Student records– Dictionary

Inorder List requirements• Some part of the information stored

must be a designated key• For any two keys (k1, k2) there must

be a way to evaluate them, such as k1 < k2

• A ‘total order’ is any set of keys that obey an ordering rule.

The Inorder List ADT• Characteristics:• An Inorder List L stores items of some

type (ListElementType) that is totally ordered.

• The items in the List are in order; that is, if a and b are elements of ListElement Type, and a < b, then if a and b are in L, a will be before b.

Inorder List operations• Prerequisite:• ListElementType must work with the

operations <= and ==.• Operations:• void L.insert(const ListElementType &elem)• Precondition: None.• Postcondition:L = L with an instance of

elem added to the list

First() method

• bool L.first(ListElementType &elem)• Precondition: None• Postcondition: If the list is empty, none.

Otherwise, the variable elem contains the smallest item in L; the “next” item to be returned is the second in L.

• Return: true if and only if there is at least one element in L.

Next() method• bool L.next(ListElementType &elem)• Precondition: The “first” operation has

been called at least once.• Postcondition:Variable elem contains the

next item in L, in order, if there is one.• Return: true if and only if there is a next

item.

Inorder invariant

u vinvariant: u < v

Insertion (before)

7 12

9

Insertion (after)

7 12

9

Required insertion pointers

7 12

9predaddedNode

Assertions (before insertion)

7 12

9predaddedNode

pred->elem pred->next pred->next->elem

addedNode->elem

Assertion: pred->elem <= addedNode->elem && addedNode->elem <= pred->next->elem

Assertion (considering end-of-list)

• (pred->elem <= addedNode->elem) &&

• (addedNode->elem <= pred->next->elem || pred->next == 0).

Assertion(after insertion)

7 12

9pred

pred->elem pred->next pred->next->next->elem

pred->next->elem

Assertion: pred->elem <= pred->next->elem <= pred->next->next->elem

pred->next->next

Assertions for continued advancing

• 7-2 addedNode->elem > pred->next->elem

• 7-3 pred->next != 0

Insert for Inorder List ADT• // cx7-9.cpp• // cx7-8.cpp• // implementation file, linked list implementation

of List ADT• #include "cx7-5.h"• void List::insert(const ListElementType & elem)• {• // precondition: list is in order• Link addedNode(new Node);

Code Example 7-9• assert(addedNode);• addedNode->elem = elem;• // Special case: if existing list is empty, or if the new data• // is less than smallest item in the list, new node is added• // to the front of the list• if (head == 0 || elem <= head->elem) {• addedNode->next = head;• head = addedNode;• }• else {• // find the pointer to the node that is the predecessor• // to the new node in the in-order list

Code Example 7-9• Link pred(head);• // assertion: pred->elem <= addedNode->elem• while (pred->next != 0 && pred->next->elem <= addedNode-

>elem)• // invariant: pred->next != 0 && pred->next->elem <= elem• pred = pred->next;• // assertion 7-1: (pred->elem <= addedNode->elem) &&• //(addedNode->elem <= pred->next->elem || pred->next == 0)• addedNode->next = pred->next;• pred->next = addedNode;• // assertion: pred->elem <= pred->next->elem &&• // (pred->next->elem <= pred->next->next->elem || pred-

>next->next == 0)• // postcondition: list is in order, with elem added

Variations on linked lists• Dummy head nodes

– Eliminates special case surrounding first node in list

– Never insert or delete a first node• Circular linked lists• Doubly linked lists

Empty linked lists

(head == 0)

(dummy)

List comparisons

(dummy)

7 4

7 4

List classfor list with dummy node• class List {• // Use L to mean "this List"• public:• List(); • // Precondition: None• // Postcondition: L is an empty List• void insert(const ListElementType & elem); • // Precondition: None• // Postcondition: Lpost = Lpre with an instance of

elem added to Lpost

List operations (first)• bool first(ListElementType & elem);• // Precondition: None• // Postcondition: If the list empty, none.

Otherwise, variable elem contains first item in L; the "next" item to be returned is the second in L.

• // Returns: true if and only if there is at least one element in L

List operations (next, remove)

• bool next(ListElementType & elem);• // Precondition: The "first" operation has been called at

least once.• // Postcondition: elem contains next item in L, if there is

one, and next counter advances by one; if no next element,none.

• // Returns: true if and only if there was a next item.• void remove(const ListElementType & target);• // Precondition: None• // Postcondition: Lpost = Lpre with one instance of target

removed

Data members• private:• struct Node; // declaration without definition• typedef Node *Link; // use declaration of Node• struct Node { // now we define Node• ListElementType elem;• Link next;• };• Link head;• Link current;• };

Modifications (constructor)• // cx7-11.cpp• #include "cx7-10.h"

• List::List()• {• // Initialize an empty list• head = new Node;• assert(head); // What is the reason for this?• head->next = 0;• current = 0;• }

Modification to insert()• void List::insert(const ListElementType & elem)• { // precondition: list is in order• Link addedNode(new Node);• assert(addedNode);• addedNode->elem = elem;• // find pointer to predecessor in the in-order list• Link pred(head);• // loop invariant: pred>elem <= elem• while (pred->next != 0 && (pred->next->elem <=

addedNode->elem))• pred = pred->next;

Insertion (con’t)• // assertion: (pred>elem <= addedNode>elem) &&• //(addedNode->elem <= pred->next->elem • // || pred->next == 0)• addedNode->next = pred->next;• pred->next = addedNode;• // postcondition: list is in order• }

Modification of first• bool List::first(ListElementType &elem)• { // After first(), current points to first item in list• assert(head); // if no head, something is wrong! • if (head->next == 0)• return false;• else {• current = head->next;• elem = current->elem;• return true;• }• }

Modification of next()• bool List::next(ListElementType & elem)• { // With proper use, current should be nonzero• assert(current);• // After each call, current points to item returned.• if (current->next == 0)• return false; // no next element available• else {• current = current->next;• elem = current->elem;• return true;• }• }

Modification of remove()• void List::remove(const ListElementType & target)• { assert(head);• Link pred, delNode;• // pred starts out pointing at the dummy head• for (pred = head; pred->next != 0 && pred->next->elem <

target;pred = pred->next);• // at this point, check to see if we've found target --• // if so, remove it. Check to avoid dereferencing null pointer!• if (pred && (pred->next) && (pred->next->elem == target))

{ // remove the next node in the list• delNode = pred->next;• pred->next = delNode->next;• delete delNode; // return node to memory}• }

List before removal

7 12

9pred

delNode

List after removal

7 12

9pred

delNode

Circular linked lists• No ‘0’ pointer at end• Last link points to first node• If external pointer is assigned to tail

of list, it is easy to reference both the tail and the head

Circular linked list

7 12 5

Doubly linked lists• Allow traversal in either direction• Require two links for each node

– Next– Predecessor

A doubly linked list

7 12 5

head

Header file for doubly linked list

• typedef int ListElementType;• class List {• public:• List();• void insert(const ListElementType & elem); • bool first(ListElementType & elem);• bool next(ListElementType & elem);• bool previous(ListElementType & elem);

Data members• private:• struct Node; // declaration without definition• typedef Node *Link;• struct Node {• ListElementType elem;• Link next;• Link prev;• };• Link head;• Link current;• };

Insertion into DLL (front)• void List::insert(const ListElementType & elem)• { Link addedNode = new Node;• assert(addedNode);• addedNode->elem = elem;• addedNode->next = head;• if (head) // test to see if a node exists• head->prev = addedNode; // if so, it needs to

point back to the new node• addedNode->prev = 0;• head = addedNode;• }

Previous for DLL• bool List::previous(ListElementType &elem)• {• assert(current); • if (current->prev == 0)• return false;• else {• current = current->prev;• elem = current->elem;• return true;• }• }

Dynamic Linear Lists• Arrays (conventional linear lists) are

dimensioned in the program code and space allocated at compile time.

• Dynamic arrays (dynamic linear lists) have space allocated for them at run-time.

• This makes them more versatile than static linear lists and easier to code than linked lists.

Dynamic linear list class• typedef int ListElementType;• class List {• public:• List(int lSize);• void insert(const ListElementType & elem);• bool first(ListElementType & elem);• bool next(ListElementType & elem);• int size(); • private:• ListElementType * listArray;• int numberOfElements;• int currentPosition;• int listSize;• };

Constructor and size accessor

• List::List(int lSize)• {• assert(lSize > 0);• listSize = lSize;• listArray = new ListElementType[listSize];• assert(listArray); // memory was successfully allocated• numberOfElements = 0;• currentPosition = -1;• }• List::size()• {• return listSize;• }

Dynamic list client• int main()• {• int list1size, list2size;• cout << "Enter size of the first list: ";• cin >> list1size;• List list1(list1size);• cout << "Enter size of the second list: ";• cin >> list2size;• List list2(list2size);• // . . . and so on . . .• }

The use of const• If a const could possibly be passed to a

function, then the function must be able to accept it.

• Const is generally the proper way to implement accessors

• It avoid client code blowing up when it sends a const actual argument into a function with non-const formal arguments

Example of use of const• #include <string>• class ClubMember {• public:• ClubMember();• void setName(const string & fn, const string & ln);• void setAddress(const string & ad1, const string & ad2);• void setTelnum(const string & tn); • void setGradYear(const int gy);• void setClubMemberData(const string & fn, const string & ln, const string & ad1,

const string & ad2, const string & tn, const int gy);• string getFirstName() const;• string getLastName() const;• string getAddrOne() const;• string getAddrTwo() const;• string getTelnum() const;• int getGradYear() const;

Private section of class• private:• string firstName;• string lastName;• string addrOne;• string addrTwo;• string telNum;• int gradYear;• };

Const problem situation• Void printMember(const ClubMember &member)• {• cout << member.GetFirstName()

• If GetFirstName is not const the compiler will assume it may change any involved parameters and disallow this for a printMember where the parameter was const.

• string getFirstName() const; // is OK• string getFirstName(); // is dangerous

Operator overloading• This is important when using class

objects (derived types)• Standard operators are designed

only to work with native types.• If you invent a class and intend to

use operators, you must overload them for your class objects.

<= operator overloading• // We'll use the names lhs -- short for left hand side -- as the• // name for the argument on the left of an operator, • // and rhs -- right hand side -- for the argument on the right.

• int operator<= (const ClubMember & lhs, const ClubMember & rhs)

• {• if (lhs.getLastName() == rhs.getLastName())• return lhs.getFirstName() <= rhs.getFirstName();• else• return lhs.getLastName() <= rhs.getLastName();• }

Chapter Summary• A List ADT represents items that can be retrieved in

some order.• Linear lists implement the List ADT using an array.• Iterator functions can be used to retrieve items in a

List.• Linked list provide greater flexibility by breaking

the connection between the logical idea of a list and its implementations.

• Dynamic memory allocation allows a program to allocate memory at runtime.

Chapter Summary• The Inorder List ADT maintains items in a specified

order.• Dummy head-nodes, circular linked lists, and

doubly-linked lists provide alternative approaches to the implementation of a linked list.

• Client applications may need to implement particular functions for classes stored within another class.

• Operator overloading provides a way to make built-in operators meaningful for user-defined classes.

top related