Tải bản đầy đủ (.pdf) (40 trang)

Absolute C++ (phần 19) doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (452.82 KB, 40 trang )

720 Linked Data Structures
Display 17.15 Implementation of the Stack Template Class
(part 2 of 2)
20 template<class T>
21 Stack<T>& Stack<T>::operator =(const Stack<T>& rightSide)
22


<
The definition of the overloaded assignment operator is Self-Test Exercise 13.
>
23 template<class T>
24 Stack<T>::~Stack(
)
25 {
26 T next;
27 while (! isEmpty(
))
28 next = pop(
);//pop calls delete.
29 }
30
31 //Uses cstddef:
32 template<class T>
33 bool Stack<T>::isEmpty(
) const
34 {
35 return (top == NULL);
36 }
37 template<class T>
38 void Stack<T>::push(T stackFrame)


39


<
The rest of the definition is Self-Test Exercise 11.
>
40 //Uses cstdlib and iostream:
41 template<class T>
42 T Stack<T>::pop(
)
43 {
44 if (isEmpty(
))
45 {
46 cout << "Error: popping an empty stack.\n";
47 exit(1);
48 }
49 T result = top->getData( );
50 Node<T> *discard;
51 discard = top;
52 top = top->getLink( );
53 delete discard;
54 return result;
55 }
56 }//StackSavitch
17_CH17.fm Page 720 Tuesday, August 19, 2003 10:22 AM
Linked List Applications 721
Self-Test Exercises
Writing the definition of the member function push is Self-Test Exercise 11. However, we have
already given the algorithm for this task. The code for the

push member function is essentially the
same as the function
headInsert shown in Display 17.11, except that in the member function
push we use a pointer named top in place of a pointer named head.
An empty stack is just an empty linked list, so an empty stack is implemented by setting the
pointer top equal to NULL. Once you realize that NULL represents the empty stack, the imple-
mentations of the default constructor and of the member function
empty are obvious.
The definition of the copy constructor is a bit more complicated but does not use any techniques
we have not already discussed. The details are left to Self-Test Exercise 12.
The
pop member function first checks to see if the stack is empty. If the stack is not empty, it pro-
ceeds to remove the top character in the stack. It sets the local variable
result equal to the top
symbol on the stack as follows:
T result = top->getData( );
After the data in the top node is saved in the variable result, the pointer top is moved to the
next node in the linked list, effectively removing the top node from the list. The pointer
top is
moved with the statement
top = top->getLink( );
However, before the pointer top is moved, a temporary pointer, called discard, is positioned so
that it points to the node that is about to be removed from the list. The storage for the removed
node can then be recycled with the following call to
delete:
delete discard;
Each node that is removed from the linked list by the member function pop has its memory recy-
cled with a call to
delete, so all that the destructor needs to do is remove each item from the
stack with a call to

pop. Each node will then have its memory returned to the freestore for
recycling.
11. Give the definition of the member function push of the template class Stack described in
Displays 17.13 and 17.15.
12. Give the definition of the copy constructor for the template class
Stack described in Dis-
plays 17.13 and 17.15.
13. Give the definition of the overloaded assignment operator for the template class
Stack
described in Displays 17.13 and 17.15.
empty stack
destructor
17_CH17.fm Page 721 Tuesday, August 19, 2003 10:22 AM
722 Linked Data Structures
Example
A Q
UEUE
T
EMPLATE
C
LASS
A stack is a last-in/first-out data structure. Another common data structure is a qq
qq
uu
uu
ee
ee
uu
uu
ee

ee
, which
handles data in a
first-in/first-out
fashion. A queue can be implemented with a linked list in a
manner similar to our implementation of the
Stack template class. However, a queue needs a
pointer at both the head of the list and at the end of the linked list, since action takes place in
both locations. It is easier to remove a node from the head of a linked list than from the other end
of the linked list. Therefore, our implementation will remove nodes from the head of the list
(which we will now call the ff
ff
rr
rr
oo
oo
nn
nn
tt
tt
of the list) and will add nodes to the other end of the list, which
we will now call the bb
bb
aa
aa
cc
cc
kk
kk
of the list (or the back of the queue).

