Binary Search Tree

advertisement
106731499
-1-
Chapter 10
Trees
Binary Tree Structure
Designing TreeNode Functions
Using Tree Scan Algorithms
Binary search trees
Using Binary search Trees
The BinSTree Implementation
106731499
-2-
Binary Tree Structure
Arrays and Linked Lists are linear lists. A linear list is a general description
for data structures that include arrays, stacks, queues, and linked lists.
Linked Lists have the advantages of flexibility over arrays, however their
weak feature is that they are sequential..
Searching a linked list for some target key requires a sequential search.
O(n)
QUESTION? Can the nodes of a linked list be rearranged so that we can
search in time
O ( log n ) ?
IDEA: To keep the advantages of linked storage and obtain the speed of
binary search.
SOLUTION: Store the nodes in the structure of the comparison tree itself
using links to describe the relations ( left and right ) of the tree. Such a tree
is called a binary tree.
106731499
-3-
Tree Terminology -A tree is a nonlinear structure, that consists of nodes
and branches. The organization flows from the root to the outer nodes, called
leaves.
A
B
D
C
E
F
I
G
H
J
Tree Structure:
- A collection of nodes that originates from the starting node called
the root or parent.
- The root or parent node may point to other nodes called its
children.
- The children of a node or children of these children are called
descendants.
- The parent and grandparents are called ancestors.
- A node with no children is called a leaf node.
- Each node in the tree is the root of its own subtree which is
defined as a node and all descendants of the node.
- You move from the parent node to other descendants along a path.
- Each non-root node has a single parent
- The path between the root and a node provides a measure called
the level of a node. The level of a node is the length of the path
from the root to the node.
- The depth of a tree is the maximum level of any node in the tree or
the path of a tree is the longest path from the root to a node.
106731499
-4-
Binary Trees - General Definition of a binary tree:
A Binary Tree is either empty, or it consists of a node called the root
together with two binary trees called the left subtree ( left child ) and the
right subtree ( right child ) of the root.
A binary tree is a recursive structure. At any level n, a binary tree may
n
contain 1 to 2 nodes. The number of nodes in a tree relative to the depth of
the tree contributes to the density. A degenerate tree - single leaf node and
each non leaf has only one child.:
Complete binary trees (each level has all possible nodes or all leaf nodes at
the leftmost positions in the tree )
degenerate binary tree
incomplete binary tree
106731499
-5-
BINARY TREE NODES
left
left
right
right
Left
riright
rightrigth
CLASS tnode
left
right
Left
Declaration
right
right
Left
“d_tnode.h”
// represents a node in a binary tree
template <typename T>
class tnode
{
public:
// tnode is a class implementation structure. making the
// data public simplifies building class functions
T nodeValue;
tnode<T> *left, *right;
// default constructor. data not initialized
tnode()
{}
// initialize the data members
tnode (const T& item, tnode<T> *lptr = NULL,
tnode<T> *rptr = NULL):
nodeValue(item), left(lptr), right(rptr)
{}
};
right
106731499
-6-
Building a binary tree
// pointers to integer tree nodes
tnode<int> *root, *p, *q, *r;
// allocate leaf nodes with values 20 and 40
p = new tnode<int> (20);
q = new tnode<int> (40);
// allocate node at level 1 with value 30
// and left child 40
r = new tnode<int>(30, q, NULL);
// allocate root node with value 10 and
// children 20 (left) and 30 (right)
root = new tnode<int>(10, p, r);
10
root
20
30
p
r
40
q
106731499
-7-
Traversal Methods
Recursive Scanning Methods
Recursive Tree Traversal - Different scan traversals are distinguished by
the order in which they perform the actions at a node.
N L R - Preorder Traversal
template <typename T>
void preorderOutput(tnode<T> *t, const string& separator = "
{
// the recursive scan terminates on a empty subtree
if (t != NULL)
{
cout << t->nodeValue << separator; // output the node
preorderOutput(t->left, separator); // descend left
preorderOutput(t->right, separator);// descend right
")
}
}
L N R - Inorder Traversal
template <typename T>
void inorderOutput(tnode<T> *t, const string& separator = " ")
{
// the recursive scan terminates on a empty subtree
if (t != NULL)
{
inorderOutput(t->left, separator); // descend left
cout << t->nodeValue << separator; // output the node
inorderOutput(t->right, separator); // descend right
}
}
L R N - Postorder Traversal
template <typename T>
void postorderOutput(tnode<T> *t, const string& separator = " ")
{
// the recursive scan terminates on a empty subtree
if (t != NULL)
{
postorderOutput(t->left, separator);
// descend left
postorderOutput(t->right, separator);
// descend right
cout << t->nodeValue << separator;
// output the node
}
}
106731499
-8-
Tree Traversal
A
B
C
D
E
G
1. Preorder Traversal:
2. Inorder Traversal:
3. Postorder Traversal:
H
ABDGCEHIF
DGBAHEICF
GDBHIEFCA
F
I
106731499
-9-
Binary Search Tree
Binary Search Trees
BINARY SEARCH TREE - Definition
A binary search tree is a binary tree that is either empty or in which each
node contains a key that satisfies the conditions:
1. All keys ( if any ) in the left subtree of the root precede ( al less than ) the
key in the root.
2. The key in the root precedes ( is less than ) all keys (if any ) in its right
subtree.
3. The left and right subtrees of the root are again binary search trees.
Note: an inorder traversal visits the data in ascending order (Treesort)
25
10
37
15
30
65
Sample Binary Search Tree
A binary search tree is easily implemented using linked storage. To search
for a particular target the first comparison is made at the root of the tree. If
the target is less than the key in the root the search continues with the left
subtree. If the target is greater than the key in the root, the search continues
with the right subtree. The function can be written using recursion or by
writing a loop. The search terminates on an empty tree or if the target is
found. Other key operations on a Binary Search Tree include insert(),
erase(), and find() .
106731499
- 10 -
#ifndef BINARY_SEARCH_TREE_CLASS
#define BINARY_SEARCH_TREE_CLASS
#ifndef NULL
#include <cstddef>
#endif // NULL
#include
#include
#include
#include
#include
<iomanip>
<strstream>
<string>
<queue>
<utility>
#include "d_except.h"
// for setw()
// for format conversion
// node data formatted as a string
// pair class
// exception classes
using namespace std;
// declares a binary search tree node object
template <typename T>
class stnode
{
public:
// stnode is used to implement the binary search tree class
// making the data public simplifies building the class
functions
T nodeValue;
// node data
stnode<T> *left, *right, *parent;
// child pointers and pointer to the node's parent
// constructor
stnode (const T& item, stnode<T> *lptr = NULL,
stnode<T> *rptr = NULL, stnode<T> *pptr = NULL):
nodeValue(item), left(lptr), right(rptr),
parent(pptr)
{}
};
// objects hold a formatted label string and the level,column
// coordinates for a shadow tree node
class tnodeShadow
{
public:
string nodeValueStr;
// formatted node value
int level,column;
tnodeShadow *left, *right;
tnodeShadow ()
{}
};
106731499
- 11 -
template <typename T>
class stree
{
public:
// include the iterator nested classes
#include "d_stiter.h"
stree();
// constructor. initialize root to NULL and size to 0
stree(T *first, T *last);
// constructor. insert the elements from the pointer
// range [first, last) into the tree
stree(const stree<T>& tree);
// copy constructor
~stree();
// destructor
stree<T>& operator= (const stree<T>& rhs);
// assignment operator
iterator find(const T& item);
// search for item. if found, return an iterator pointing
// at it in the tree; otherwise, return end()
const_iterator find(const T& item) const;
// constant version
int empty() const;
// indicate whether the tree is empty
int size() const;
// return the number of data items in the tree
pair<iterator, bool> insert(const T& item);
// if item is not in the tree, insert it and
// return a pair whose iterator component points
// at item and whose bool component is true. if item
// is in the tree, return a pair whose iterator
// component points at the existing item and whose
// bool component is false
// Postcondition: the tree size increases by 1 if item
// is not in the tree
int erase(const T& item);
// if item is in the tree, erase it and return 1;
// otherwise, return 0
// Postcondition: the tree size decreases by 1 if
// item is in the tree
void erase(iterator pos);
// erase the item pointed to by pos.
// Preconditions: the tree is not empty and pos points
// to an item in the tree. if the tree is empty, the
// function throws the underflowError exception. if the
// iterator is invalid, the function throws the
// referenceError exception.
// Postcondition: the tree size decreases by 1
void erase(iterator first, iterator last);
// erase all items in the range [first, last).
// Precondition: the tree is not empty. if the tree
106731499
- 12 //
//
//
//
is empty, the function throws the underflowError
exception.
Postcondition: the size of the tree decreases by
the number of elements in the range [first, last)
iterator begin();
// return an iterator pointing to the first item
// inorder
const_iterator begin() const;
// constant version
iterator end();
// return an iterator pointing just past the end of
// the tree data
const_iterator end() const;
// constant version
void displayTree(int maxCharacters);
// tree display function. maxCharacters is the
// largest number of characters required to draw
// the value of a node
private:
stnode<T> *root;
// pointer to tree root
int treeSize;
// number of elements in the tree
stnode<T> *getSTNode(const T& item,
stnode<T> lptr,stnode<T> *rptr, stnode<T> *pptr);
// allocate a new tree node and return a pointer to it.
// if memory allocation fails, the function throws the
// memoryAllocationError exception
stnode<T> *copyTree(stnode<T> *t);
// recursive function used by copy constructor and ssignment
// operator to assign the current tree as a copy of another ree
void deleteTree(stnode<T> *t);
// recursive function used by destructor and assignment
// operator to delete all the nodes in the tree
stnode<T> *findNode(const T& item) const;
// search for item in the tree. if it is in the tree,
// return a pointer to its node; otherwise, return NULL.
// used by find() and erase()
tnodeShadow *buildShadowTree(stnode<T> *t, int level, int& olumn)
// recursive function that builds a subtree of the shadow ree
// corresponding to node t of the tree we are drawing. level is
// thelevel-coordinate for the root of the subtree, and column is
// the changing column-coordinate of the tree nodes
void deleteShadowTree(tnodeShadow *t);
// remove the shadow tree from memory after displayTree()
// displays the binary search tree
};
106731499
- 13 -
template <typename T>
stnode<T> *stree<T>::getSTNode(const T& item,
stnode<T> *lptr,stnode<T> *rptr, stnode<T> *pptr)
{
stnode<T> *newNode;
// initialize the data and all pointers
newNode = new stnode<T> (item, lptr, rptr, pptr);
if (newNode == NULL)
throw memoryAllocationError("stree: memory allocation
failure");
return newNode;
}
template <typename T>
stnode<T> *stree<T>::copyTree(stnode<T> *t)
{
stnode<T> *newlptr, *newrptr, *newNode;
// if tree branch NULL, return NULL
if (t == NULL)
return NULL;
// copy the left branch of root t and assign its root to newlptr
newlptr = copyTree(t->left);
// copy the right branch of tree t and assign its root to newrptr
newrptr = copyTree(t->right);
// allocate storage for the current root node, assign
// its value and pointers to its left and right subtrees.
// the parent pointer of newNode is assigned when
// newNode's parent is created. if newNode is root,
// NULL is the correct value for its parent pointer
newNode = getSTNode(t->nodeValue, newlptr, newrptr, NULL);
// the current node is the parent of any subtree that
// is not empty
if (newlptr != NULL)
newlptr->parent = newNode;
if (newrptr != NULL)
newrptr->parent = newNode;
return newNode;
}
106731499
// delete the tree stored by the current object
template <typename T>
void stree<T>::deleteTree(stnode<T> *t)
{
// if current root node is not NULL, delete its left subtree,
// its right subtree and then the node itself
if (t != NULL)
{
deleteTree(t->left);
deleteTree(t->right);
delete t;
}
}
// search for data item in the tree. if found, return its node
// address; otherwise, return NULL
template <typename T>
stnode<T> *stree<T>::findNode(const T& item) const
{
// cycle t through the tree starting with root
stnode<T> *t = root;
// terminate on on empty subtree
while(t != NULL && !(item == t->nodeValue))
if (item < t->nodeValue)
t = t->left;
else
t = t->right;
// return pointer to node; NULL if not found
return t;
}
template <typename T>
stree<T>::stree(): root(NULL),treeSize(0)
{}
template <typename T>
stree<T>::stree(T *first, T *last): root(NULL),treeSize(0)
{
T *p = first;
// insert each item in [first, last) into the tree
while (p != last)
{
insert(*p);
p++;
}
}
template <typename T>
stree<T>::stree(const stree<T>& tree): treeSize(tree.treeSize)
{
// copy tree to the current object
root = copyTree(tree.root);
}
- 14 -
106731499
template <typename T>
stree<T>::~stree()
{
// erase the tree nodes from memory
deleteTree(root);
// tree is emtpy
root = NULL;
treeSize = 0;
}
template <typename T>
stree<T>& stree<T>::operator= (const stree<T>& rhs)
{
// can't copy a tree to itself
if (this == &rhs)
return *this;
// erase the existing tree nodes from memory
deleteTree(root);
// copy tree rhs into current object
root = copyTree(rhs.root);
// set the tree size
treeSize = rhs.treeSize;
// return reference to current object
return *this;
}
template <typename T>
stree<T>::iterator stree<T>::find(const T& item)
{
stnode<T> *curr;
// search tree for item
curr = findNode (item);
// if item found, return const_iterator with value current;
// otherwise, return end()
if (curr != NULL)
return iterator(curr, this);
else
return end();
}
- 15 -
106731499
template <typename T>
stree<T>::const_iterator stree<T>::find(const T& item) const
{
stnode<T> *curr;
// search tree for item
curr = findNode (item);
// if item found, return const_iterator with value current;
// otherwise, return end()
if (curr != NULL)
return const_iterator(curr, this);
else
return end();
}
template <typename T>
int stree<T>::empty() const
{
return root == NULL;
}
template <typename T>
int stree<T>::size() const
{
return treeSize;
}
- 16 -
106731499
- 17 -
template <typename T>
pair<stree<T>::iterator, bool> stree<T>::insert(const T& item)
{
// t is current node in traversal, parent the previous node
stnode<T> *t = root, *parent = NULL, *newNode;
// terminate on on empty subtree
while(t != NULL)
{
// update the parent pointer. then go left or right
parent = t;
// if a match occurs, return a pair whose iterator
// component points at item in the tree and whose
// bool component is false
if (item == t->nodeValue)
return pair<iterator, bool> (iterator(t, this),
false);
else if (item < t->nodeValue)
t = t->left;
else
t = t->right;
}
// create the new leaf node
newNode = getSTNode(item,NULL,NULL,parent);
// if parent is NULL, insert as root node
if (parent == NULL)
root = newNode;
else if (item < parent->nodeValue)
// insert as left child
parent->left = newNode;
else
// insert as right child
parent->right = newNode;
// increment size
treeSize++;
// return an pair whose iterator component points at
// the new node and whose bool component is true
return pair<iterator, bool> (iterator(newNode, this), true);
}
106731499
- 18 -
template <typename T>
void stree<T>::erase(iterator pos)
{
// dNodePtr = pointer to node D that is deleted
// pNodePtr = pointer to parent P of node D
// rNodePtr = pointer to node R that replaces D
stnode<T> *dNodePtr = pos.nodePtr, *pNodePtr, *rNodePtr;
if (treeSize == 0)
throw
underflowError("stree erase(): tree is empty");
if (dNodePtr == NULL)
throw
referenceError("stree erase(): invalid iterator");
// assign pNodePtr the address of P
pNodePtr = dNodePtr->parent;
// If D has a NULL pointer, the
// replacement node is the other child
if (dNodePtr->left == NULL || dNodePtr->right == NULL)
{
if (dNodePtr->right == NULL)
rNodePtr = dNodePtr->left;
else
rNodePtr = dNodePtr->right;
if (rNodePtr != NULL)
// the parent of R is now the parent of D
rNodePtr->parent = pNodePtr;
}
// both pointers of dNodePtr are non-NULL.
else
{
// find and unlink replacement node for D.
// starting at the right child of node D,
// find the node whose value is the smallest of all
// nodes whose values are greater than the value in D.
// unlink the node from the tree.
// pOfRNodePtr = pointer to parent of replacement node
stnode<T> *pOfRNodePtr = dNodePtr;
// first possible replacement is right child of D
rNodePtr = dNodePtr->right;
// descend down left subtree of the right child of D,
// keeping a record of current node and its parent.
// when we stop, we have found the replacement
while(rNodePtr->left != NULL)
{
pOfRNodePtr = rNodePtr;
rNodePtr = rNodePtr->left;
}
106731499
- 19 if (pOfRNodePtr == dNodePtr)
{
// right child of deleted node is the replacement.
// assign left subtree of D to left subtree of R
rNodePtr->left = dNodePtr->left;
// assign the parent of D as the parent of R
rNodePtr->parent = pNodePtr;
// assign the left child of D to have parent R
dNodePtr->left->parent = rNodePtr;
}
else
{
// we moved at least one node down a left branch
// of the right child of D. unlink R from tree by
// assigning its right subtree as the left child of
// the parent of R
pOfRNodePtr->left = rNodePtr->right;
// the parent of the right child of R is the
// parent of R
if (rNodePtr->right != NULL)
rNodePtr->right->parent = pOfRNodePtr;
// put replacement node in place of
// assign children of R to be those
rNodePtr->left = dNodePtr->left;
rNodePtr->right = dNodePtr->right;
// assign the parent of R to be the
rNodePtr->parent = pNodePtr;
// assign the parent pointer in the
// of R to point at R
rNodePtr->left->parent = rNodePtr;
rNodePtr->right->parent = rNodePtr;
dNodePtr
of D
parent of D
children
}
}
// complete the link to the parent node.
// deleting the root node. assign new root
if (pNodePtr == NULL)
root = rNodePtr;
// attach R to the correct branch of P
else if (dNodePtr->nodeValue < pNodePtr->nodeValue)
pNodePtr->left = rNodePtr;
else
pNodePtr->right = rNodePtr;
// delete the node from memory and decrement tree size
delete dNodePtr;
treeSize--;
}
106731499
- 20 -
template <typename T>
int stree<T>::erase(const T& item)
{
int numberErased = 1;
// search tree for item
stnode<T> *p = findNode(item);
// if item found, delete the node
if (p != NULL)
erase(iterator(p,this));
else
numberErased = 0;
return numberErased;
}
template <typename T>
void stree<T>::erase(iterator first, iterator last)
{
if (treeSize == 0)
throw
underflowError("stree erase(): tree is empty");
iterator p = first;
if (first == begin() && last == end())
{
// we are asked to erase the entire tree.
// erase the tree nodes from memory
deleteTree(root);
// tree is emtpy
root = NULL;
treeSize = 0;
}
else
// erase each item in a subrange of the tree
while (p != last)
erase(p++);
}
template <typename T>
stree<T>::iterator stree<T>::begin()
{
stnode<T> *curr = root;
// if the tree is not empty, the first node
// inorder is the farthest node left from root
if (curr != NULL)
while (curr->left != NULL)
curr = curr->left;
// build return value using private constructor
return iterator(curr, this);
}
106731499
- 21 -
template <typename T>
stree<T>::const_iterator stree<T>::begin() const
{
const stnode<T> *curr = root;
// if the tree is not empty, the first node
// inorder is the farthest node left from root
if (curr != NULL)
while (curr->left != NULL)
curr = curr->left;
// build return value using private constructor
return const_iterator(curr, this);
}
template <typename T>
stree<T>::iterator stree<T>::end()
{
// end indicated by an iterator with NULL stnode pointer
return iterator(NULL, this);
}
template <typename T>
stree<T>::const_iterator stree<T>::end() const
{
// end indicated by an iterator with NULL stnode pointer
return const_iterator(NULL, this);
}
// recursive inorder scan used to build the shadow tree
template <typename T>
tnodeShadow *stree<T>::buildShadowTree(stnode<T> *t, int level, int&
column)
{
// pointer to new shadow tree node
tnodeShadow *newNode = NULL;
// text and ostr used to perform format conversion
char text[80];
ostrstream ostr(text,80);
if (t != NULL)
{
// create the new shadow tree node
newNode = new tnodeShadow;
// allocate node for left child at next level in tree;
attach node
tnodeShadow *newLeft = buildShadowTree(t->left, level+1,
column);
newNode->left = newLeft;
// initialize data members of the new node
ostr << t->nodeValue << ends; // format conversion
newNode->nodeValueStr = text;
newNode->level = level;
newNode->column = column;
106731499
- 22 // update column to next cell in the table
column++;
// allocate node for right child at next level in tree;
attach node
tnodeShadow *newRight = buildShadowTree(t->right, level+1,
column);
newNode->right = newRight;
}
return newNode;
}
template <typename T>
void stree<T>::displayTree(int maxCharacters)
{
string label;
int level = 0, column = 0;
int colWidth = maxCharacters + 1;
//
int currLevel = 0, currCol = 0;
if (treeSize == 0)
return;
// build the shadow tree
tnodeShadow *shadowRoot = buildShadowTree(root, level, column);
// use during the level order scan of the shadow tree
tnodeShadow *currNode;
// store siblings of each tnodeShadow object in a queue so that
// they are visited in order at the next level of the tree
queue<tnodeShadow *> q;
// insert the root in the queue and set current level to 0
q.push(shadowRoot);
// continue the iterative process until the queue is empty
while(!q.empty())
{
// delete front node from queue and make it the current node
currNode = q.front();
q.pop();
// if level changes, output a newline
if (currNode->level > currLevel)
{
currLevel = currNode->level;
currCol = 0;
cout << endl;
}
// if a left child exists, insert the child in the queue
if(currNode->left != NULL)
q.push(currNode->left);
106731499
- 23 -
// if a right child exists, insert the child in the queue
if(currNode->right != NULL)
q.push(currNode->right);
// output formatted node label
if (currNode->column > currCol)
{
cout << setw((currNode->column-currCol)*colWidth) <<
" ";
currCol = currNode->column;
}
cout << setw(colWidth) << currNode->nodeValueStr;
currCol++;
}
cout << endl;
// delete the shadow tree
deleteShadowTree(shadowRoot);
}
template <typename T>
void stree<T>::deleteShadowTree(tnodeShadow *t)
{
// if current root node is not NULL, delete its left subtree,
// its right subtree and then the node itself
if (t != NULL)
{
deleteShadowTree(t->left);
deleteShadowTree(t->right);
delete t;
}
}
#endif
// BINARY_SEARCH_TREE_CLASS
106731499
- 24 -
#ifndef VIDEO_CLASS
#define VIDEO_CLASS
#include <iostream>
#include <string>
using namespace std;
class video
{
public:
// constructor. initialize film title and numCopies
video(const string& film = "", int copies = 1):
filmTitle(film), numCopies(copies)
{}
// add n to the number of copies. note that if n < 0
// the function decreases the number of copies
void updateCopies(int n){
numCopies += n;
}
// return the number of copies of the film title
int getCopies() {
return numCopies;
}
// two video objects are "equal" if they have the same
title
friend bool operator== (const video& lhs, const video& rhs)
{
return lhs.filmTitle == rhs.filmTitle;
}
// compare video objects by comparing film titles
friend bool operator< (const video& lhs, const video& rhs)
{
return lhs.filmTitle < rhs.filmTitle;
}
// output a video object
friend ostream& operator<< (ostream& ostr,
const video& obj){
ostr << obj.filmTitle << " (" << obj.numCopies << ")" ;
return ostr;
}
private:
// title of the film
string filmTitle;
// number of copies (>= 0)
int numCopies;
};
#endif
// VIDEO_CLASS
106731499
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
- 25 -
File: prg10_5.cpp
the program simulates inventory maintenance for a
video store. the program stores the title of a film
and the number of copies the store owns in a video
object. the video class has functions that access
and update the number of copies, compare objects
by title, and output a title and the number of copies.
the function setupInventory() inputs film titles
from the file "films.dat" and creates the stree
object inventory of video data. after listing the
films in the inventory, in an interactive loop
the clerk inputs whether the customer wishes
to rent a film, return a film, or whether business
is over for the day. when a customer rents a film,
the program updates the inventory by reducing the
number of copies of the film by 1, and adds the film
to the stree object rentals that maintains a database
of rented films. when a customer returns a film, the
program removes 1 copy of the film from the rentals
object and increases the number of copies in inventory
by 1. at the end of the business day, the program outputs
the list of rented films and the films remaining in
the inventory
#include
#include
#include
#include
<iostream>
<fstream>
<string>
<utility>
#include "d_stree.h"
#include "d_video.h"
#include "d_util.h"
// for pair class
// stree class
// video class
// for writeSTree()
using namespace std;
// initialize inventoryList from file "films.dat"
void setupInventory(stree<video>& inventory);
// process the return of a film
void returnTransaction(stree<video>& inventory,
stree<video>& rentals, const string& filmName);
// process the rental of a film
void rentalTransaction(stree<video>& inventory,
stree<video>& rentals, const string& filmName);
106731499
- 26 -
int main()
{
// the inventory and rental lists
stree<video> inventory, rentals;
// assign return value from find() to filmIter
stree<video>::iterator filmIter;
// input from store operator. transactionType = "Rent", "Return",
// or "Done"
string transactionType;
// film requested by a customer
string filmName;
// read and output inventory file
setupInventory(inventory);
cout << "Initial inventory list:" << endl;
writeSTree(inventory, "\n");
cout << endl;
// process customers by entering "Rental" or "Return"
// followed by the film name or "Done" to end the program.
// for "Rent", decrease number of copies in inventory by 1
// and add the copy to the rental database. for "Return",
// remove copy from rental database and increase number
// of copies in inventory by 1
cout << "Transactions: Enter type (Rent, Return, Done)" << endl;
cout << "followed by film name or space if done" << endl << endl;
while (true)
{
// input the transaction type. the input must terminate
with
// a blank
cout << "Transaction: ";
getline(cin, transactionType, ' ');
// if "Done", terminate the loop
if (transactionType == "Done")
break;
getline(cin, filmName,'\n'); // get film name
if (transactionType == "Return")
returnTransaction(inventory, rentals, filmName);
else
rentalTransaction(inventory, rentals, filmName);
}
cout << endl;
// output the final rental and inventory lists.
cout << "Rented Films: " << endl << endl;
writeSTree(rentals, "\n");
cout << endl;
cout << "Films Remaining in Inventory:" << endl << endl;
writeSTree(inventory, "\n");
return 0;
}
106731499
- 27 -
void setupInventory(stree<video>& inventory)
{
ifstream filmFile;
// input stream
string filmName;
// individual file names
// use with stree insert()
pair<stree<video>::iterator, bool> p;
// open the file "films.dat"
filmFile.open("films.dat");
if (!filmFile)
{
cerr << "File 'films.dat' not found!" << endl;
exit(1);
}
// read lines until EOF; insert names in inventory list
while(true)
{
getline(filmFile,filmName,'\n');
if (!filmFile)
break;
// try an insertion with default of 1 copy
p = inventory.insert(video(filmName));
// see if video already in the inventory
if (p.second == false)
// it is in the inventory. increment number of copies
(*(p.first)).updateCopies(1);
}
}
void returnTransaction(stree<video>& inventory,
stree<video>& rentals, const string& filmName)
{
stree<video>::iterator filmIter;
// locate the film in the return database
filmIter = rentals.find(video(filmName));
// if there is only 1 copy left, erase the entry;
// otherwise, decrease the number of rented copies
// by 1
if ((*filmIter).getCopies() == 1)
rentals.erase(filmIter);
else
(*filmIter).updateCopies(-1);
// locate the film in the inventory and increase the
// number of copies available by 1
filmIter = inventory.find(video(filmName));
(*filmIter).updateCopies(1);
}
106731499
- 28 -
void rentalTransaction(stree<video>& inventory,
stree<video>& rentals, const string& filmName)
{
stree<video>::iterator filmIter;
// use pObj with stree insert()
pair<stree<video>::iterator,bool> pObj;
// is film available?
filmIter = inventory.find(video(filmName));
if ( filmIter == inventory.end())
// film is not in the store's inventory
cout << "Film " << filmName << " is not in inventory" <<
endl;
else if ((*filmIter).getCopies() == 0)
// all copies are checked out
cout << "All copies of " << filmName << " are checked out"
<< endl;
else
{
// decrease the number of copies in the inventory
// by 1
(*filmIter).updateCopies(-1);
// attempt to insert the film into rentalList. if it is
// inserted, the number of copies will be 1
pObj = rentals.insert(video(filmName));
// if film not inserted, increase number of rented copies
// by 1
if (pObj.second == false)
(*(pObj.first)).updateCopies(1);
}
}
/*
Run:
Initial inventory list:
Frequency (1)
Gladiator (2)
Lord of the Rings (4)
U-571 (2)
Transactions: Enter type (Rent, Return, Done)
followed by film name or space if done
Transaction: Rent Gladiator
Transaction: Rent Frequency
Transaction: Rent Shaft
Film Shaft is not in inventory
Transaction: Rent Frequency
All copies of Frequency are checked out
Transaction: Done
Rented Films:
Frequency (1)
106731499
Gladiator (1)
Films Remaining in Inventory:
Frequency (0)
Gladiator (1)
Lord of the Rings (4)
U-571 (2)
*/
- 29 -
106731499
- 30 -
Associative containers
Associative containers store and retrieve data by value rather than by position.
A set is a collection of keys where each key is unique.
A map is a collection of key-value pairs that associate a key with a value. In a map there is only one value
associated with a key. A map is often called an associative array because applying the index operator with
the key as its argument returns the value associated with the key.
Multimaps and multiset containers allow multiple occurrences of a key
The STL set class uses one template argument to refer to the key type.
The STL map class requires two template arguments (one for the key and the other for the value type)
A binary search tree is an associative structure and is ideal for the implementation of sets and maps.
Download