Please send questions to
st10@humboldt.edu .
//-----------------------------------------------------------
// File: set.template
// Name: Michael Main, Walter Savitch
// (adapted by Sharon M. Tuttle)
// last modified: 4-28-05
//
// Class implemented: set (see set.h for documentation)
// (a container template class for a collection of
// items with NO duplicates)
//
// INVARIANT for the set template class:
// 1. The number of items in the set is in the
// member variable used;
// 2. The actual items of the set are stored in a partially
// filled array. The array is a dynamic array, pointed to
// by the member variable data;
// 3. The size of the dynamic array is in the member
// variable capacity.
// 4. If there is a current item, then current_index lies in the
// range of [0 .. used-1]; otherwise, current_index == used.
//--------------------------------------------------------------
#include <algorithm> // Provides copy function
#include <cassert> // Provides assert function
#include "set.h"
using namespace std;
// (note that the following is required for the named constant)
//
//template <typename Item>
//const typename size_t set<Item>::DEFAULT_CAPACITY;
/********************************************************/
/* CONSTRUCTORS and DESTRUCTOR */
/********************************************************/
// postcondition: creates an empty set instance with an
// initial capacity of DEFAULT_CAPACITY
//
template <typename Item>
set<Item>::set()
{
data = new Item[DEFAULT_CAPACITY];
capacity = DEFAULT_CAPACITY;
used = 0;
current_index = used;
}
// Copy constructor
//
// Library facilities used: algorithm
//
template <typename Item>
set<Item>::set(const set<Item>& source)
{
data = new Item[source.capacity];
capacity = source.capacity;
used = source.used;
// (copy is from the algorithm library)
copy(source.data, source.data + used, data);
// in the spirit (I think) of the discussion on the course
// text, p. 297, the copy's iterator will NOT be
// set (user needs to call start( ) for the copy
// to start up an iteration)
current_index = used;
}
// destructor
//
template <typename Item>
set<Item>::~set( )
{
delete [ ] data;
}
/*************************************************************/
/* ACCESSORS and other constant member functions (observers) */
/*************************************************************/
// get_size
//
// postcondition: returns the number of items in the set.
//
template <typename Item>
size_t set<Item>::get_size( ) const
{
return used;
}
// contains
//
// postcondition: returns true if the target is in the set,
// and returns false if it is not.
//
//
template <typename Item>
bool set<Item>::contains(const value_type& target) const
{
size_t i;
// check each item of set, seeing if it is the target;
// (if it is, we are DONE, the set DOES contain the
// target!)
for (i = 0; i < used; i++)
{
if (target == data[i])
{
return true;
}
}
// IF reach here, target was not found in set
// (otherwise, we would have returned from inside
// the loop)
return false;
}
// is_item
//
// postcondition: returns true if there is a valid "current"
// item that may be returned by the current member function.
// Returns false if there is no valid current item.
//
template <typename Item>
bool set<Item>::is_item() const
{
return (current_index != used);
}
// current
//
// precondition: is_item() == true
// postcondition: returns the current item in the set's
// internal iterator.
//
//
template <typename Item>
Item set<Item>::current() const
{
assert(is_item() == true);
return data[current_index];
}
/****************************************************/
/* MODIFIERS and other modifying member functions */
/****************************************************/
// remove
//
// postcondition: if target was in the set, remove it and
// return true; otherwise, return false (and the set is
// unchanged)
//
template <typename Item>
bool set<Item>::remove(const value_type& target)
{
size_t index; // The location of target in the data array
// First, set index to the location of target in the data
// array, which could be as small as 0 or as large as used-1.
// If target is not in the array, then index will be set
// equal to used.
index = 0;
while ((index < used) && (data[index] != target))
{
index++;
}
// if target isn't in the set, then there's no work to do
if (index == used)
{
return false;
}
// IF execution reaches here, target is in the set at
// data[index]. So, reduce used by 1 and copy the
// last item onto data[index].
used--;
data[index] = data[used];
// in the spirit of the discussion on p.297 of Savitch
// and Main, the iterator should probably become
// invalid when the container changes (because of
// this removal, in this case).
// Now, if there WAS no current iteration going on,
// we'd need to make sure that the class invariant
// held by assigning (the new) used to current_index.
// BUT, given the p.297 discussion, we'd want to make
// a current iteration --- IF there was one ---
// invalid. And how would we do that? By setting
// current_index to used!
// SO... in EITHER case:
current_index = used;
// and, if reach here --- DID remove an item from set, so:
return true;
}
// add
//
// postcondition: if entry is not already in the set,
// then add it to the set; otherwise, set is unchanged.
//
template <typename Item>
void set<Item>::add(const value_type& entry)
{
size_t new_capacity; // if need to "expand" array
Item* larger_array; // "
// if entry is already in the set, we're done;
// no change is required.
if (contains(entry))
{
return;
}
// IF reach here, entry is NOT in set, and should be
// added.
// increase set's capacity if necessary
if (used == capacity)
{
new_capacity = capacity + INCR_AMT;
larger_array = new Item[new_capacity];
copy(data, data + used, larger_array); // from algorithm library
// (notice that we are freeing the "old" array...)
delete [ ] data;
data = larger_array;
capacity = new_capacity;
}
data[used] = entry;
used++;
// in the spirit of the discussion on p.297 of Savitch
// and Main, the iterator should probably become
// invalid when the container changes (because of
// this insertion, in this case).
// Now, if there WAS no current iteration going on,
// we'd need to make sure that the class invariant
// held by assigning (the new) used to current_index.
// BUT, given the p.297 discussion, we'd want to make
// a current iteration --- IF there was one ---
// invalid. And how would we do that? By setting
// current_index to used!
// SO... in EITHER case:
current_index = used;
}
// start
//
// postcondition: one of the items in the set becomes
// the current item (but if the set is empty, there
// is no current item)
//
template <typename Item>
void set<Item>::start()
{
if (get_size() == 0)
{
return; // if set is empty, there is no current item
}
else
{
// note that, given the definition of a set, I could
// start the internal iterator wherever I want ---
// but I'm making it easy and starting at index 0.
current_index = 0;
}
}
// advance
//
// precondition: is_item() == true
// postcondition: if the current item is the last in the
// set's internal iterator, then there is no longer
// any current item. Otherwise, the new current item
// is another set element that has not yet been current
// during this internal iterator.
//
template <typename Item>
void set<Item>::advance()
{
assert(is_item() == true);
// have we already "seen" all of the items in the current
// internal iterator? If so, then there is no longer
// any current item.
if (current_index == (used-1))
{
current_index = used; // see set invariant #4
// (yes, I COULD have just added
// one here, too. Maybe I should
// have. But I want to make the
// point that with a different
// invariant, this COULD be a
// separate case.)
}
// otherwise, we'd like the new current item to be another
// set element that has not yet been current during this
// internal iterator. Keeping this simple, then:
else
{
current_index++;
}
}
/*************************************************************/
/* OTHER METHODS */
/*************************************************************/
// postcondition: this returns a new set whose membership is
// the set-theoretic (classic) union of the calling set
// and <set2>
//
template <typename Item>
set<Item> set<Item>::unionWith(set<Item> set2)
{
set<Item> result_set;
// will start result off with the values in the passed set
result_set = set2;
// now, only add those elements of the calling set that
// are not already in the result
for (start(); is_item(); advance())
{
if (! result_set.contains( current() ) )
{
result_set.add( current() );
}
}
return result_set;
}
// postcondition: this returns a new set whose membership
// is the set-theoretic (classic) intersection of the
// calling set and <set2>
//
template <typename Item>
set<Item> set<Item>::intersect(set<Item> set2)
{
set<Item> result_set;
// now, only add those elements of the calling set that
// are also in set2
for (start(); is_item(); advance())
{
if (set2.contains(current()) )
{
result_set.add( current() );
}
}
return result_set;
}
/****************************************************/
/* OVERLOADED OPERATORS */
/****************************************************/
// +=
//
// postcondition: for each item in addend:
// * if that item is not already in this set, it is
// added to this set.
// * if that item is already in this set, then
// this set is unchanged.
//
// Library facilities used: algorithm
//
template <typename Item>
void set<Item>::operator +=(const set<Item>& addend)
{
size_t i;
// increase set capacity if necessary
if (used + addend.used > capacity)
{
reserve(used + addend.used);
}
// should ONLY add addend items to set if they
// are not already there; but operation insert
// DOES check for this (and it will update used
// appropriately, too! 8-) )
for (i=0; i<addend.used; i++)
{
insert(addend.data[i]);
}
// we just changed used, so we need to again make sure that
// current_index still == used;
// AND, in the spirit of Savitch and Main p. 297, we should
// probably invalidate any current iteration in the
// face of this change to the container; so, in either
// case:
current_index = used;
}
// =
//
// postcondition: the set that activated this function
// has the same items and capacity as source.
//
// Library facilities used: algorithm
//
template <typename Item>
void set<Item>::operator =(const set<Item>& source)
{
value_type *new_data;
// Check for possible self-assignment:
if (this == &source)
{
return;
}
// If needed, allocate an array with a different size:
if (capacity != source.capacity)
{
new_data = new value_type[source.capacity];
delete [ ] data;
data = new_data;
capacity = source.capacity;
}
// Copy the data from the source array:
used = source.used;
copy(source.data, source.data + used, data); // from algorithm lib
// we just (maybe) changed used, so we need to again make sure that
// current_index still == used;
// AND, in the spirit of Savitch and Main p. 297, we should
// probably invalidate any current iteration in the
// face of this change to the container; so, in either
// case:
current_index = used;
}
/*************************************************/
/* NONMEMBER FUNCTIONS for the set class */
/*************************************************/
// +
//
// postcondition: the set returned is the union of
// s1 and s2.
//
template <typename Item>
set<Item> operator +(const set<Item>& s1, const set<Item>& s2)
{
set<Item> answer(s1.size() + s2.size());
answer += s1;
answer += s2;
return answer;
}