The definition of the
Queue template class is given in Display 17.16. A sample application that uses
the class
Queue is shown in Display 17.17. The definitions of the member functions are left as Self-
Test Exercises (but remember that the answers are given at the end of the chapter should you have
any problems filling in the details).
Q
UEUE
A queue is a first-in/first-out data structure; that is, the data items are removed from the queue in
the same order that they were added to the queue.
queue
front
back
Display 17.16 Interface File for a Queue Template Class
(part 1 of 2)
1
2 //This is the header file queue.h. This is the interface for the class
3 //Queue, which is a template class for a queue of items of type T.
4 #ifndef QUEUE_H
5 #define QUEUE_H
6 namespace QueueSavitch
7 {
8 template<class T>
9 class Node
10 {
11 public:
12 Node(T theData, Node<T>* theLink) : data(theData), link(theLink){}
13 Node<T>* getLink( ) const { return link; }
14 const T getData( ) const { return data; }
15 void setData(const T& theData) { data = theData; }

16 void setLink(Node<T>* pointer) { link = pointer; }
17 private:
18 T data;
This is the same definition of the template class Node
that we gave for the stack interface in Display 17.13. See
the tip “A Comment on Namespaces” for a discussion of
this duplication.
17_CH17.fm Page 722 Tuesday, August 19, 2003 10:22 AM
Linked List Applications 723
Display 17.16 Interface File for a Queue Template Class
(part 2 of 2)
19 Node<T> *link;
20 };
21 template<class T>
22 class Queue
23 {
24 public:
25 Queue( );
26 //Initializes the object to an empty queue.
27 Queue(const Queue<T>& aQueue);
28 Queue<T>& operator =(const Queue<T>& rightSide);
29 virtual ~Queue( );
30
31 void add(T item);
32 //Postcondition: item has been added to the back of the queue.
33 T remove( );
34 //Precondition: The queue is not empty.
35 //Returns the item at the front of the queue
36 //and removes that item from the queue.
37 bool isEmpty( ) const;

38 //Returns true if the queue is empty. Returns false otherwise.
39 private:
40 Node<T> *front;//Points to the head of a linked list.
41 //Items are removed at the head
42 Node<T> *back;//Points to the node at the other end of the linked list.
43 //Items are added at this end.
44 };
45 }//QueueSavitch
46 #endif //QUEUE_H
Copy constructor
The destructor destroys the
queue and returns all the
memory to the freestore.
You might prefer to replace the
parameter type
T with const T&.
17_CH17.fm Page 723 Tuesday, August 19, 2003 10:22 AM
724 Linked Data Structures
Display 17.17 Program Using the Queue Template Class
(part 1 of 2)
1 //Program to demonstrate use of the Queue template class.
2 #include <iostream>
3 #include "queue.h"
4 #include "queue.cpp"
5 using std::cin;
6 using std::cout;
7 using std::endl;
8 using QueueSavitch::Queue;
9 int main(
)

10 {
11 char next, ans;
12 do
13 {
14 Queue<char> q;
15 cout << "Enter a line of text:\n";
16 cin.get(next);
17 while (next != ’\n’)
18 {
19 q.add(next);
20 cin.get(next);
21 }
22 cout << "You entered:\n";
23 while ( ! q.isEmpty(
) )
24 cout << q.remove(
);
25 cout << endl;
26 cout << "Again?(y/n): ";
27 cin >> ans;
28 cin.ignore(10000, ’\n’);
29 }while (ans != ’n’ && ans != ’N’);
30 return 0;
31 }
Contrast this with the similar program using a stack
instead of a queue that we gave in Display 17.14.
17_CH17.fm Page 724 Tuesday, August 19, 2003 10:22 AM
Linked List Applications 725
Tip
A C

OMMENT

ON
N
AMESPACES

Notice that both of the namespaces StackSavitch (Display 17.13) and QueueSavitch (Display
17.16) define a template class called
Node. As it turns out, the two definitions of Node are the
same, but the point discussed here is the same whether the two definitions are the same or differ-
ent. C++ does not allow you to define the same identifier twice, even if the two definitions are the
same, unless the two names are somehow distinguished. In this case, the two definitions are
allowed because they are in two different namespaces. It is even legal to use both the
Stack tem-
plate class and the
Queue template class in the same program. However, you should use
using StackSavitch::Stack;
using QueueSavitch::Queue;
rather than
using namespace StackSavitch;
using namespace QueueSavitch;
Most compilers will allow either set of using directives if you do not use the identifier Node, but
the second set of
using directives provides two definitions of the identifier Node and therefore
should be avoided.
It would be fine to also use either, but not both, of the following:
using StackSavitch::Node;
or
using QueueSavitch::Node;
Display 17.17 Program Using the Queue Template Class

(part 2 of 2)
S
AMPLE
D
IALOGUE
Enter a line of text:
straw
You entered:
straw
Again?(y/n): y
Enter a line of text:
I love C++
You entered:
I love C++
Again?(y/n): n
17_CH17.fm Page 725 Tuesday, August 19, 2003 10:22 AM
726 Linked Data Structures
Self-Test Exercises
14. Give the definitions for the default (zero-argument) constructor and the member functions
Queue<T>::isEmpty for the template class Queue (Display 17.16).
15. Give the definitions for the member functions
Queue<T>::add and Queue<T>::remove
for the template class
Queue (Display 17.16).
16. Give the definition for the destructor for the template class
Queue (Display 17.16).
17. Give the definition for the copy constructor for the template class
Queue (Display 17.16).
18. Give the definition for the overloaded assignment operator for the template class
Queue

(Display 17.16).

FRIEND CLASSES AND SIMILAR ALTERNATIVES
You may have found it a nuisance to use the accessor and mutator functions getLink
and setLink in the template class Node (see Display 17.13 or Display 17.16). You
might be tempted to avoid the invocations of
getLink and setLink by simply making
the member variable
link of the class Node public instead of private. Before you aban-
don the principle of making all member variables private, note two things. First, using
getLink and setLink is not really any harder for you the programmer than directly
accessing the links in the nodes. (However,
getLink and setLink do introduce some
overhead and so may slightly reduce efficiency.) Second, there is a way to avoid using
getLink and setLink and instead directly access the links of nodes without making the
link member variable public. Let’s explore this second possibility.
Chapter 8 discussed friend functions. As you will recall, if
f is a friend function of a
class
C, then f is not a member function of C; however, when you write the definition of
the function
f, you can access private members of C just as you can in the definitions of
member functions of
C. A class can be a friend of another class in the same way that a
function can be a friend of a class. If the class
F is a friend of the class C, then every
member function of the class
F is a friend of the class C. Thus, if, for example, the Queue
template class were a friend of the Node template class, then the private link member
variables would be directly available in the definitions of the member functions of

