Title: Chapter 7: The List ADT
1Chapter 7 The List ADT
2 - Chapter 7
- Lists
- Overview
- The List ADT and its uses dynamic memory
allocation programming with linked lists.
3Objectives
- 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.
4The List ADT
- Characteristics
- A List L stores items of some type, called
ListElementType. - Operations
- void L.insert(ListElementType elem)
- Precondition None.
- PostconditionLpost Lpre with an instance of
elem added to Lpost.
5List ADT, first
- bool L.first(ListElementType elem)
- Precondition None
- PostconditionIf 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.
6List ADT, next
- bool L.next(ListElementType elem)
- Precondition The first operation has been
called at least once. - PostconditionVariable 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.
7A useful exercise
- Define some additional operations that might be
useful for a List ADT.
8List 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
9Implementing lists
- A header file for the list ADT
- cx7-1.h (on authors web page)
- See next slide
- Must include List ADT
- characteristics
- operations
10Code 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
11List()
- Is the list copy constructor
- With no parameters or body it is a default
constructor
12void 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
13Lists 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
14Header 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
15Code Example 7-2
- class List
- public
- List()
- void insert(const ListElementType elem)
- bool first(ListElementType elem)
- bool next(ListElementType elem)
- private
- ListElementType listArraymaxListSize
- int numberOfElements
- int currentPosition
-
16Array List Constructor
- // cx7-3.cpp
- include "cx7-2.h"
- ListList()
-
- // initialize to an empty list
- numberOfElements 0
- currentPosition -1
-
17Insertion into linear list
- void Listinsert(const ListElementType elem)
-
- assert(numberOfElements lt maxListSize)
- listArraynumberOfElements elem
- numberOfElements
18Iterator function first
- bool Listfirst(ListElementType elem)
-
- if (numberOfElements 0)
- return false
- else
- currentPosition 0
- elem listArraycurrentPosition
- return true
-
19Iterator function next
- bool Listnext(ListElementType elem)
-
- // currentPosition should always be
- // greater than or equal to zero
- assert(currentPosition gt 0)
- if (currentPosition gt numberOfElements - 1)
- return false
- else
- currentPosition
- elem listArraycurrentPosition
- return true
20Simple 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 ltlt "Enter items to add to list, or 0 to
stop " - cin gtgt i
- while (i ! 0)
- l.insert(i)
- cin gtgt i
21Client main continued
- cout ltlt "Here are the items in the list.\n"
- ListElementType elem
- bool notEmpty(l.first(elem))
- while (notEmpty)
- cout ltlt elem ltlt endl
- notEmpty l.next(elem)
-
- return 0
-
22Problems 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.
23Figure 7-1
24Linked 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
25Figure 7-2
26Figure 7-3
27Adding 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
28Figure 7-4
29Figure 7-5
30Adding to end of list
- A five step process
- Create new node
- Copy data into it
- Assign new node ptr to tail-gtlink
- Assign 0 to new node link (not NULL)
- Assign new node ptr to tail
31Figure 7-6
32Figure 7-7
33Algorithm 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 -
34Linked 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
35First()
- 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.
36List class, public (cont)
- 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.
377.5 (cont) 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
-
38Linked list constructor
- ListList()
-
- // Initialize an empty list
- head 0
- tail 0
- current 0
39Insert for linked list
- void Listinsert(const ListElementType elem)
-
- Link addedNode(new Node)
- assert(addedNode) // check whether node was
allocated - addedNode-gtelem elem
- if (head 0) // list was empty -- set head
- head addedNode
- else
- tail-gtnext addedNode
- tail addedNode
- addedNode-gtnext 0
40An 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 ltlt zero pointer\n
- else
- cout ltlt non-zero pointer\n
- return 0
-
41Dynamic 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)
42Linked list implementation
- ListList()
-
- // Initialize an empty List
- head 0
- tail 0
- current 0
43Code Example 7-8
- void Listinsert(const ListElementType elem)
-
- Link addedNode new Node
- assert(addedNode) // check whether node was
allocated - addedNode-gtelem elem
- if (head 0) // list was empty -- set head
- head addedNode
- else
- tail-gtnext addedNode
- tail addedNode
- addedNode-gtnext 0
44First() method
- bool Listfirst(ListElementType elem)
-
- // After calling first, current points to //
first item in list - if (head 0)
- return false
- else
- elem head-gtelem
- current head
- return true
-
45Next() method
- bool Listnext(ListElementType elem)
-
- // current should always be nonzero
- assert(current)
- // After each call, current points to the item
- // that next has just returned.
- if (current-gtnext 0)
- return false
- else
- current current-gtnext
- elem current-gtelem
- return true
-
46Figure 7-8
47The Inorder List ADT
- Many applications require that lists be
maintained in some order - Address books
- File names
- County records
- Student records
- Dictionary
48Inorder 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 lt k2 - A total order is any set of keys that obey an
ordering rule.
49The 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 lt
b, then if a and b are in L, a will be before b.
50Inorder List operations
- Prerequisite
- ListElementType must work with the operations lt
and . - Operations
- void L.insert(const ListElementType elem)
- Precondition None.
- PostconditionL L with an instance of elem
added to the list
51First() 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.
52Next() method
- bool L.next(ListElementType elem)
- Precondition The first operation has been
called at least once. - PostconditionVariable elem contains the next
item in L, in order, if there is one. - Return true if and only if there is a next item.
53Inorder invariant
54Insertion (before)
55Insertion (after)
56Required insertion pointers
57Assertions (before insertion)
58Assertion (considering end-of-list)
- (pred-gtelem lt addedNode-gtelem)
- (addedNode-gtelem lt pred-gtnext-gtelem
pred-gtnext 0).
59Assertion(after insertion)
60Assertions for continued advancing
- 7-2 addedNode-gtelem gt pred-gtnext-gtelem
- 7-3 pred-gtnext ! 0
61Insert for Inorder List ADT
- // cx7-9.cpp
- // cx7-8.cpp
- // implementation file, linked list
implementation of List ADT - include "cx7-5.h"
- void Listinsert(const ListElementType elem)
-
- // precondition list is in order
- Link addedNode(new Node)
62Code Example 7-9
- assert(addedNode)
- addedNode-gtelem 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 lt head-gtelem)
- addedNode-gtnext head
- head addedNode
-
- else
- // find the pointer to the node that is the
predecessor - // to the new node in the in-order list
63Code Example 7-9
- Link pred(head)
- // assertion pred-gtelem lt addedNode-gtelem
- while (pred-gtnext ! 0 pred-gtnext-gtelem
lt addedNode-gtelem) - // invariant pred-gtnext ! 0 pred-gtnext-gtelem
lt elem - pred pred-gtnext
- // assertion 7-1 (pred-gtelem lt addedNode-gtelem)
- //(addedNode-gtelem lt pred-gtnext-gtelem
pred-gtnext 0) - addedNode-gtnext pred-gtnext
- pred-gtnext addedNode
- // assertion pred-gtelem lt pred-gtnext-gtelem
- // (pred-gtnext-gtelem lt pred-gtnext-gtnext-gtelem
pred-gtnext-gtnext 0) - // postcondition list is in order, with elem
added
64Variations 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
65Empty linked lists
66List comparisons
67List 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
68List 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
69List 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
70Data 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
-
71Modifications (constructor)
- // cx7-11.cpp
- include "cx7-10.h"
- ListList()
-
- // Initialize an empty list
- head new Node
- assert(head) // What is the reason for this?
- head-gtnext 0
- current 0
-
72Modification to insert()
- void Listinsert(const ListElementType elem)
- // precondition list is in order
- Link addedNode(new Node)
- assert(addedNode)
- addedNode-gtelem elem
- // find pointer to predecessor in the in-order
list - Link pred(head)
- // loop invariant predgtelem lt elem
- while (pred-gtnext ! 0 (pred-gtnext-gtelem lt
addedNode-gtelem)) - pred pred-gtnext
73Insertion (cont)
- // assertion (predgtelem lt addedNodegtelem)
- //(addedNode-gtelem lt pred-gtnext-gtelem
- // pred-gtnext 0)
- addedNode-gtnext pred-gtnext
- pred-gtnext addedNode
- // postcondition list is in order
-
74Modification of first
- bool Listfirst(ListElementType elem)
- // After first(), current points to first item
in list - assert(head) // if no head, something is
wrong! - if (head-gtnext 0)
- return false
- else
- current head-gtnext
- elem current-gtelem
- return true
-
75Modification of next()
- bool Listnext(ListElementType elem)
- // With proper use, current should be nonzero
- assert(current)
- // After each call, current points to item
returned. - if (current-gtnext 0)
- return false // no next element available
- else
- current current-gtnext
- elem current-gtelem
- return true
-
76Modification of remove()
- void Listremove(const ListElementType target)
- assert(head)
- Link pred, delNode
- // pred starts out pointing at the dummy head
- for (pred head pred-gtnext ! 0
pred-gtnext-gtelem lt targetpred pred-gtnext) - // at this point, check to see if we've found
target -- - // if so, remove it. Check to avoid
dereferencing null pointer! - if (pred (pred-gtnext) (pred-gtnext-gtelem
target)) //
remove the next node in the list - delNode pred-gtnext
- pred-gtnext delNode-gtnext
- delete delNode // return node to memory
77List before removal
78List after removal
79Circular 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
80Circular linked list
81Doubly linked lists
- Allow traversal in either direction
- Require two links for each node
- Next
- Predecessor
82A doubly linked list
83Header 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)
84Data members
- private
- struct Node // declaration without definition
- typedef Node Link
- struct Node
- ListElementType elem
- Link next
- Link prev
-
- Link head
- Link current
-
85Insertion into DLL (front)
- void Listinsert(const ListElementType elem)
- Link addedNode new Node
- assert(addedNode)
- addedNode-gtelem elem
- addedNode-gtnext head
- if (head) // test to see if a node exists
- head-gtprev addedNode // if so, it
needs to point back to the new node - addedNode-gtprev 0
- head addedNode
86Previous for DLL
- bool Listprevious(ListElementType elem)
-
- assert(current)
- if (current-gtprev 0)
- return false
- else
- current current-gtprev
- elem current-gtelem
- return true
-
-
87Dynamic 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.
88Dynamic 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
-
89Constructor and size accessor
- ListList(int lSize)
-
- assert(lSize gt 0)
- listSize lSize
- listArray new ListElementTypelistSize
- assert(listArray) // memory was successfully
allocated - numberOfElements 0
- currentPosition -1
-
- Listsize()
-
- return listSize
90Dynamic list client
- int main()
-
- int list1size, list2size
- cout ltlt "Enter size of the first list "
- cin gtgt list1size
- List list1(list1size)
- cout ltlt "Enter size of the second list "
- cin gtgt list2size
- List list2(list2size)
- // . . . and so on . . .
91The 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
92Example of use of const
- include ltstringgt
- 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
93Private section of class
- private
- string firstName
- string lastName
- string addrOne
- string addrTwo
- string telNum
- int gradYear
-
94Const problem situation
- Void printMember(const ClubMember member)
-
- cout ltlt 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
95Operator 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.
96lt 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 operatorlt (const ClubMember lhs,
const ClubMember rhs) -
- if (lhs.getLastName() rhs.getLastName())
- return lhs.getFirstName() lt
rhs.getFirstName() - else
- return lhs.getLastName() lt
rhs.getLastName()
97Chapter 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.
98Chapter 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.