Collections
Dynamic Data structures are
those which can shrink and grow to fit changing requirements. A container is a
dynamic data structure. Dynamic variables can be allocated (created) and
de-allocated (destroyed) when needed. Dynamic data structures are advantageous
in that the number of variables needed is always exact – no more, no less.
There are three types of collections; linked lists, stacks/queues, and trees.
Linked Lists
A linked list is a chain of
data structures linked together by pointers. The heap is memory not used by the
stack. Static variables reside in the stack, while dynamic variables reside in
the heap. As the stack grows, the heap shrinks. Pointers are used to access the
variables in the heap.
Creating:
Create a pointer to the heap:
|
Node*
list_head; |
Notice that list_head
points to “garbage” right now.
Create a new node in
the list:
|
list_head
= new Node; |
Now the pointer is
pointing to a new node.
Fill in the new nodes
data:
|
list_head->data
=42; |
The
-> follows the pointer into the heap and changes
data’s value for the node that list_head points to.
Create
a second node:
|
list_head->next
= new Node; |
This adds a new node to
the end of the list by changing were the first node points to.
Fill in the new nodes
data and cleanly terminate the list:
|
list_head->next->data
= 91; |
Adds the value 91 to the
new nodes data and then tells the new node’s pointer to point to NULL.
Deleting by moving the
pointer:
|
Node*
temp; |
Creates a temporary
pointer that points to the location list_head points to, then changes
list_head so as it points to the node in the list.
Then deletes the first node of the list.
Inserting:
Adding a node to a linked list involves finding the correct location, and doing the work to add the node. There are three possible positions in which to add nodes at the front, back, or some were in the middle.
To the front:
There
is no work to be done as the list_head always points to the correct location
empty or not. You will need a
by-reference pointer parameter. Create
a new node and fill in its data.
Set
its pointer so as it points to were list_head points too.
Update list_head so as it points to the new start of the list.
|
void
List::insertAtFront(const
int
&value)
if
(isEmpty()) |
To the end of the list:
|
void
List::insertAtBack (const int
&value) |
To the middle:
Used
when order is important. Need to
go to the node that should follow the one to add.
Recursion or iteration must be used to transverse the list till the
correct point in the list is
reached. Each
time you loop compare to see if you need to add the new node before the
current node. If value is the
largest then you need to add the node to the end. To perform the
insertion you will need to create the new
node, insert the nodes data, update the next pointer to the new node, update
the current pointer to the new node.
|
|
Now that you know what a
linked list is lets code one up.
List Node:
|
#ifndef LISTNODE_H |
|
#include "ListNode.h" ListNode::ListNode
(const int
info) |
As you can see a
list
node class is created purely to hold the value needed to be stored, and to
point to the next node in the list.
|
#ifndef LIST_h #include "ListNode.h" class List |
|
#include "List.h" using namespace std; List::List
() List::~List()
while (currentPtr != NULL) void List::insertAtFront(const int
&value)
if (isEmpty()) void List::insertAtBack (const int
&value) bool List::removeFromFront (int
&value) bool List::removeFromBack (int
&value) bool
List::isEmpty() const ListNode*
List::getNewNode (int value) void List::print () const |
As you can see it is the
List
class its self that does all of the work for the list.
The only method present in the List class that has not been as
of yet mentioned is the Print method, which handles printing out the
value stored in the nodes of the list. Now
that you know how you would go about coding a list node and a list
we can move onto Stacks and Queues.
Stacks
and queues are ideas that imply a logical set of behaviors.
They can be implemented in three ways, via a linked list, a tree,
or an array. This lab will
not cover trees, and thus the Stacks and Queues will be
implemented using Linked lists.
Stacks:
Stacks are dynamic
collections that operate on a last in first out data structure
Behaviors:
Push: Add to top of stack
Pop: Remove from top of stack (and return that top value)
Top: Return topmost item (but leave it on the stack)
•Is_Full: is it full?
Is_Empty: is it empty?
Initialize: empty stack
When you use a
linked
list to set up a stack, the linked list will have a limited set of
operations that change its state (only modified from the last node in the
list). Further more you will need
to create a node class, a linked list class, and a stack
class. The Stack class
you create must contain all of the necessary above behaviors.
As you might of guessed we will be using the above Node and the List
classes in order to create our Stack class.
Stacks
Dynamic Implementation:
Step
1.
Within the Push method
create a new node.
|
|
Step
2.
Add the new node to the front of the list.
|
|
Pop Steps
Step
1. Within
the Pop method
capture the value of the top node.
|
|
Step
2. Within
the Pop
method change top so as it points to the next node on the Stack and
then delete the top node.
|
|
Step
3.
Return the value you captured in step 1.
|
|
For the
Is_Full
method you would simply return a Boolean stating if the max size
for the Stack has been reached (true
if it has, false
if it has not) (again this is only used if you set up a max size for the stack).
For the Is_Empty
method again you also return a Boolean if the Stack is
empty (true if it is, false
if it is not). Every time you
initialize a stack it must be initialized so as list_head points to NULL.
Now that you know how the Stack should behave lets look at an
example of a Stack class.
|
#ifndef STACK_H #include "List.h" using namespace std; class Stack |
|
#include "Stack.h" void Stack::push (const int
&d) bool Stack::pop (int &d) bool Stack::isStackEmpty() const void
Stack::printStack() const |
As you can see in order to
set up a stack you will need to #include List.h
in the Stack class. By doing so you can use the List class as the
framework for your stack. As
you can see in order to turn a list into a stack you need to restrict
the allowed modifications capable of being performed on the list. This means that the application should only be allowed to add
and remove nodes from either the front or the end of the list (If you inserted
the nodes at the front of the list remove them their also.
The same goes for the end of the list).
As you can see the print method in the List class
still handles all of the printing for the list/stack.
Queues:
Queues are dynamic
collections that operate on a first in first out data structure.
Behaviors:
Enqueue: Add to end of queue
Top: Return front-most item (but leave it in the queue)
Is_Full: Is it full?
Is_Empty: Is it empty?
Initialize: empty queue
Queue
as a linked list:
|
|
Engueue:
Step 1. Create a new node with data
|
|
Step
2. Add the
new node to the end.
|
|
Step
3. Update
pointers as needed
|
|
Dequeue:
Step
1. Capture
the first value (to return)
|
|
Step
2.
Remove first node move head to next.
|
|
Step
3.
Return value capture in step 1.
|
|
For the
Is_Full
method you would simply return a Boolean stating if the max size
for the Queue has been reached (true
if it has, false
if it has not) (again this is only for if you set up a max size for the queue).
For the Is_Empty
method simply return a Boolean if the Queue is empty (true
if it is, false
if it is not). Every time you
initialize a queue it must be initialized so as head and tail
point to NULL.
Now
that you know how a Queue should behave lets look at an example Queue
class.
|
#ifndef QUEUE_H #include "List.h" class Queue |
|
#include "Queue.h" void Queue::enqueue (const int
&d) bool Queue::isQueueEmpty () const void Queue::printQueue () const |
As you can see in order to
set up a queue you will need to #include the List class in the Queue class.
By doing so you can use the List class as the framework for your
queue. As you can see in
order to turn a list into a queue you need to restrict the
allowed modifications capable of being performed on the list.
This means that the application should only be allowed to add nodes to
one end and remove them from the other end (For example if you added nodes to
the front you would remove them from the rear).
As you can see the print method in the List class still
handles all of the printing for the list/queue.
Now
that you know what the code for the linked lists, Stacks, and Queues
should look like it is time to move on to templates. As you may have noticed above, all of the above procedures
for the Dynamic Data Types take in only integers.
However what would you do if you wanted to work with floats,
doubles, or longs?
The answer is that as the code stands now you would have to change
every instance of int to the data type you want the Dynamic Data Types to work with.
The problem with this is that it is not only time consuming but it is
also error prone. How ever there
is a way to change the code so as we can pass in what ever variable type we
wish and still have the application perform properly, which is know as making
a template. To templatize the
application you would the majority of instances were you keyed in the key word
int
you would simply need to change it to the key word you choose to use when
setting up the template.
|
template
<class
d> |
For
example if you set up the template like the above example you would replace
all instances of int
were you took
the integer in as an argument with d. For example with the list node class when you templatize
(make it a template) the header file it will look like the bellow code.
As you can see below it proved necessary to take the code from the CPP
files and place it in the Header files.
This is because if you do not do so you will get the link error
link2001 when you build the application.
Also as you will notice between the non-templatized and the templatized
version very little changed. As
you can see the key word int changed to the key word you choose when setting up
the template. Also every were you
created an instance of the classes in the header files you needed to type in
<Keyword>
beside the data type (for ex: ListNode<NODETYPE>). Lastly when you expanded upon a class procedure you needed to
type in ClassName<Keyword>::Procedurename
()
|
#ifndef LISTNODE_H template<class NODETYPE> template <class NODETYPE> |
|
#ifndef
LIST_H #include "ListNode.h" template <class NODETYPE>
ListNode< NODETYPE > *getNewNode (const
NODETYPE &); template <class NODETYPE> template <class NODETYPE>
while ( currentPtr != NULL )
cout<<"All nodes destroyed\n\n"; template <class NODETYPE> template <class NODETYPE> template <class NODETYPE> template <class NODETYPE>
while (currentPtr->nextPtr !=
listEnd ) template <class NODETYPE> template <class NODETYPE> template <class NODETYPE> |
|
#ifndef STACK_H #include "List.h" using namespace std; template<class STACKTYPE> template<class STACKTYPE> template<class STACKTYPE> template<class STACKTYPE> |
|
#ifndef QUEUE_H #include "List.h" template <class QUEUETYPE> template <class QUEUETYPE> template <class QUEUETYPE> template <class QUEUETYPE> |
Now
that you know how the code for the classes should look (templatized/non-templatized)
the bellow example shows how the templatized version of Main.cpp
should look.
|
#include <iostream> using namespace std;
int choice;
T value;
do void main ()
Stack<int> intStack;
Stack<double> doubleStack;
Queue <int> intQueue; |
As
you can see above we created two procedures along with the Main section
of the application. The testList procedure is used to handle all the List class
calls and to hand the user input when responding to the menu.
The instructions
procedure is used to handle printing out the applications menu.
Also as you notice in the above code example when ever you create an
instance of one of the classes that you coded you need to type in DataType
<int> VarName
so as when you create an instance of the class it will know the data type that
it will be dealing with. In order
to convert the above Main section to non-templatized you simply remove
all of the <datatype> from
when you created an instance of the classes.
Lastly you will also need to remove template
<class T> from
over the testList procedure and the <T>
from the argument list.