Queue. The details are outlined in Display 17.18.
When one class is a friend of another class, it is typical for the classes to reference
each other in their class definitions. This requires that you include a
forward declaration
to the class or class template defined second, as illustrated in Display 17.18. Note that
the forward declaration is just the heading of the class or class template definition fol-
lowed by a semicolon. A complete example using a friend class is given in Section 17.4
(see “A Tree Template Class”).
friend class
forward
declaration
17_CH17.fm Page 726 Tuesday, August 19, 2003 10:22 AM
Linked List Applications 727
Display 17.18 A Queue Template Class as a Friend of the Node Class
(part 1 of 2)
1
2 //This is the header file queue.h. This is the interface for the class
3 //Queue, which is a template class for a queue of items of type T.
4 #ifndef QUEUE_H
5 #define QUEUE_H
6 namespace QueueSavitch
7 {
8 template<class T>
9 class Queue;
10
11 template<class T>
12 class Node
13 {
14 public:
15 Node(T theData, Node<T>* theLink) : data(theData), link(theLink){}

16 friend class Queue<T>;
17 private:
18 T data;
19 Node<T> *link;
20 };
21 template<class T>
22 class Queue
23 {
24
<
The definition of the template class Queue is identical to the one given in Display 17.16. However, the
25
definitions of the member functions will be different from the ones we gave (in the Self-Test Exercises)
26
for the nonfriend version of Queue.
>
27 }//QueueSavitch
28 #endif //QUEUE_H
29 #include <iostream>
30 #include <cstdlib>
31 #include <cstddef>
32 #include "queue.h"
33 using std::cout;
34 namespace QueueSavitch
35 {
36 template<class T> //Uses cstddef:
37 void Queue<T>::add(T item)
38 {
39 if (isEmpty( ))
A forward declaration. Do not forget the semicolon.

This is an alternate approach to that given in Display 17.16.
In this version the Queue template class is a friend of the
Node template class.
If
Node<T> is only used in the definition of the friend
class
Queue<T>, there is no need for mutator or accessor
functions.
The implementation file would contain these definitions
and the definitions of the other member functions
similarly modified to allow access by name to the
link
and
data member variables of the nodes.
17_CH17.fm Page 727 Tuesday, August 19, 2003 10:22 AM
728 Linked Data Structures
Two approaches that serve pretty much the same purpose as friend classes and which
can be used in pretty much the same way with classes and template classes such as
Node
and Queue are (1) using protected or private inheritance to derive Queue from Node, and
(2) giving the definition of
Node within the definition of Queue, so that Node is a local
class (template) definition. (Protected inheritance is discussed in Chapter 14, and
classes defined locally within a class are discussed in Chapter 7.)
Display 17.18 A Queue Template Class as a Friend of the Node Class
(part 2 of 2)
40 front = back = new Node<T>(item, NULL);
41 else
42 {
43 back->link = new Node<T>(item, NULL);

44 back = back->link;
45 }
46 }
47 template<class T> //Uses cstdlib and iostream:
48 T Queue<T>::remove( )
49 {
50 if (isEmpty( ))
51 {
52 cout << "Error: Removing an item from an empty queue.\n";
53 exit(1);
54 }
55 T result = front->data;
56 Node<T> *discard;
57 discard = front;
58 front = front->link;
59 if (front == NULL) //if you removed the last node
60 back = NULL;
61 delete discard;
62 return result;
63 }
64 }//QueueSavitch
If efficiency is a major issue, you might want to use
(front == NULL) instead of (isEmpty( ).
Contrast these implementations with the ones given as the
answer to Self-Test Exercise 15.
17_CH17.fm Page 728 Tuesday, August 19, 2003 10:22 AM
Iterators 729
Iterators
The white rabbit put on his spectacles. “Where shall I begin,
please your Majesty?” he asked.

“Begin at the beginning,” the King said, very gravely, “And go
on till you come to the end: then stop.”
Lewis Carroll, Alice in Wonderland
An important notion in data structures is that of an iterator. An iterator is a construct
(typically an object of some iterator class) that allows you to cycle through the data
items stored in a data structure so that you can perform whatever action you want on
each data item.

POINTERS AS ITERATORS
The basic ideas, and in fact the prototypical model, for iterators can easily be seen in
the context of linked lists. A linked list is one of the prototypical data structures, and a
pointer is a prototypical example of an iterator. You can use a pointer as an iterator by
moving through the linked list one node at a time starting at the head of the list and
cycling through all the nodes in the list. The general outline is as follows:
Node_Type
*iterator;
for (iterator =
Head
; iterator != NULL; iterator = iterator->
Link
)

Do whatever you want with the node pointed to by
iterator;
where
Head
is a pointer to the head node of the linked list and
Link
is the name of the
member variable of a node that points to the next node in the list.

For example, to output the data in all the nodes in a linked list of the kind we dis-
cussed in Section 17.1, you could use the following:
IntNode *iterator;
for(iterator = head; iterator != NULL; iterator = iterator->getLink( ))
cout << (iterator->getData( ));
I
TERATOR
An iterator is a construct (typically an object of some iterator class) that allows you to cycle
through the data items stored in a data structure so that you can perform whatever action you
want on each data item in the data structure.
17.3
iterator
17_CH17.fm Page 729 Tuesday, August 19, 2003 10:22 AM
730 Linked Data Structures
The definition of IntNode is given in Display 17.4.
Note that you test to see if two pointers are pointing to the same node by comparing
them with the equal operator,
==. A pointer is a memory address. If two pointer vari-
ables contain the same memory address, then they compare as equal and they point to
the same node. Similarly, you can use
!= to compare two pointers to see if they do not
point to the same node.

ITERATOR CLASSES
An iterator class is a more versatile and more general notion than a pointer. It very
often does have a pointer member variable as the heart of its data, as in the next pro-
gramming example, but that is not required. For example, the heart of the iterator
might be an array index. An iterator class has functions and overloaded operators that
allow you to use pointer syntax with objects of the iterator class no matter what you use
for the underlying data structure, node type, or basic location marker (pointer or array

index or whatever). Moreover, it provides a general framework that can be used across a
wide range of data structures.
An iterator class typically has the following overloaded operators:
++ Overloaded increment operator, which advances the iterator to the next item.
Overloaded decrement operator, which moves the iterator to the previous item.
== Overloaded equality operator to compare two iterators and return true if they
both point to the same item.
!= Overloaded not-equal operator to compare two iterators and return true if they
do not point to the same item.
* Overloaded dereferencing operator that gives access to one item. (Often it
returns a reference to allow both read and write access.)
When thinking of this list of operators you can use a linked list as a concrete exam-
ple. In that case, remember that the items in the list are the data in the list, not the
entire nodes and not the pointer members of the nodes. Everything but the data items
is implementation detail that is meant to be hidden from the programmer who uses the
iterator and data structure classes.
An iterator is used in conjunction with some particular structure class that stores
data items of some type. The data structure class normally has the following member
functions that provide iterators for objects of that class:
begin( ): A member function that takes no argument and returns an iterator that is
located at (“points to”) the first item in the data structure.
end( ): A member function that takes no argument and returns an iterator that can
be used to test for having cycled through all items in the data structure. If
i is an
iterator and
i has been advanced beyond the last item in the data structure, then i
should equal
end( ).
iterator class
17_CH17.fm Page 730 Tuesday, August 19, 2003 10:22 AM

Iterators 731
Example
Using an iterator, you can cycle through the items in a data structure ds as follows:
for (i = ds.begin( ); i != ds.end( ); i++)

process *i //*i is the current data item.
where i is an iterator. Chapter 19 discusses iterators with a few more items and refine-
ments than these, but these will do for an introduction.
This abstract discussion will not come alive until we give an example. So, let’s walk
through an example.
A
N
I
TERATOR
C
LASS
Display 17.19 contains the definition of an iterator class that can be used for data structures, such
as a stack or queue, that are based on a linked list. We have placed the node class and the iterator
class into a namespace of their own. This makes sense, since the iterator is intimately related to
the node class and since any class that uses this node class can also use the iterator class. This
iterator class does not have a decrement operator, because a definition of a decrement operator
depends on the details of the linked list and does not depend solely on the type
Node<T>. (There
is nothing wrong with having the definition of the iterator depend on the underlying linked list.
We have just decided to avoid this complication.)
As you can see, the template class
ListIterator is essentially a pointer wrapped in a class so
that it can have the needed member operators. The definitions of the overload operators are
straightforward and in fact so short that we have defined all of them as inline functions. Note that
I

TERATOR
C
LASS
An iterator class typically has the following overloaded operators: ++, move to next item; ,
move to previous item;
==, overloaded equality; !=, overloaded not-equal operator; and *, over-
loaded dereferencing operator that gives access to one data item.
The data structure corresponding to an iterator class typically has the following two member
functions:
begin( ), which returns an iterator that is located at (“points to”) the first item in the
data structure; and
end( ), which returns an iterator that can be used to test for having cycled
through all items in the data structure. If
i is an iterator and i has been advanced
beyond
the last
item in the data structure, then
i should equal end( ).
Using an iterator, you can cycle through the items in a data structure
ds as follows:
for (i = ds.begin( ); i != ds.end( ); i++)

process *i //*i is the current data item.
17_CH17.fm Page 731 Tuesday, August 19, 2003 10:22 AM
732 Linked Data Structures
Display 17.19 An Iterator Class for Linked Lists
(part 1 of 2)
1 //This is the header file iterator.h. This is the interface for the class
2 //ListIterator, which is a template class for an iterator to use with linked
3 //lists of items of type T. This file also contains the node type for a

4 //linked list.
5 #ifndef ITERATOR_H
6 #define ITERATOR_H
7 namespace ListNodeSavitch
8 {
9 template<class T>
10 class Node
11 {
12 public:
13 Node(T theData, Node<T>* theLink) : data(theData), link(theLink){}
14 Node<T>* getLink( ) const { return link; }
15 const T getData( ) const { return data; }
16 void setData(const T& theData) { data = theData; }
17 void setLink(Node<T>* pointer) { link = pointer; }
18 private:
19 T data;
20 Node<T> *link;
21 };
22
23 template<class T>
24 class ListIterator
25 {
26 public:
27 ListIterator( ) : current(NULL) {}
28 ListIterator(Node<T>* initial) : current(initial) {}
29 const T operator *( ) const { return current->getData( ); }
30 //Precondition: Not equal to the default constructor object;
31 //that is, current != NULL.

32 ListIterator operator ++( ) //Prefix form

33 {
34 current = current->getLink( );
35 return *this;
36 }
37 ListIterator operator ++(int) //Postfix form
38 {
39 ListIterator startVersion(current);
40 current = current->getLink( );
Note that the
dereferencing operator
* produces the data
member of the node,
not the entire node. This
version does not allow
you to change the data
in the node.
17_CH17.fm Page 732 Tuesday, August 19, 2003 10:22 AM
Iterators 733
the dereferencing operator, *, produces the data member variable of the node pointed to. Only
the data member variable is data. The pointer member variable in a node is part of the implemen-
tation detail that the user programmer should not need to be concerned with.
You can use the
ListIterator class as an iterator for any class based on a linked list that uses
the template class
Node. As an example, we have rewritten the template class Queue so that it has
iterator facilities. The interface for the template class
Queue is given in Display 17.20. This defini-
tion of the
Queue template is the same as our previous version (Display 17.16) except that we have
added a type definition as well as the following two member functions:

Iterator begin( ) const { return Iterator(front); }
Iterator end( ) const { return Iterator( ); }
//The end iterator has end( ).current == NULL.
Let’s discuss the member functions first.
The member function
begin( ) returns an iterator located at (“pointing to”) the front node of
the queue, which is the head node of the underlying linked list. Each application of the increment
operator, ++, moves the iterator to the next node. Thus, you can move through the nodes, and
hence the data, in a queue named
q as follows:
for (i = q.begin( );
Stopping_Condition
; i++)

process *i //*i is the current data item.
Display 17.19 An Iterator Class for Linked Lists
(part 2 of 2)
41 return startVersion;
42 }
43 bool operator ==(const ListIterator& rightSide) const
44 { return (current == rightSide.current); }

45 bool operator !=(const ListIterator& rightSide) const
46 { return (current != rightSide.current); }

47 //The default assignment operator and copy constructor
48 //should work correctly for ListIterator.
49 private:
50 Node<T> *current;
51 };

52 }//ListNodeSavitch
53 #endif //ITERATOR_H
17_CH17.fm Page 733 Tuesday, August 19, 2003 10:22 AM
734 Linked Data Structures
Display 17.20 Interface File for a Queue with Iterators Template Class
1 //This is the header file queue.h. This is the interface for the class
2 //Queue, which is a template class for a queue of items of type T, including
3 //iterators.
4 #ifndef QUEUE_H
5 #define QUEUE_H
6 #include "iterator.h"
7 using namespace ListNodeSavitch;
8 namespace QueueSavitch
9 {
10 template<class T>
11 class Queue
12 {
13 public:
14 typedef ListIterator<T> Iterator;
15 Queue( );
16 Queue(const Queue<T>& aQueue);
17 Queue<T>& operator =(const Queue<T>& rightSide);
18 virtual ~Queue( );
19 void add(T item);
20 T remove( );
21 bool isEmpty( ) const;
22 Iterator begin( ) const { return Iterator(front); }
23 Iterator end( ) const { return Iterator( ); }
24 //The end iterator has end( ).current == NULL.
25 //Note that you cannot dereference the end iterator.

26 private:
27 Node<T> *front;//Points to the head of a linked list.
28 //Items are removed at the head
29 Node<T> *back;//Points to the node at the other end of the linked
30 //list.
31 //Items are added at this end.
32 };
33 }//QueueSavitch
34 #endif //QUEUE_H
The definitions of Node<T> and
ListIterator<T> are in the namespace
ListNodeSavitch in the file iterator.h.
17_CH17.fm Page 734 Tuesday, August 19, 2003 10:22 AM
Iterators 735
where i is a variable of the iterator type.
The member function
end( ) returns an iterator whose current member variable is NULL. Thus,
when the iterator
i has passed the last node, the Boolean expression
i != q.end( )
changes from true to false. This is the desired
Stopping_Condition
. This queue class and itera-
tor class allow you to cycle through the data in the queue in the way we outlined for an iterator:
for (i = q.begin( ); i != q.end( ); i++)

process *i //*i is the current data item.
Note that i is not equal to q.end( ) when i is at the last node. The iterator i is not equal to
q.end( ) until i has been advanced one position past the last node. To remember this detail,
think of

q.end( ) as being an end marker like NULL; in this case it is essentially a version of
NULL. A sample program that uses such a for loop is shown in Display 17.21.
Notice the type definition in our new queue template class:
typedef ListIterator<T> Iterator;
This typedef is not absolutely necessary. You can always use ListIterator<T> instead of the
type name
Iterator. However, this type definition does make for cleaner code. With this type
definition, an iterator for the class
Queue<char> is written
Queue<char>::Iterator i;
This makes it clear with which class the iterator is meant to be used.
The implementation of our new template class
Queue is given in Display 17.22. Since the only
member functions we added to this new
Queue class are defined inline, the implementation file
contains nothing really new, but we include the implementation file to show how it is laid out and
to show which directives it would include.
typedef
17_CH17.fm Page 735 Tuesday, August 19, 2003 10:22 AM
736 Linked Data Structures
Display 17.21 Program Using the Queue Template Class with Iterators
(part 1 of 2)
1 //Program to demonstrate use of the Queue template class with iterators.
2 #include <iostream>
3 #include "queue.h"//not needed
4 #include "queue.cpp"
5 #include "iterator.h"//not needed
6 using std::cin;
7 using std::cout;
8 using std::endl;

9 using namespace QueueSavitch;
10 int main( )
11 {
12 char next, ans;
13 do
14 {
15 Queue<char> q;
16 cout << "Enter a line of text:\n";
17 cin.get(next);
18 while (next != '\n')
19 {
20 q.add(next);
21 cin.get(next);
22 }
23 cout << "You entered:\n";
24 Queue<char>::Iterator i;
25 for (i = q.begin( ); i != q.end( ); i++)
26 cout << *i;
27 cout << endl;
28 cout << "Again?(y/n): ";
29 cin >> ans;
30 cin.ignore(10000, '\n');
31 }while (ans != 'n' && ans != 'N');
32 return 0;
33 }
34
Even though they are not needed,
many programmers prefer to
include these
include directives

for the sake of documentation.
If your compiler is unhappy with
Queue<char>::Iterator i;
try
using namespace ListNodeSavitch;
ListIterator<char> i;
17_CH17.fm Page 736 Tuesday, August 19, 2003 10:22 AM
Iterators 737
Display 17.21 Program Using the Queue Template Class with Iterators
(part 2 of 2)
S
AMPLE
D
IALOGUE
Enter a line of text:
Where shall I begin?
You entered:
Where shall I begin?
Again?(y/n): y
Enter a line of text:
Begin at the beginning
You entered:
Begin at the beginning
Again?(y/n): n
Display 17.22 Implementation File for a Queue with Iterators Template Class
(part 1 of 2)
1 //This is the file queue.cpp. This is the implementation of the template
2 //class Queue. The interface for the template class Queue is in the header
3 //file queue.h.
4 #include <iostream>

5 #include <cstdlib>
6 #include <cstddef>
7 #include "queue.h"
8 using std::cout;
9 using namespace ListNodeSavitch;
10 namespace QueueSavitch
11 {
12 template<class T>
13 Queue<T>::Queue( ) : front(NULL), back(NULL)
14

<
The rest of the definition is given in the answer to Self-Test Exercise 14.
>

15 template<class T>
16 Queue<T>::Queue(const Queue<T>& aQueue)
17

<
The rest of the definition is given in the answer to Self-Test Exercise 17.
>

18 template<class T>
19 Queue<T>& Queue<T>::operator =(const Queue<T>& rightSide)
20

<
The rest of the definition is given in the answer to Self-Test Exercise 18.
>

The member function definitions are the same as in the
previous version of the
Queue template. This is given
to show the file layout and use of namespaces.
17_CH17.fm Page 737 Tuesday, August 19, 2003 10:22 AM
738 Linked Data Structures
Self-Test Exercises
19. Write the definition of the template function inQ shown below. Use iterators. Use the def-
inition of
Queue given in Display 17.20.
template<class T>
bool inQ(Queue<T> q, T target);
//Returns true if target is in the queue q;
//otherwise, returns false.
Trees
I think that I shall never see a data structure as useful as a tree.
Anonymous
A detailed treatment of trees is beyond the scope of this chapter. The goal of this chap-
ter is to teach you the basic techniques for constructing and manipulating data struc-
tures based on nodes and pointers. The linked list served as a good example for our
discussion. However, there is one detail about the nodes in a linked list that is quite
restricted: They have only one pointer member variable to point to another node. A
Display 17.22 Implementation File for a Queue with Iterators Template Class
(part 2 of 2)
21 template<class T>
22 Queue<T>::~Queue( )
23
<
The rest of the definition is given in the answer to Self-Test Exercise 16.
>


24 template<class T>
25 bool Queue<T>::isEmpty( ) const
26

<
The rest of the definition is given in the answer to Self-Test Exercise 14.
>

27 template<class T>
28 void Queue<T>::add(T item)
29

<
The rest of the definition is given in the answer to Self-Test Exercise 15.
>

30 template<class T>
31 T Queue<T>::remove( )
32

<
The rest of the definition is given in the answer to Self-Test Exercise 15.
>
33 }//QueueSavitch
34 #endif //QUEUE_H
17.4
17_CH17.fm Page 738 Tuesday, August 19, 2003 10:22 AM
Trees 739
tree node has two (and in some applications more than two) member variables for

pointers to other nodes. Moreover, trees are a very important and widely used data
structure. So, we will briefly outline the general techniques used to construct and
manipulate trees.
This section uses recursion, which is covered in Chapter 13.

TREE PROPERTIES
A tree is a data structure that is structured as shown in Display 17.23. Note that a tree
must have the sort of structure illustrated in Display 17.23. In particular, in a tree you
can reach any node from the top (root) node by some path that follows the links
(pointers). Note that there are no cycles in a tree. If you follow the pointers, you even-
tually get to an “end.” A definition for a node class for this sort of tree of
ints is also
shown in Display 17.23. Note that each node has two links (two pointers) coming
from it. This sort of tree is called a binary tree because it has exactly two link member
variables. There are other kinds of trees with different numbers of link member vari-
ables, but the binary tree is the most common case.
The pointer named
root serves a purpose similar to that of the pointer head in a
linked list (Display 17.1). The node pointed to by the
root pointer is called the root
node. Note that the pointer
root is not itself the root node, but rather points to the
root node. Any node in the tree can be reached from the root node by following the
links.
The term tree may seem like a misnomer. The root is at the top of the tree and the
branching structure looks more like a root branching structure than a true tree branch-
ing structure. The secret to the terminology is to turn the picture (Display 17.23)
upside down. The picture then does resemble the branching structure of a tree and the
root node is where the tree’s root would begin. The nodes at the ends of the branches
with both link member variables set to

NULL are known as leaf nodes, a terminology
that may now make some sense.
By analogy to an empty linked list, an empty tree is denoted by setting the pointer
variable
root equal to NULL.
Note that a tree has a recursive structure. Each tree has two subtrees whose root
nodes are the nodes pointed to by the
leftLink and rightLink of the root node. These
two subtrees are circled in Display 17.23. This natural recursive structure make trees
particularly amenable to recursive algorithms. For example, consider the task of search-
ing the tree in such a way that you visit each node and do something with the data in
the node (such as writing it to the screen). The general plan of attack is as follows:
Preorder Processing
1. Process the data in the root node.
2. Process the left subtree.
3. Process the right subtree.
binary tree
root node
leaf node
empty tree
preorder
17_CH17.fm Page 739 Tuesday, August 19, 2003 10:22 AM
740 Linked Data Structures
Display 17.23 A Binary Tree
class IntTreeNode
{
public:
IntTreeNode(int theData, IntTreeNode* left, IntTreeNode* right)
: data(theData), leftLink(left), rightLink(right){}
private:

int data;
IntTreeNode *leftLink;
IntTreeNode *rightLink;
};
IntTreeNode *root;
40
20
60
NULL NULL
30
NULL NULL
10
NULL NULL
50
NULL
root
Left subtree Right subtree
17_CH17.fm Page 740 Tuesday, August 19, 2003 10:22 AM
Trees 741
You can obtain a number of variants on this search process by varying the order of
these three steps. Two more versions are given below.
In-order Processing
1. Process the left subtree.
2. Process the data in the root node.
3. Process the right subtree.
Postorder Processing
1. Process the left subtree.
2. Process the right subtree.
3. Process the data in the root node.
The tree in Display 17.23 has stored each number in the tree in a special way known

as the Binary Search Tree Storage Rule. The rule is given in the accompanying box. A
tree that satisfies the Binary Search Tree Storage Rule is referred to as a binary search
tree.
Note that if a tree satisfies the Binary Search Tree Storage Rule and you output the
values using the in-order processing method, the numbers will be output in order from
smallest to largest.
For trees that follow the Binary Search Tree Storage Rule and which are short and fat
rather than long and thin, values can be very quickly retrieved from the tree using a
binary search algorithm that is similar in spirit to the binary search algorithm presented
in Display 13.5. The topic of searching and maintaining a binary storage tree to realize
this efficiency is a large topic that goes beyond what we have room for here.
B
INARY
S
EARCH
T
REE
S
TORAGE
R
ULE
1. All the values in the left subtree are less than the value in the root node.
2. All the values in the right subtree are greater than or equal to the value in the root node.
3. This rule applies recursively to each of the two subtrees.
(The base case for the recursion is an empty tree, which is always considered to satisfy the rule.)
in order
postorder
Binary
Search Tree
Storage Rule

binary
search tree
17_CH17.fm Page 741 Tuesday, August 19, 2003 10:22 AM
742 Linked Data Structures
Example
A T
REE
T
EMPLATE
C
LASS
Display 17.24 contains the definition of a template class for a binary search tree. In this example,
we have made the
SearchTree class a friend class of the TreeNode class. This allows us to
access the node member variables by name in the definitions of the tree class member variables.
The implementation of this
SearchTree class is given in Display 17.25, and a demonstration pro-
gram is given in Display 17.26.
This template class is designed to give you the flavor of tree processing, but it is not really a com-
plete example. A real class would have more member functions. In particular, a real tree class
would have a copy constructor and an overloaded assignment operator. We have omitted these to
conserve space.
There are some things to observe about the function definitions in the class
SearchTree. The
functions
insert and inTree are overloaded. The single-argument versions are the ones we
need. However, the clearest algorithms are recursive, and the recursive algorithms require one
additional parameter for the root of a subtree. Therefore, we defined private helping functions
with two arguments for each of these functions and implemented the recursive algorithms in the
two-parameter function. The single-parameter function then simply makes a call to the two-

parameter version with the subtree root parameter set equal to the root of the entire tree. A similar
situation holds for the overloaded member function name
inorderShow. The function delete-
Subtree
serves a similar purpose for the destructor function.
Finally, it is important to note that the
insert function builds a tree that satisfies the Binary
Search Tree Storage Rule. Since
insert is the only function available to build trees for this tem-
plate class, objects of this tree template class will always satisfy the Binary Search Tree Storage
Rule. The function
inTree uses the fact that the tree satisfies the Binary Search Tree Storage Rule
in its algorithms. This makes searching the tree very efficient. Of course this means that the <
operator must be defined for the type
T of data stored in the tree. To make things work correctly,
the operation < should satisfy the following rules when applied to values of type
T:

Transitivity:

a
<
b
and
b
<
c
implies
a
<

c
.

Antisymmetry:
If
a
and
b
are not equal, then either
a
<
b
or
b
<
a
, but not both.

Irreflexive:
You never have
a
<
a
.
Most natural orders satisfy these rules.
1

1
Note that you normally have both a “less-than-or-equal” relation and a “less-than” relation. These
rules apply to only the “less-than” relation. You can actually make do with an even weaker notion of

ordering known as a
strict weak ordering
which is defined in Chapter 19, but that is more detail than
you need for normally encountered orderings.
17_CH17.fm Page 742 Tuesday, August 19, 2003 10:22 AM
Trees 743
Display 17.24 Interface File for a Tree Template Class
1 //Header file tree.h. The only way to insert data in a tree is with the
2 //insert function. So, the tree satisfies the Binary Search Tree Storage
3 //Rule. The function inTree depends on this. < must be defined and give a
4 //well-behaved ordering to the type T.
5 #ifndef TREE_H
6 #define TREE_H
7 namespace TreeSavitch
8 {
9 template<class T>
10 class SearchTree; //forward declaration
11 template<class T>
12 class TreeNode
13 {
14 public:
15 TreeNode( ) : root(NULL){}
16 TreeNode(T theData, TreeNode<T>* left, TreeNode<T>* right)
17 : data(theData), leftLink(left), rightLink(right){}
18 friend class SearchTree<T>;
19 private:
20 T data;
21 TreeNode<T> *leftLink;
22 TreeNode<T> *rightLink;
23 };

24
25 template<class T>
26 class SearchTree
27 {
28 public:
29 SearchTree( ) : root(NULL){}
30 virtual ~SearchTree( );
31 void insert(T item); //Adds item to the tree.
32 bool inTree(T item) const;
33 void inorderShow( ) const;
34 private:
35 void insert(T item, TreeNode<T>*& subTreeRoot);
36 bool inTree(T item, TreeNode<T>* subTreeRoot) const;
37 void deleteSubtree(TreeNode<T>*& subTreeRoot);
38 void inorderShow(TreeNode<T>* subTreeRoot) const;
39 TreeNode<T> *root;
40 };
41 } //TreeSavitch
42 #endif
The SearchTree template class should have a copy constructor,
an overloading of the assignment operator, and other member
functions. However, we have omitted these functions to keep this
example short. A real template class would contain more member
functions and overloaded operators.
17_CH17.fm Page 743 Tuesday, August 19, 2003 10:22 AM
744 Linked Data Structures
Display 17.25 Implementation File for a Tree Template Class
(part 1 of 2)
1 //This is the implementation file tree.cpp. This is the implementation for
2 //the template class SearchTree. The interface is in the file tree.h.

3 namespace TreeSavitch
4 {
5 template<class T>
6 void SearchTree<T>::insert(T item, TreeNode<T>*& subTreeRoot)
7 {
8 if (subTreeRoot == NULL)
9 subTreeRoot = new TreeNode<T>(item, NULL, NULL);
10 else if (item < subTreeRoot->data)
11 insert(item, subTreeRoot->leftLink);
12 else //item >= subTreeRoot->data
13 insert(item, subTreeRoot->rightLink);
14 }
15 template<class T>
16 void SearchTree<T>::insert(T item)
17 {
18 insert(item, root);
19 }
20 template<class T>
21 bool SearchTree<T>::inTree(T item, TreeNode<T>* subTreeRoot) const
22 {
23 if (subTreeRoot == NULL)
24 return false;
25 else if (subTreeRoot->data == item)
26 return true;
27 else if (item < subTreeRoot->data)
28 return inTree(item, subTreeRoot->leftLink);
29 else //item >= link->data
30 return inTree(item, subTreeRoot->rightLink);
31 }
32 template<class T>

33 bool SearchTree<T>::inTree(T item) const
34 {
35 return inTree(item, root);
36 }
37 template<class T> //uses iostream:
38 void SearchTree<T>::inorderShow(TreeNode<T>* subTreeRoot) const
39 {
40 if (subTreeRoot != NULL)
41 {
42 inorderShow(subTreeRoot->leftLink);
43 cout << subTreeRoot->data << " ";
If all data is entered using the
function
insert, the tree will
satisfy the Binary Search Tree
Storage Rule.
The function
inTree
uses a binary search
algorithm that is a
variant of the one given
in Display 13.5.
Uses in-order traversal
of the tree.
17_CH17.fm Page 744 Tuesday, August 19, 2003 10:22 AM

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×