Chapter 13
POINTERS AND LINKED LISTS
1. Solutions to and Remarks on Selected Practice Programs and
Programming Projects
Programming Project 1: Reverse Nodes
Write a void function that accepts a linked list of integers and reverses the order of
its nodes. The function will have one call by reference parameter that is a pointer to
the head of the list. After the call, this pointer will point to a list that has the same
nodes in reverse order of the original list. This function does not allocate nor delete
store, but only rearranges the nodes.
Remarks: The easiest way is to traverse the list is stacking pointers using a loop (This
could be done recursively, but that would hide the stack usage thus subverting the intent
of the problem.)
// file 13prg1.cpp
// For Chapter 13 Programming Project #1:
//
// head_insert.cpp taken from Display 13.4
// stack.h taken from Display 13.17
// stack.cpp taken from Display 13.18
// See the modifications noted below.
//
// In head_insert.cpp, stack.h and stack.cpp, I did a query
//search to carefully replace char with int.
//
//WARNING: This code compiles but fails silently if you do
//not replace char with int. The compiler happily converts
//int to char and back without any error message.
//
//compile commands:
//g++ -c stack.cpp head_insert.cpp
1
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
//generates .o files. This is a great help during debugging
//
//g++ 15prg1.cc stack.o head_insert.o
//
to generate the executable
//
#include <iostream>
#include "stack.h"
//stack.h includes a definition of struct StackFrame;
void head_insert( StackFramePtr & head, int i );
void reverse( StackFramePtr head );
//struct definition is here as a convenience during coding.
//
struct StackFrame
//
{
//
int data;
//
StackFrame* link;
//
};
typedef StackFrame * StackFramePtr;
int main()
{
using namespace std;
int number = 0;
StackFramePtr head, temp;
head = NULL;
cout << "enter numbers, negative quits" << endl;
cin >> number;
while( number > 0 )
{
head_insert( head, number);
2
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
cin >> number;
}
temp = head;
cout << "list as entered: " << endl;
while( NULL != temp )
{
cout << temp->data << endl;
temp = temp->link;
}
cout << "calling reverse" << endl;
reverse( head );
temp = head;
cout << "list reversed: " << endl;
while( NULL != temp )
{
cout << temp->data << endl;
temp = temp->link;
}
}
void reverse( StackFramePtr head )
{
Stack stack;
StackFramePtr temp;
temp = head;
while ( NULL != temp )
{
stack.push( temp->data);
temp = temp->link;
}
temp = head;
while ( NULL != temp )
{
3
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
temp->data = stack.pop();
temp = temp->link;
}
}
A typical run follows:
enter numbers, negative quits
1 2 3 4 5 -1
list as entered:
5
4
3
2
1
calling reverse
inside reverse
list reversed:
1
2
3
4
5
Programming Project 2: Merge_lists
The function merge_lists is to take two call-by-reference parameters that are pointer
variables that point to the heads of two linked lists of values of type int. The linked lists
are assumed to be already sorted. (The smallest is at the head, increasing along the list).
The function returns a pointer to the head of a new list that is the rearranged nodes of the
combined lists. The resulting list is to be sorted, smallest at the head, and increasing along
the list.
When the function terminates, the two arguments should have the value NULL (0). Note
that this solution neither creates nor destroys nodes.
4
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
typedef Node* NodePtr;
NodePtr merge_lists( NodePtr & head1, NodePtr & head2);
Using the reference to pointer parameters to walk through the lists, determine which head
node element is smaller, hook that node to the head pointer for the return value, and
compare the one that is left to the next node from the list that was last used. Continue
until the referent to the smaller list is NULL. Run out the larger list, hooking its nodes to
the head pointer for the return value.
This results in a list that is sorted with the largest at the head and decreasing as we move
along the list.
Return head.
// File: merge_lists.cpp
// For Chapter 13 Project 2
#include "stack.h"
#include <iostream>
//Precondition: head1 and head1 point to linked lists of
//
StackFrame structs that are each sorted, each head
//
pointer pointing to the
smallest item in the list.
//Postcondition: head points to a list of nodes taken from
//
both lists, sorted. head points to the smallest in the
//
combined list. The pointers head1 and head2 are null
//
pointers..
StackFramePtr
merge_lists( StackFramePtr & head1, StackFramePtr & head2)
{
StackFramePtr head, follow, smaller;
// if either list is NULL, return the other head.
if( NULL == head1 && NULL != head2 )
{
5
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
head = head2;
head2 = NULL;
return head;
}
if( NULL == head2 && NULL != head1 )
{
head = head1;
head1 = NULL;
return head;
}
if( NULL == head2 && NULL == head1 )
return NULL;
// now get one node attached to head...
if ( NULL != head1 && NULL != head2 )
{
if ( head2->data > head1->data )
{
head = head1;
// smaller node to front of
head's
head1 = head1->link; // list and take it off head1's
list
}
else
{
head = head2;
// as above,
for head2
head2 = head2->link;
}
}
follow = head;
// connect the rest of them.
6
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
while ( NULL != head1 && NULL != head2 )
{
if ( head2->data > head1->data )
{
smaller = head1;
// put smaller node on head's list
head1 = head1->link; // and take it off head1's list
}
else
{
smaller = head2; // as above,
for head2
head2 = head2->link;
}
follow->link = smaller;
follow = smaller;
}
// one of head1 or head1 is NULL. Must run out the other
//list
if (NULL != head2 )
follow->link = head2; // put node on head's list
if(NULL != head1 )
follow->link = head1; // put node on head's list
head1 = NULL;
head2 = NULL;
return head;
}
// file 13prg2.cc
//
// Test program for merge_lists.cc
//
7
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
//
// compile commands:
// g++ -c stack.cpp head_insert.cpp reverse.cpp
//
to generate .o files. This is a great help during
//
debugging
//
//g++ 13prg2.cpp merge_lists.cpp head_insert.o reverse.o
//stack.o
//to generate executable
//
//head_insert.cpp taken from 15-04.cc, main program deleted.
//
//Modifications: I did a query search and replace of Node
//by StackFrame and a query search and replace of char by
//int
//
// reverse.cc taken from 15prg1.cc
// stack.h taken from Display 13.17
// stack.cc taken from Display 13.18
// Modification: stack.h and stack.cpp, I did a query search
//and replace of char by int
//
//BUGS: if unsorted data is entered, or is sorted in the
//larger to smaller direction, this program enters an
//infinite loop.
//
//Exercise for the interested student: Why?
//
#include <iostream>
#include "stack.h"
//stack.h has typedef StackFrame * StackFramePtr;
8
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
void head_insert( StackFramePtr & head, int i );
StackFramePtr merge_lists( StackFramePtr &, StackFramePtr&);
void reverse( StackFramePtr head );
/*
This is here so I can see what a StackFrame looks like
while writing code.
struct StackFrame
{
int data;
StackFrame* link;
};
*/
typedef StackFrame * StackFramePtr;
int main()
{
using namespace std;
int number = 0;
StackFramePtr head, head1, head2, temp;
head = head1 = head2 = temp = NULL;
//NOTE WELL the necessity of this initialization.
cout << "enter increasing numbers for list1 , "
<< "negative quits\n";
cin >> number;
while( number > 0 )
{
head_insert(head1, number);
9
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
cin >> number;
}
reverse(head1);
temp = head1;
cout << "list1: " << endl;
while( NULL != temp )
{
cout << temp->data << endl;
temp = temp->link;
}
cout << "enter increasing numbers for list2, "
<< "negative quits\n";
cin >> number;
while( number >0 )
{
head_insert( head2, number);
cin >> number;
}
reverse(head2);
temp = head2;
cout << "list2: " << endl;
while( NULL != temp )
{
cout << temp->data << endl;
temp = temp->link;
}
10
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
head = merge_lists(head1, head2);
temp = head;
cout << "merged list, front to rear: " << endl;
while( NULL != temp )
{
cout << temp->data << endl;
temp = temp->link;
}
}
Runs testing this have one list larger, then the other list larger, then equal sizes. I had
some trouble with bugs, so I tested each of the 3 ways.
A test run follows:
enter increasing numbers for list1 , negative quits
1 3 5 -1
list1:
1
3
5
enter increasing numbers for list2, negative quits
2 4 -1
list2:
2
4
merged list, front to rear:
1
2
3
4
5
11
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
Another test run:
enter increasing numbers for list1 , negative quits
1 3 -1
list1:
1
3
enter increasing numbers for list2, negative quits
2 4 6 8 -1
list2:
2
4
6
8
merged list, front to rear:
1
2
3
4
6
8
Another test run:
enter increasing numbers for list1 , negative quits
1 3 5 -1
list1:
1
3
5
enter increasing numbers for list2, negative quits
2 4 6 -1
list2:
2
12
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
4
6
merged list, front to rear:
1
2
3
4
5
6
Programming Project 5: Different Linked List of double values
This problem was harder to do than I expected because the details are not very specific. It
is necessary to pay careful attention to what the member functions are to do and how they
are be implemented. (Actually, I set the problem for Professor Savitch, but I did not
carefully specify the details.)
When you assign this problem, do it in stages. Read my solution and the embedded
comments. First, ask the students for carefully written pre- and post- conditions for each
member function and a description of how it might be implemented. Require that they
think carefully about the specifications and implementation for each of the member
functions. Second talk with them about what these conditions ought to be and how the
functions might be implemented with their pre and post conditions. Third, require a test
program that tests all the member functions, and tests them in several orders to detect
untoward interactions.
Disabling compiler generated member functions
I have disabled operator assignment and copy construction of the class List by placing
declarations of operator= and the copy constructor in the private section of the class.
This prevents inadvertent assignment of one list to another, the initialization of one list
from another, passing a list by value or returning a list from a function by value. Though
this is not mentioned in the text, I usually discuss this valuable technique with my classes.
13
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
The make facility:
For programs of a size beyond a single file, I advocate use of any make facility you have
available. I have discussed make earlier in the IRM.
Even a program of this small size was easier to write using a command line editor (I used
the Emacs editor under Debian Linux 3.0, but I have used Windows XP command line
editor and make as well. (For NT and successors: click run, type cmd, then type edit
filename.cpp). Both Borland and Microsoft provide command line make facilities that,
while they are not the same as Unix make that I use, nevertheless are faster and require
far less machine resources than the GUI they provide. I have former students who work
for Adobe and Microsoft where they report that most programmers there prefer to use a
command line editor and some make facility that uses the command line compiler. While
the programs that they were working on were larger than any we will write in this class,
you don’t learn a facility when you need it for a large program.
make or make –k?
If there is an error, the make –k command "makes" everything that is not out of date1
except the component where there the compiler detects an error, whereas make stops the
process at the first error the compiler finds. With large programs and many files to
compile, make –k can save considerable time by compiling the components already free
of compiler detectable errors.
//File: list.h
//This is the HEADER FILE list.h. This is the INTERFACE
1
The make program checks the files that the program we are trying to make depends on.
If any source (.cpp or .h) file that our program to be made depends on is newer than the
corresponding object (.o) file, the make program recompiles anything that has an out-ofdate dependency, but recompiles nothing that is up-to-date. This saves enough time to be
well worth the time to learn the make facility and write the makefile. Of course, the GUI
development environment also only recompiles the program components that have been
changed, but the GUI by its nature takes far more resources so is slower than the
command line facility.
14
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
//for the class List for Chapter 13 programming problem 6
#ifndef LIST_H
#define LIST_H
#include <iostream>
struct Node
{
double item;
Node * next;
};
class List
{
public:
List( );
double front()const;
//Precondition: calling List object is not empty
//Post condition: returns first item in list (data at
//node pointed to by head)
double back() const;
// Precondition: calling List object is not empty
// Post condition: returns last item in list.
double current() const;
// Precondition: calling object is not empty
// Postcondition: returns item pointed to by current
//
pointer.
15
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
void advance();
//Precondition: calling object is not empty and current
//
is not the last item in the list
//Postcondition: current pointer moved to next item.
void reset();
//Precondition: calling object is not empty
//Postcondition: current pointer moved to first item in
//
the list
int size()const;
//Precondition: calling object has been defined.
//Postcondition: The return value is the value of count,
//
i.e. the number of items in the list.
friend std::ostream& operator<<(std::ostream& outs,
const List& write_me);
//Precondition: List argument is defined
//Postcondtion: Entries in the list have been inserted
//
into the ostream outs, one per line. This
//
function just returns on an empty list.
//I suggest additional versions of the insert function
//below. These are coded in List.cpp and tested in
//ListTest.cpp
void insert(double after_me, double insert_me);
// I interpret the meaning of this insert member
// as follows:
//
// Precondition: calling object has been defined.
// Postcondition:
// If the list was empty,
16
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
//
insert_me has been inserted as the first and
//
current item
// else if after_me is in the list
//
insert_me has been inserted after the first
//
instance of after_me in the list.
// else
//
insert_me has been inserted at the end of the list.
//
and the node containing insert_me is the current
//
node count has been incremented.
bool find(double find_me);
//Precondition: calling List object is defined
//Post Condition:
//If list is empty,
//
return false
// else if find_me is in the list
//
set make node containing find_me "current" node
//
return true
// else (find_me is not in the list)
//
return false
void remove();
// precondition: calling List object is defined
// post condition: "current" node has been removed
//
Node following current is new current
void remove(double delete_me);
// Precondition: calling List object is defined
// Postcondition: if node containing delete_me is in list,
17
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
//
that node is removed. The node following
//
is the new current node.
bool is_empty();
//Precondition: calling object is defined
//Postcondition:
//
//
//
//
if there are no elements in the list,
return true
else
return false
//This is a suggested replacement for the insert given
//above. I have implemented both these and the above
//member function.
void insert(double insert_me);
// Precondition: calling object has been defined.
// PostCondition:
// if the list was empty,
//
insert_me has been inserted as the first item in the
//
list, current points to it
// else
//
insert_me has been inserted after the current item
// count has been incremented
void insert_front(double insert_me);
// Precondition: calling object has been defined.
// PostCondition:
// if the list was empty,
//
insert_me has been inserted as the first item in the
//
list, current points to it
// else
//
insert_me has been inserted as the front item
18
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
//
Instructor’s Resource Guide
Chapter 13
current points to this item
// count has been incremented
~List();
private:
void operator=();
// disable assignment
List(const List&); // disable copy construction
Node* cur;
Node* head;
int count;
};
#endif //LIST_H
//File: list.cpp
// This is the implementation file for the list of
// Programming Project 6 of Chapter 13
// The interface file is list.h
#include "list.h"
#include <iostream>
#include <cassert>
using namespace std;
List::List() : cur(0), head(0), count(0)
{// deliberately empty
}
19
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
List::~List()
{
cout << "starting destructor\n";
Node* ptr = head;
Node* delete_me;
while(0 != ptr)
{
delete_me = ptr;
ptr = ptr->next;
delete delete_me;
}
cout << "Leaving destructor\n";
}
void List::remove(double delete_me)
{
if(find(delete_me))
remove();
}
// precondition: calling List object is not empty
// post condition: "current" node has been removed
//
Node following current is new current
void List::remove()
{
//Debugging code: Attempting to fetch or remove a
//"current" item beyond the end of a non-empty list
//results in a segmentation fault. This code traps that
//condition
if(0 != head && cur == 0)
{
20
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
cout << "In the List::remove() function. "
<< "Non null head pointer and null cur pointer."
<<" Aborting\n";
abort();
}
Node* ptr = head;
//list is empty (head is null), do nothing
if (0 == head)
{
cout << "Null list, do nothing\n";
return;
}
//If list consists of only the head node (head->next
//is null),head is the only node, so that head is the
//current node.
Kill the head node, set head and cur to
//null pointer value, and decrement count.
if(0 == head->next)
{
delete head;
head = 0; // prevents dangling pointers
cur = 0;
// prevents dangling pointers
count--;
return;
}
//If list consists of more than one node, head is the
//current node, then move head up and kill node head did
//point to, decrement count
if(head == cur)
{
21
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
head = head->next;
cur = head;
delete ptr;
ptr = 0; //In move involved code, this practice will
//help prevent dangling pointers. It is surely
//overkill here.
count--;
return;
}
//If head isn't null and isn't current node, cur isn't
//last node move ptr up to node prior to current node
//It is here that non-null head pointer and null cur
//pointer causes a segmentation fault.
if(0 != head && head != cur &&
0 != cur->next)
{
for( ; ptr->next != cur; ptr = ptr->next)
; // ptr now points to node previous to current
ptr->next = cur->next;
delete cur;
cur = ptr->next;
count--;
return;
}
//Current node is last node
if(0 == cur->next)
{
for( ; ptr->next != cur; ptr = ptr->next)
22
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
; // ptr now points to node previous to current
ptr->next = 0;
delete cur;
cur = ptr;
return;
}
std::cout << "falling off the end of remove\n";
}
double List::front() const
{
return head->item;
}
// Implementation Note: for List::back(), I could
// have added a private data member pointing to last
// item, but this incurs overhead other places
double List::back() const
{
Node* ptr = head;
for( ; ptr->next != 0; ptr = ptr->next)
; // next pointer of last node is null
return ptr->item;
}
//Precondition: List is not empty.
//NOTE: The behavior of current is anomalous for an empty
//list
//Postcondition: return value is current item
double List::current() const
23
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
{
return cur->item;
}
//Precondition: List must not be empty
//Postcondition:
//if current is not at end of list then
//
current is advanced by one node
//otherwise,
//
leaves current at end of list.
//
//This allows harmless use for empty and single node lists
void
List::advance()
{
if(0 != cur->next)
cur = cur->next;
}
void List::reset()
{
cur = head;
}
int List::size()const
{
return count;
}
//Outputs nothing for an empty list.
std::ostream& operator<<(std::ostream& outs, const List&
write_me)
{
if(0 != write_me.size())
24
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
for(Node* ptr = write_me.head; ptr != 0; ptr = ptr>next)
outs << ptr->item << std::endl;
return outs;
}
void List::insert(double after_me, double insert_me)
{
if(is_empty())
insert_front(insert_me);
find(after_me);
insert(insert_me);
}
bool List::is_empty()
{
return (0 == head);
}
bool List::find(double find_me)
{
if(0 == size()) // list is empty
return false;
else
{
Node* ptr = head;
// for loop advances ptr until either we find
// find_me or the end of the list is encountered
for(; 0 != ptr && find_me != ptr->item; ptr = ptr->next)
25
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
;
if(0 != ptr)
{ //ptr not null, so ptr points to node containing
find_me
cur = ptr;
assert(0 == head || cur != 0 );
return true;
}
// ptr is null,
find_me was not found
assert(0 == head || cur != 0 );
return false;
}
}
// list may not be empty with insert.
// if list is empty, uses insert_front
// otherwise insert after current item
// This
inserts after cur
void List::insert(double insert_me)
{
if(0 == head)
{
insert_front(insert_me);
return;
}
Node* n = new Node;
n->item = insert_me;
n->next = cur->next;
cur->next = n;
26
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
count++;
cur = n; // inserted item is now "current"
assert(0 == head || cur != 0 );
}
//as name says,
this inserts in front of the list
//This may be used to insert into an empty list
//creates, initializes node then links it in front.
void List::insert_front(double insert_me)
{
Node* n = new Node;
n->item = insert_me;
n->next = head;
head = n;
count++;
cur = head; // inserted item is now "current"
assert(0 == head || cur != 0 );
}
//File: listTest.cpp
//Test program for Chapter 13 programming project 6
#include <iostream>
#include "list.h"
//Compile command g++ list.cpp listTest.cpp
//or if a Unix compatible make facility is available,
//the make file mentioned at the bottom may be used by
//issuing one of the commands:
//
27
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
//make
//
or
//make –k
int main()
{
using namespace std;
List list;
cout << "\nsize == 0, the list is empty initially. \n";
if ( 0 == list.size() ) // to test for empty list
cout << "\nThe list size is 0, i.e., "
<< "the list is empty initially."
<< endl;
int i; //loop variables to be used outside for loops
list.insert_front(-50);
cout << "Inserted -50. \nThe front item is: "
<< list.front();
cout << "\nThe back item is: "
<< list.back() << endl;
for(i = 1; i < 50; i++)
list.insert( 2 * i - 50 );
// This leaves "current" item at the last item inserted
// necessitating a list reset prior to list display
list.reset();
28
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
cout << "\nFinished building list\nValue of size
= "
<< list.size() << endl << endl;
cout << "Here is the list we constructed\n"
<< "i
v\ti
v\ti
v\ti
v\ti
for(i = 0 ; i < list.size();
i++)
v";
{
if(!(i%5))
cout << endl;
cout << i << " " << list.current() << "\t";
list.advance();
}
cout << endl;
cout << "current item is \n" ;
cout << list.current() << endl;
cout << "Dumping the list (one per line) with 'cout
<< list'" << endl;
cout << list << endl;
cout <<
"List size =
" << list.size() << endl;
cout << "front of list: " << list.front() << endl;
cout << "back of list: " << list.back() << endl;
list.advance();
cout << "current item is " << list.current() << endl;
cout << "removing current item from the list\n";
list.remove();
29
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
cout << "current item is: " << list.current() << endl;
cout <<
"List size:
" << list.size() << endl;
cout << "Try to find 40 in the list\n";
if(list.find(40))
cout << "current item (40?) is "
<< list.current() << endl;
else
cout << "item with value 40 not found\n";
cout << "Try to find 400 in the list\n";
if(list.find(400))
cout << "current item (400?) is " << list.current() <<
endl;
else
{
cout << "item with value 400 not found\n"
<< "current item is " << list.current()
<<
endl;
}
list.advance();
cout << "Inserting 999999 after 40\n";
list.insert(40, 999999);
cout << "current item is now " << list.current() << endl;
cout << "About to remove 999999 from the list\n";
list.remove(999999);
30
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
if(list.find(999999))
cout << "999999 is in the list but should not be
there\n";
else
cout << "999999 is NOT in the list and should not be
there\n";
List list2; // should be an empty list
cout << "list2 is empty is ";
if(list2.is_empty())
cout << "true\n";
else
cout << "false\n";
cout <<"\ninserting 99 into empty_list\n";
list2.insert(99);
cout << "list2 is empty is ";
if(list2.is_empty())
cout << "true\n";
else
cout << "false\n";
cout << "this is the result of cout << list2\n";
cout << list2 << endl;
cout << "This is output from 'cout
<< list2.current() << endl' \n";
cout << list2.current() << endl;
cout << "Removing current item\n";
list2.remove();
31
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
cout << "This is the result of cout << list2\n";
cout << list2 ; // overloaded << provides a trailing <CR>
cout << "If list2 is not empty, the "
<< "current item in list2 fetched: \n";
if(!list2.is_empty())
cout << list2.current() << endl;
else
cout << "list2 is empty, cannot fetch current item.\n";
//Here I attempt to use the disabled operations:
//copy construction:
//List list3 = list2;
//Error Message from g++:
//list.h:140
'List List(const List & )' is private
//ListTest2.cpp:149: within this context
//operator assignment:
//List f1, f2;
//f1 = f2;
//Error Message from g++:
//list.h:139: 'void List::operator=(const List&)' is
//private
//ListTest2.cpp:155
within this context
return 0;
}
32
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
A test run gives the following output.
size == 0, the list is empty initially.
The list size is 0, i.e., the list is empty initially.
Inserted -50.
The front item is: -50
The back item is: -50
Finished building list
Value of size
= 50
Here is the list we constructed
i
v
i
v
i
v
i
v
i
v
0 -50
1 -48
2 -46
3 -44
4 -42
5 -40
6 -38
7 -36
8 -34
9 -32
10 -30
11 -28
12 -26
13 -24
14 -22
15 -20
16 -18
17 -16
18 -14
19 -12
20 -10
21 -8
22 -6
23 -4
24 -2
25 0
26 2
27 4
28 6
29 8
30 10
31 12
32 14
33 16
34 18
35 20
36 22
37 24
38 26
39 28
40 30
41 32
42 34
43 36
44 38
45 40
46 42
47 44
48 46
49 48
current item is
48
Dumping the list (one per line) with 'cout << list'
-50
-48
-46
-44
33
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
-42
-40
-38
-36
-34
-32
-30
-28
-26
-24
-22
-20
-18
-16
-14
-12
-10
-8
-6
-4
-2
0
2
4
6
8
10
12
14
16
18
20
34
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
22
24
26
28
30
32
34
36
38
40
42
44
46
48
List size =
50
front of list: -50
back of list: 48
current item is 48
removing current item from the list
current item is: 46
List size:
50
Try to find 40 in the list
current item (40?) is 40
Try to find 400 in the list
item with value 400 not found
current item is 40
Inserting 999999 after 40
current item is now 999999
About to remove 999999 from the list
999999 is NOT in the list and should not be there
list2 is empty is true
35
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
inserting 99 into empty_list
list2 is empty is false
this is the result of cout << list2
99
This is output from 'cout << list2.current() << endl'
99
Removing current item
This is the result of cout << list2
If list2 is not empty, the current item in list2 fetched:
list2 is empty, cannot fetch current item.
starting destructor
Leaving destructor
starting destructor
Leaving destructor
A make file for Unix or Linux :
# File: makefile
# For Chapter 13 Project 6
# Use: to create your executable, be certain that the files
# listTest2.cpp, list.cpp and list.h are in the same
# directory with this make file. Then issue the command:
#
# make
# To remove the .o files,
issue the command:
# make clean
#MAKE PECULARITY: The lines following the ch13prog6: and the
#clean: lables MUST start with a tab character. You can't
#see it, but the make facility will FAIL without it.
36
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
ch13prog6: listTest2.o list.o list.h
g++
-o ch13prog6
listTest2.o list.o
# The next line is for cleaning up .o and executables.
clean:
rm ch13prog6 listTest2.o list.o
#end of makefile
Programming Project 6 : Circular Linked List, Princess and Suitors
// ****************************************************************
//
// This program determines where to stand in line if you would
// like to win the hand of the princess. The princess eliminates
// every third suitor and loops back to the beginning of the line upon
// reaching the end.
//
// This program uses a circular linked list
// to store the list of suitors and removes
// each one in turn.
//
// ****************************************************************
#include <iostream>
#include <cstdlib>
using namespace std;
// ======================
// Node definition for
// the queue of suitors
// ======================
class SuitorNode
{
public:
SuitorNode(){}
SuitorNode(int initialnum) : num(initialnum) { next = NULL; }
int getNum() { return num; }
SuitorNode* getNext() { return next; }
void setNext(SuitorNode *nextNode) { next=nextNode; }
private:
SuitorNode *next;
// Next node in linked list
int num;
// Place in line
};
// ======================
//
main function
// ======================
37
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
int main()
{
// Variable declarations
int i;
int numSuitors;
SuitorNode *current = NULL;
SuitorNode *tail = NULL;
SuitorNode *prev = NULL;
Instructor’s Resource Guide
Chapter 13
// Linked list of suitors
// Tracks end of list as it is built
// Tracks previous node in list
cout << "Enter the number of suitors" << endl;
cin >> numSuitors;
if (numSuitors > 0)
{
current = new SuitorNode(1);
current->setNext(current);
// Make into a circular list
tail = current;
for (int i=1; i<numSuitors; i++)
{
// Add new node to the end of the list
SuitorNode *temp = new SuitorNode(i+1);
temp->setNext(tail->getNext());
tail->setNext(temp);
tail = temp;
}
}
if (numSuitors <=0)
{
cout << "Not enough suitors." << endl;
}
else if (numSuitors == 1)
{
cout << "You would stand first in line." << endl;
}
else
{
// Eliminate a suitor as long as there is at least one
while (current->getNext() != current)
{
// Count three people ahead, or go two people down
// since we include the current person in the count
for (i=0; i<2; i++)
{
prev = current;
current = current->getNext();
}
// Eliminate contestant current
prev->setNext(current->getNext()); // Skip over this node
delete (current);
current = prev->getNext();
}
cout << "To win the princess, you should stand in position " <<
current->getNum() << endl;
// Delete last node
delete current;
}
return 0;
38
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
}
Programming Project 7 : Linked List for Lab Stations
// *********************************************************************
//
// This program uses linked lists to store login information for
// four labs. Each of the four labs is referenced by the labs[] array
// which is indexed from 0-3. A pointer in the labs[] array then
// references a linked list that is of length for however many
computers
// are in that lab.
//
// *********************************************************************
#include <iostream>
#include <cstdlib>
using namespace std;
// Constants
const int NUMLABS = 4;
// ======================
// Node definition for
// the list of computer ID's
// ======================
class ComputerNode
{
public:
ComputerNode()
{
userID = -1;
next = NULL;
}
ComputerNode(int stationID) : computerID(stationID)
{
next = NULL;
userID = -1;
}
int getStationID() { return computerID; }
void setUserID(int id) { userID = id; }
int getUserID() { return userID; }
ComputerNode* getNext() { return next; }
void setNext(ComputerNode *nextNode) { next=nextNode; }
private:
ComputerNode *next;
// Next node in linked list
int computerID;
// ID of this station
int userID;
// ID of the user on this station
};
// Type definition
typedef ComputerNode* NodePtr;
// Function prototypes
39
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
void
void
void
void
void
void
Instructor’s Resource Guide
Chapter 13
createLists(NodePtr labs[], int labsizes[]);
freeLists(NodePtr labs[]);
showLabs(NodePtr labs[]);
login(NodePtr labs[]);
logoff(NodePtr labs[]);
search(NodePtr labs[]);
// ======================
// createLists:
// Creates the linked lists for the labs.
// The first array is the array of labs,
// The second array contains the size (or number of computers)
// we will put in each lab. This dictates the size of the linked
// lists.
// ======================
void createLists(NodePtr labs[], int labsizes[])
{
int i,j;
for (i=0; i < NUMLABS; i++)
{
labs[i] = new ComputerNode(1);
// First computer
in lab
NodePtr curNode = labs[i];
// Start the loop below at 2 since w already added computer
#1
for (j=2; j<=labsizes[i]; j++)
{
NodePtr newNode = new ComputerNode(j);
curNode->setNext(newNode);
curNode = newNode;
}
}
return;
}
// ======================
// freeArrays:
// Releases memory we allocated with "new".
// ======================
void freeLists(NodePtr labs[])
{
int i;
NodePtr curNode, nextNode;
for (i=0; i < NUMLABS; i++)
{
curNode = labs[i];
while (curNode != NULL)
{
nextNode = curNode->getNext();
delete curNode;
curNode = nextNode;
}
}
return;
}
// ======================
// showLabs:
// Displays the status of all labs (who is logged into which computer).
40
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
// ======================
void showLabs(NodePtr labs[])
{
int i;
NodePtr curNode;
cout << "LAB STATUS" << endl;
cout << "Lab #
Computer Stations" << endl;
// Loop through each lab
for (i=0; i < NUMLABS; i++)
{
cout << i+1 << "
";
// Loop through each computer in the lab
curNode = labs[i];
while (curNode != NULL)
{
cout << curNode->getStationID() << ": ";
if (curNode->getUserID() == -1)
{
cout << "empty ";
}
else
{
cout << curNode->getUserID() << " ";
}
curNode = curNode->getNext();
}
cout << endl;
}
cout << endl;
return;
}
// ======================
// login:
// Simulates a user login by asking for the login info from
// the console.
// ======================
void login(NodePtr labs[])
{
int id = -1, lab = -1, num = -1;
NodePtr current;
// Get input from the keyboard, validating data ranges
while ((id < 0) || (id > 99999))
{
cout << "Enter the 5 digit ID number of the user logging
in:" << endl;
cin >> id;
}
while ((lab < 0) || (lab > NUMLABS))
{
cout << "Enter the lab number the user is logging in from
(1-" <<
NUMLABS << "):" << endl;
cin >> lab;
}
cout << "Enter computer station number the user is logging in to:
" << endl;
cin >> num;
41
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
// Check to see if this station is valid
// Loop until we hit the end of the list or find one
// where the station ID matches the ID for the login
current = labs[lab-1];
while ((current!=NULL) && (current->getStationID()!=num))
{
current = current->getNext();
}
if (current == NULL)
// Reached end of list with no matching
station
{
cout << "ERROR, invalid station number." << endl;
return;
}
if (current->getUserID()!=-1)
{
cout << "ERROR, user " << current->getUserID() <<
" is already logged into that station." << endl;
return;
}
// Assign this station to the user
current->setUserID(id);
return;
}
// ======================
// logoff:
// Searches through the linked lists for the input user ID and if found
// logs that user out.
// ======================
void logoff(NodePtr labs[])
{
int id = -1, i,j;
NodePtr current;
// Get input from the keyboard, validating data ranges
while ((id < 0) || (id > 99999))
{
cout << "Enter the 5 digit ID number of the user to find:"
<< endl;
cin >> id;
}
// Search linked lists as in above function
for (i=0; i<NUMLABS; i++)
{
current = labs[i];
while (current!=NULL)
{
if (current->getUserID() == id)
{
current->setUserID(-1);
cout << "User " << id << " is logged off." <<
endl;
return;
}
current = current->getNext();
}
}
42
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
cout << "That user is not logged in." << endl;
return;
}
// ======================
// search:
// Searches through the linked lists for the input user ID and if found
// outputs the station number.
// ======================
void search(NodePtr labs[])
{
int id = -1, i,j;
NodePtr current;
// Get input from the keyboard, validating data ranges
while ((id < 0) || (id > 99999))
{
cout << "Enter the 5 digit ID number of the user to find:"
<< endl;
cin >> id;
}
for (i=0; i<NUMLABS; i++)
{
current = labs[i];
while (current != NULL)
{
if (current->getUserID() == id)
{
cout << "User " << id << " is logged into
station "
<< current->getStationID() << " in lab "
<< (i+1) << endl;
return;
}
current = current->getNext();
}
}
cout << "That user is not logged in." << endl;
return;
}
// ======================
//
main function
// ======================
int main()
{
NodePtr labs[NUMLABS];
int labsizes[NUMLABS];
int choice = -1;
// Initialize
labsizes[0] =
labsizes[1] =
labsizes[2] =
labsizes[3] =
// Array to reference each lab
// Number of computers in each lab
labsizes to those given in the problem
5;
// 5 computers in lab 1
6;
4;
3;
// 3 computers in lab 4
// Create ragged array structure
createLists(labs, labsizes);
43
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
// Main Menu
while (choice != 0)
{
cout << endl;
showLabs(labs);
cout << "MAIN MENU" << endl;
cout << "0) Quit" << endl;
cout << "1) Simulate login" << endl;
cout << "2) Simulate logoff" << endl;
cout << "3) Search" << endl;
cin >> choice;
if (choice == 1)
{
login(labs);
}
else if (choice == 2)
{
logoff(labs);
}
else if (choice == 3)
{
search(labs);
}
}
freeLists(labs);
return 0;
// Free memory before exiting
}
Programming Project 8 : Queue class for DMV simulation
// *********************************************************************
// Modify or re-write the Queue class (Display 13.21 – 13.23) to
// simulate customer arrivals at the DMV (Department of Motor Vehicles)
// counter. As customers arrive they are given a ticket number. When a
// customer service agent is free the customer with the next ticket
// number is called. This system results in a FIFO queue of customers
// ordered by ticket number. Write a program that implements the queue
// and simulates customers entering and leaving the queue. Input into
// the queue should be the ticket number and a timestamp when the ticket
// was entered into the queue. A ticket and its corresponding timestamp
// is removed when a customer service agent handles the next customer.
// Your program should save the length of time the last three customers
// spent waiting in the queue. Every time a ticket is removed from the
// queue update these times and output the average of the last three
// customers as an estimate of how long it will take until the next
// customer is handled.
//**********************************************************************
//
//
//
//
//
//
//
//
File Name: queue.h
Author:
Email Address:
Project Number: 13.08
Description: This is the interface for the class Queue, which is a
class for a queue of dmv customers. This is a modified
version of Listing 13-21.
Last Changed: October 13, 2007
44
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
#ifndef QUEUE_H
#define QUEUE_H
namespace queuesavitch
{
struct QueueNode
{
long timestamp;
int customer_number;
QueueNode *link;
};
typedef QueueNode* QueueNodePtr;
class Queue
{
public:
Queue( );
//Initializes the object to an empty queue.
Queue(const Queue& aQueue);
~Queue( );
void add(long timestamp, int custno);
//Postcondition: item has been added to the back of the queue.
void remove(long ×tamp, int &customer_number);
//Precondition: The queue is not empty.
//Returns the timestamp and customer number at the front of
// the queue and removes that item from the queue.
bool empty( ) const;
//Returns true if the queue is empty. Returns false otherwise.
private:
QueueNodePtr front; //Points to the head of a linked list.
//Items are removed at the head
QueueNodePtr back; //Points to the node at the other end of the
//linked list. Items are added at this end.
};
}//queuesavitch
#endif //QUEUE_H
// File Name: queue.cpp
// Author:
// Email Address:
// Project Number: 13.08
// Description: Implementation of the Queue Class. Based on Listing 1323.
// Last Changed: October 13, 2007
#include
#include
#include
#include
<iostream>
<cstdlib>
<cstddef>
"queue.h"
45
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
using namespace std;
namespace queuesavitch
{
//Uses cstddef:
Queue::Queue( ) : front(NULL), back(NULL)
{
//Intentionally empty.
}
Queue::Queue(const Queue& aQueue)
{
front = NULL;
back = NULL;
QueueNodePtr temp_ptr = aQueue.front;
while (temp_ptr != NULL)
{
add(temp_ptr->timestamp, temp_ptr->customer_number);
temp_ptr = temp_ptr->link;
}
}
Queue::~Queue( )
{
QueueNodePtr temp_ptr = front;
while (temp_ptr != NULL)
{
QueueNodePtr next = temp_ptr->link;
delete temp_ptr;
temp_ptr = next;
}
}
//Uses cstddef:
bool Queue::empty( ) const
{
return (back == NULL); //front == NULL would also work
}
//Uses cstddef:
void Queue::add(long timestamp, int custno)
{
if (empty( ))
{
front = new QueueNode;
front->timestamp = timestamp;
front->customer_number = custno;
front->link = NULL;
back = front;
}
else
{
QueueNodePtr temp_ptr;
temp_ptr = new QueueNode;
temp_ptr->timestamp = timestamp;
temp_ptr->customer_number = custno;
temp_ptr->link = NULL;
back->link = temp_ptr;
46
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
back = temp_ptr;
}
}
//Uses cstdlib and iostream:
void Queue::remove(long &ts, int &cust)
{
if (empty( ))
{
cout << "Error: Removing an item from an empty queue.\n";
exit(1);
}
ts = front->timestamp;
cust = front->customer_number;
QueueNodePtr discard;
discard = front;
front = front->link;
if (front == NULL) //if you removed the last node
back = NULL;
delete discard;
}
}//queuesavitch
// File Name: dmv.cpp
// Author:
// Email Address:
// Project Number: 13.08
// Description: Queue-based simulation of a line of customers at the
DMV.
// Last Changed:
#include <ctime>
#include <iostream>
#include "queue.h"
using namespace std;
using namespace queuesavitch;
int main()
{
// A queue for the line of customers
Queue line;
char input;
long seconds;
int customer_number = 0;
// An array for the last three wait-times
long wait_times[3] = { 0, 0, 0 };
do
{
// Let the user know whether the line is empty or not
if (line.empty()) {
cout << "The line is empty. Enter '1' to simulate a "
<< "customer's arrival " << endl << "or 'q' to quit: " ;
}
else {
47
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
cout << "Enter '1' to simulate a customer's arrival, '2' to"
<< endl;
cout << "simulate helping the next customer, or 'q' to quit:
";
}
cin >> input;
cout << endl;
seconds = static_cast<long>(time(NULL));
if (input == '1')
{
// Increment the customer number and add them to the queue
customer_number++;
cout << "Customer " << customer_number
<< " entered the queue at time " << seconds << "." << endl
<< endl;
line.add(seconds, customer_number);
}
else if (input == '2')
{
// Make sure the queue is not empty
if (line.empty())
{
cout << "No customers are in the queue." << endl << endl;
}
else
{
int cust;
long timestamp, wait, wait_sum, average_wait;
line.remove(timestamp, cust);
wait = seconds - timestamp;
// Compute the average of the last three (if we've had
// at least 3)
wait_times[cust % 3] = wait;
wait_sum = wait_times[0] + wait_times[1] + wait_times[2];
if (cust < 3)
{
average_wait = wait_sum/cust;
}
else
{
average_wait = wait_sum/3;
}
cout <<
<<
<<
cout <<
<<
<<
"Customer " << cust << " is being helped at time "
seconds << ". Wait time was " << wait
" seconds." << endl;
"The estimated wait time for customer "
(cust + 1) << " is " << average_wait
" seconds." << endl << endl;
}
}
}
while (input != 'q');
return 0;
}
48
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
Programming Project 9 : Maze using Linked Structures
// *********************************************************************
// Write a program that implements the maze shown in the text using
// nodes and pointers. Each node in the graph will correspond to a node
// in your code that is implemented in the form of a class or struct.
// The edges correspond to links that point from one node to another.
// Start the user in node A. The user’s goal is to reach the finish in
// node L.
//**********************************************************************
//
//
//
//
//
//
File Name: room.h
Author:
Email Address:
Project Number: 13.09
Description: Header file for the Room class
Last Changed: October 13, 2007
#ifndef ROOM_H
#define ROOM_H
#include <string>
using namespace std;
namespace roomisenhour
{
class Room
{
public:
Room(string name);
// Constructs a room with the given name
string get_name() const;
// Returns the name of this room
string get_available_directions() const;
// Returns a string describing the available
// directions
Room *get_adjacent_room(char dir) const;
// Returns the room in the given direction. If the move
// is not permitted, NULL will get returned.
Room *add_room(char dir, Room *other);
// Adds a link to the given room in the given direction
Room *add_room(char dir, string name);
// Creates and adds a new room in the given direction
private:
string name;
Room *north;
Room *west;
Room *south;
Room *east;
49
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
string append_direction(
Room *room, string dir, string full_string) const;
// Appends the dir string to full_string if room is not NULL
};
typedef Room *RoomPtr;
}
#endif
//
//
//
//
//
//
File Name: room.cpp
Author:
Email Address:
Project Number: 13.09
Description: Implementation of the room class
Last Changed: October 13, 2007
#include <iostream>
#include <string>
#include "room.h"
using namespace std;
namespace roomisenhour
{
// Constructs a room with the given name
Room::Room(string room_name) : name(room_name), north(NULL),
west(NULL),
south(NULL), east(NULL)
{
}
// Returns the name of this room
string Room::get_name() const
{
return name;
}
// Appends the dir string to full_string if room is not NULL
string Room::append_direction(
Room *room, string dir, string full_string) const
{
if (room != NULL)
{
if (full_string.length() > 0)
{
full_string = full_string + " or ";
}
full_string = full_string + dir;
}
return full_string;
}
// Returns a string describing the available
// directions
string Room::get_available_directions() const
{
50
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
string
dirs =
dirs =
dirs =
dirs =
return
Instructor’s Resource Guide
Chapter 13
dirs;
append_direction(north, "north ('N')", dirs);
append_direction(west, "west ('W')", dirs);
append_direction(south, "south ('S')", dirs);
append_direction(east, "east ('E')", dirs);
dirs;
}
// Returns the room in the given direction. If the move
// is not permitted, NULL will get returned.
Room *Room::get_adjacent_room(char dir) const
{
Room *room = NULL;
if (dir == 'N')
room = north;
else if (dir == 'W')
room = west;
else if (dir == 'S')
room = south;
else if (dir == 'E')
room = east;
return room;
}
// Creates and adds a room in the given direction
Room *Room::add_room(char dir, string new_room_name)
{
Room *new_room = new Room(new_room_name);
Room *room = add_room(dir, new_room);
if (room == NULL)
{
delete new_room;
}
return room;
}
// Adds a link to the given room in the given direction
Room *Room::add_room(char dir, Room *new_room)
{
// Ideally we'd check for an existing room in the given
direction,
// and delete it if necessary. This turns out to be a moderately
// difficult problem since an room may be reachable in multiple
// ways. Hence we'll just print a warning.
if (get_adjacent_room(dir) != NULL)
{
cout << "WARNING: Attempting to add a second room in "
<< "direction " << dir << " from room " << name
<< endl;
}
if (dir == 'N')
north = new_room;
else if (dir == 'W')
west = new_room;
else if (dir == 'S')
south = new_room;
else if (dir == 'E')
east = new_room;
51
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
else
{
new_room = NULL;
cout << "WARNING: Attempting to add room in bad direction '"
<< dir << "' to room " << name << endl;
}
return new_room;
}
}
//
//
//
//
//
//
File Name: maze.cpp
Author:
Email Address:
Project Number: 13.09
Description: Implemention of a simple maze game.
Last Changed: October 13, 2007
#include <iostream>
#include <string>
#include "room.h"
using namespace std;
using namespace roomisenhour;
main()
{
RoomPtr a, b, c, d, e, f, g, h, i, j, k, l, current, next;
char input;
// Create rooms, making bi-directional links. A later
// chapter will introduce the 'this' pointer, which would
// simplify this kind of bi-directional linking.
a = new Room("A");
b = a->add_room('E', "B");
b->add_room('W', a);
e = a->add_room('S', "E");
e->add_room('N', a);
i = e->add_room('S', "I");
i->add_room('N', e);
j = i->add_room('E', "J");
j->add_room('W', i);
f = b->add_room('S', "F");
f->add_room('N', b);
g = f->add_room('E', "G");
g->add_room('W', f);
c = g->add_room('N', "C");
c->add_room('S', g);
k = g->add_room('S', "K");
k->add_room('N', g);
h = g->add_room('E', "H");
h->add_room('W', g);
d = c->add_room('E', "D");
d->add_room('W', c);
l = h->add_room('S', "L");
l->add_room('N', h);
current = a;
do
52
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
{
// See if we're at the end of the
if (current->get_name() == "L")
{
cout << "Congratulations! You
<< "of the maze." << endl;
}
else
{
cout << "You are in room " <<
<< " of a maze of twisty
<< endl;
}
maze
have reached room L, the end "
current->get_name()
little passages, all alike."
// Print the available directions
cout << "You can go " << current->get_available_directions()
<< ", or enter 'Q' to quit: " << endl;
cin >> input;
// Allow upper or lower case
input = toupper(input);
if (input != 'Q')
{
// See if the direction was valid
next = current->get_adjacent_room(input);
if (next == NULL)
{
cout << "'" << input << "' is not a valid direction."
<< endl;
}
else
{
current = next;
}
}
}
while (input != 'Q');
return 0;
}
Programming Project 10 : Reverse Polish Notation Calculator
Instructor Notes: This program modifies the stack class given in the chapter to a stack of
ints. The stack is then used to implement a RPN calculator.
// ****************************************************************
// stack.h
// ****************************************************************
//This is the header file stack.h. This is the interface for
//the class Stack,
//which is a class for a stack of ints.
#ifndef STACK_H
#define STACK_H
namespace stacksavitch
{
53
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
struct StackFrame
{
int data;
StackFrame *link;
};
typedef StackFrame* StackFramePtr;
class Stack
{
public:
Stack( );
//Initializes the object to an empty stack.
~Stack( );
//Destroys the stack and returns all the memory to the
freestore.
void push(int the_number);
//Postcondition: the_number has been added to the stack.
int pop( );
//Precondition: The stack is not empty.
//Returns the top integer on the stack and removes that
//top integer from the stack.
bool empty( ) const;
//Returns true if the stack is empty. Returns false otherwise.
private:
StackFramePtr top;
};
}//stacksavitch
#endif //STACK_H
// ****************************************************************
// stack.cpp
// ****************************************************************
//This is the implementation file stack.cpp that holds integers.
//This is the implementation of the class Stack.
//The interface for the class Stack is in the header file stack.h.
#include <iostream>
#include <cstddef>
#include "stack.h"
using namespace std;
namespace stacksavitch
{
//Uses cstddef:
Stack::Stack( ) : top(NULL)
{
//Body intentionally empty.
}
Stack::~Stack( )
{
char next;
while (! empty( ))
next = pop( );//pop calls delete.
}
54
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
//Uses cstddef:
bool Stack::empty( ) const
{
return (top == NULL);
}
void Stack::push(int the_number)
{
StackFramePtr temp_ptr;
temp_ptr = new StackFrame;
temp_ptr->data = the_number;
temp_ptr->link = top;
top = temp_ptr;
}
//Uses iostream:
int Stack::pop( )
{
if (empty( ))
{
cout << "Error: popping an empty stack.\n";
exit(1);
}
int result = top->data;
StackFramePtr temp_ptr;
temp_ptr = top;
top = top->link;
delete temp_ptr;
return result;
}
}//stacksavitch
// ****************************************************************
// main.cpp
// ****************************************************************
#include <iostream>
#include <string>
#include "stack.h"
using namespace std;
using namespace stacksavitch;
int main()
{
cout << "Reverse Polish Notation Calculator." << endl;
cout << "Enter numbers or operators, one per line, and 'q' to
quit." << endl;
Stack mystack;
string s;
55
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
// Repeatedly input a string and process it as a RPN calculation
do
{
cin >> s;
if (s != "q")
{
int val1, val2;
// Check for operator
// It would be more robust to check for an empty stack
if (s == "/")
{
val1 = mystack.pop();
val2 = mystack.pop();
mystack.push(val2 / val1);
}
else if (s == "*")
{
val1 = mystack.pop();
val2 = mystack.pop();
mystack.push(val2 * val1);
}
else if (s == "-")
{
val1 = mystack.pop();
val2 = mystack.pop();
mystack.push(val2 - val1);
}
else if (s == "+")
{
val1 = mystack.pop();
val2 = mystack.pop();
mystack.push(val2 + val1);
}
else
{
// Assume the character is an integer
int num = atoi(s.c_str()); // C-String to int
mystack.push(num);
}
}
} while (s != "q");
// The final value on the stack is the answer
int num = mystack.pop();
cout << "The final value is " << num << endl;
cin >> s;
return 1;
}
11. No solution provided.
2. Outline of Topics in the Chapter
56
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
13.1 Nodes and Linked Lists
Nodes
Linked Lists
Inserting a Node at the Head of a List
Searching a Linked List
Pointers as Iterators
Inserting and Removing Nodes Inside a List
13.2 Programming Examples:
Stacks and Queues
3. General Remarks on the Chapter
An array is fixed in size at the time of compilation, and that arrays cannot grow or shrink.
A dynamic array is allocated in memory at runtime, but is fixed in size at the time of
allocation, and requires a bit of work to increase or decrease the size of the space
allocated for data. To increase the memory space allocated is ugly and slow.
To increase the memory space for a dynamic array, one allocates enough free store
memory for the data presently occupying the dynamic array plus the desired increase.
Then move the data, and deallocate the original free store memory. To increase the
memory space, allocate the required smaller memory region, transfer the data, and
deallocate the presently allocated memory. Both require a peak memory allocation that is
roughly twice the memory needed.
In contrast, linked lists are not fixed in size. They can grow or shrink in size as needed.
Linked lists allow incremental allocation of store. The time required to do so is
independent of the number of elements in the list.
13.1 Nodes and Linked Lists
This section defines a node as a struct object having data and a pointer to a struct of the
same type, that is a self-referential structure. (Alternately, a node may be defined as a
class object, depending on need and taste.)
57
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
There is a discussion of the NULL pointer in the text and in Chapter 8 of this IRM, along
with ample warnings about dangling pointers, orphaned storage and memory leaks.
A Language Note:
Operators that follow the operand are called postfix operators. In C and C++ these
operators bind more closely than most other operators. Postfix operators include the
operator for member access (through a pointer), ->, and the dot operator, also called the
member access operator. This explains why the parentheses are necessary if access to a
struct through a pointer is made like this:
(*node_ptr).member_name
This is one of the things C++ inherited from C. This ugly bit of code explains why the C
language designers chose to add the -> operator:
node_ptr->member_name
Linked Lists
The text declares:
struct Node
{
int data;
Node * link;
};
typedef Node* NodePtr;
NodePtr head;
head = new Node;
Always remember that declaring a pointer allocates only (automatic) space for the
pointer, but not space for the node to which the pointer can point. Allocating memory
from the free store is the programmer’s responsibility.
Access to the data members is done via the -> operator:
head->data = 3;
58
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
head->link = NULL;
It is very important that the student understand that linked lists have this in common with
C-strings: there is a NULL terminator for each. Even though they are spelled differently,
they are both 0. (‘\0’ is one spelling for 0, and is a character. NULL is another spelling for
0, this one is an integer! Look it up in cstdlib. )
Inserting a Node at the Head of a List
The first thing to point out to the student is that a sequence of well-executed sketches of
the linked list and the insertion task will save a vast amount of time in debugging later.
It is much more convenient to add the node at the Head, since we already have a pointer
pointing to the Head. Of course we can add a node to a list at the end of a list, but this
requires that we know where that is. We either have to maintain a pointer to the end of
the list or traverse the list (examine nodes one by one) until we find the end, that is, a
node with a link pointer having a NULL value.
The text points out that adding a node to the head of the list is easy:
Create the node. Initialize the data field. Make the link pointer field point at the existing
list (the node to which the Head pointer points). Finally, move the head pointer to point
at the new node.
The sequence of steps as code looks like this:
// Head already points to an already constructed NULL
// terminated linked list
NodePtr temp;
temp = new Node;
temp->data = // the new data, whatever it is, goes here
temp->link = Head;
Head = temp;
59
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
temp
12
Head
15
3
NULL
Head is made to point to the newly allocated node.
temp
12
Head
15
3
NULL
PITFALL Losing Nodes
If you assign to a pointer that already points to free store before deleting that store, as the
students say, “... you blew it.” Suppose you assigned Head the value of temp before
assigning temp’s link filed. There is nothing pointing to the rest of the list, and you have
committed an error. If this is only a few bytes, the memory loss is nothing to worry about.
However, the data loss may be serious. Further, such losses tend to occur in loops, where
thousands, even hundreds of thousands of bytes can be chopped off and lost. See Display
13.5 in the text.
I have pointed out, and the text points out, memory lost in this manner is unavailable to
your program. Worse, the memory is unavailable to any program running concurrently on
your system (even operating system programs). The result is a system that acts as if it had
this much less memory than what you paid for. The operating system slows to a crawl, or
crashes, depending on the robustness of the operating system.
Please caution students about the antisocial effects of memory leaks, not to mention
lowered grades that can result. ;)
Searching a Linked List
60
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
A summary of the linked list search follows.
Here we declare a “visit” pointer. We make it point at the head node. Then we determine
whether the data member is equal to the target. If so return the pointer to the node.
Otherwise, we make the visit pointer point at the link member of the visit pointer. We do
this repeatedly, while the visit pointer is not NULL.
This is only slightly different from the text’s algorithm. The difference is where the loop
is exited. My discussion suggests testing at the beginning of the loop for a NULL pointer,
and the exit is in the middle of the loop, when the target is found.
There is no particular advantage to either way.
struct Node
{
int data;
Node* link;
};
typedef Node* NodePtr;
NodePtr search( NodePtr head, int target)
{
NodePtr visit = head;
while ( NULL != visit )
{
if ( visit->data == target )
return visit;
visit = visit->link;
}
return NULL;
}
61
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
The author’s approach is usually slightly different from mine. I usually stick closely to the
C/C++ idiom, while he is a bit more logical than I. I present alternatives here, in a forum
for the instructor.
Pointers as Iterators
This will be an important topic when we get to the Standard Template Library. This
library has template containers, a hierarchy of iterators that enable access to and cycling
through the elements that have been inserted into the containers without compromising
the structure of the container, and template algorithms (template functions with iterator
arguments) that manipulate the data in the containers using the iterators to access the
container contents.
The behavior of pointers is the model that was used as the beginning point for the concept
of iterator for the Standard Template Library. Much C++ programming is done using this
library. The concept of iterator is vital to advanced C++ programming.
Inserting and Removing Nodes Inside a List
If we have tools to insert at any place in a list, and tools to search the list for a location
where a new item should go, then we can keep a list sorted as data comes in at random.
To insert in a list, we need a pointer to the node that should have the newly created node
after it. The text calls this the after_me pointer. Then assign a newly allocated (with
new) node to be inserted to the temp_ptr, and initialize the data fields.
The order of the next assignments is critical to preventing losing the tail of list. Make the
link field of the node pointed to by temp_ptr point to the same place the link pointer
of the after_me pointer. Then the after_me link member can be assigned the value
of the temp_ptr.
Saying this is just horrible. It is much clearer in pictures and code.
62
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
H ead
First, find
w here
nod e goes
after_me
temp _p tr
Fou rth
Second , create,
reasssign
initialize nod e
after_me
to be inserted
Third , make the temp p ointer
p oint w here after_me’s link
p oints.
Code:
NodePtr after_me = search(head, target);
// First
NodePtr temp_ptr = new Node;
// Second
temp_ptr->data = data_to_be_added;
temp_ptr->link = after_me->link;
// Third
after_me->link = temp_ptr;
// Fourth
The text has complete code for adding a node in the middle of a linked list in Display
13.9.
PITFALL: Assigning Head Pointers
Please emphasize this pitfall. It is of critical importance that the student understand that
when a pointer variable is assigned data is not moved in the linked list. Rather, when
pointer variables are assigned, the values of the pointer variables themselves change, that
is, the place to which the pointer variable points is changed.
If head1 points to a linked list and head2 points to another linked list, merely
assigning one pointer to another will orphan the free store pointed to by the head of the
list that is the lvalue in the assignment, causing a memory leak.
head1 = head2;//This orphans the memory pointed to
//originally by head1.
If it is necessary to assign a head pointer, you must either save the value of the pointer in
another pointer variable, or walk through the list, deleting the store, node by node, if you
63
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
are through with it. If you are not through with that memory, you should reassess your
desire to assign the head pointers.
That is not the end of the difficulty. Remember our discussion of aliases? After assigning
pointers, as is done here, you have two variables that refer to the same collection of
memory locations. Changing one of these lists changes the (same) list pointed to by the
other. The author says it clearly: Remember there is only one list, not two.
13.2 A Linked List Application
When we were discussing recursion, we remarked that the call frames in recursion (in
fact, in any function call) were stored in a last-in/first-out data structure called a stack.
(This stack belongs to the operating system). The author presents a linked list
implementation of a stack abstract data type.
Suggested Additional Programming Assignments Using Stacks
These are telegraphic suggestions for programming assignments that can be done using
stacks.
1. Checking for balanced curly braces.
2. Extend the balanced curly braces to include all grouping symbols: ( ), { }, and [ ].
3. Extend the balanced curly braces to handle C style comments (/* . . . */), and C-string
literals. Do not allow nesting of comments within comments, or C-string literals within
C-string literals. Grouping symbols occurring within a comment or a C-string literal
should be ignored by the grouping symbol matching code.
4. Palindrome detection. The entire input is saved and stacked. Then compare the
characters first to last of saved input to the succession of stack top values.
5. Postfix recognition and translation to infix.
6. Postfix (reverse polish) calculator. Binary operators are + - * /, unary operator is sqrt.
Here the successive numbers are stacked upon pressing <enter>. If a binary operator is
encountered, the top two stack items are popped. The result is pushed. Care and
64
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Savitch
Problem Solving w/ C++, 9e
Instructor’s Resource Guide
Chapter 13
consistency is necessary in the case of subtraction and division. Unary operators pop a
single element from the stack. The calculator simulation displays the top of the stack.
7. Postfix Calculator Enhancement: implement unary operators sin, cos, tan, log,
exp, both base e.
8. Infix expression recognition. This will require some discussion by the instructor. Either
use precedence parsing or some other algorithm.
Additional Programming Problems using Linked Lists
Definition: A queue is a data structure that is First In - First Out.
A queue class has member functions that provide:
a) remove and read access at an end called the "front" of the queue.
b) insert access at the other end, which is called the "back" of the queue.
c) tests: bool is_empty, bool is_full
d) no other access to the elements in the list.
1. Implement a class queue using
a) linked lists
b) dynamically growing arrays (say allocate double the previous space when more
space is needed)
c) list implemented as in Programming Project 5 or 6.
d) Implement template versions each of the above queue implementations.
2. A doubly linked list has forward link and backward link pointers in the node struct as
well as data.
a) Reimplement the list of Programming Project 5 or 6 as a doubly linked list.
b) Implement the list class in part a) (much as is suggested in Project 7) as a template
class.
b) Is there any advantage to implementing the stack class of Chapter 13 using doubly
linked lists?
65
Copyright © 2015 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
© Copyright 2026 Paperzz