Associative containers
Sets & Multisets
These containers sort their elements automatically
according to a certain sorting criterion
The difference is that multisets
allow duplicates; sets do not
Implemented
as balanced binary search trees (specifically, red-black trees)
As
the elements are inserted, they are put into a red-black tree
The value of a left child of an element is always less
than the parent
The value of a right child is always greater than the
parent
Sets do not allow duplicates
When
an iterator iterators over the elements, it does an inorder
transversal
An
inorder traversal visits node n after all the
nodes in the left subtree of n and before all the nodes in the right
subtree of n
Example using the set container
#include
<iostream> #include <set>
int main()
{
set<int>
coll; // instantiate a set container for int values
// insert elements from
1 to 6 in arbitrary order value 1 gets
inserted twice
coll.insert(3);
coll.insert(1);
coll.insert(5);
coll.insert(4);
coll.insert(1);
coll.insert(6);
coll.insert(2);
// print all
elements * - iterate over all
elements
set<int> set<int>
::const_iterator pos;
// a constant
iterator is read only
for (pos = coll.begin(); pos != coll.end();
++pos)
{ cout
<< *pos << ' '; }
cout << endl; }
Maps & Multimaps
These containers manage key/value pairs as elements
They also sort their elements automatically according
to some sorting criterion that is used for the key
The difference is that maps do not allow duplicates,
while multimaps do
Pairs
The class pair is provided to treat two values as a
single unit.
It has two data members, first and second
It is used by maps and multimaps
to manage their elements
One of its member functions is make_pair
which creates a pair on the fly.
Note about Associate containers
To
modify the value of an element, you must remove the old element and insert a
new one
If you want to remove elements from an associative
container, you must use the member functions
Algorithms
that remove elements or
that reorder or modify elements cannot be used with associative
containers
Trying will result in a compiler error
This is because if they could change the value or
position of elements they would not be sorted anymore
Maps and Multimaps
Must #include <map> to use
The elements of the map or multimap
must be assignable, copyable and comparable
template < class Key, class value,
class Compare = less<T>
class Allocator
= allocator<pair<const Key,T>
>
class map;
Notice that there are four type parameters to
this template class
By
default, the function object less<T> sorts the elements by
comparing them with the < operator
So to use the default, your class must overload
operator <
Sorting your multimap
Functors: a
predefined functor less<T> is the
default in the class template definition
Sorts
the container in ascending order
This
can be replaced by a different predefined functor
When you call the constructor for the map or multimap, you can either accept the default sorting
criteria, or specify greater<T>
Examples
multimap<int,
string> IntStringMMap coll;
This will accept the default, and sort in ascending order
multimap<int, string,
greater<int> > IntStringMMap
coll;
This will specify to sort in descending order
The pair class
Pairs: each element in maps and multimaps is a pair
The
first part of the pair is a key, by which the item is sorted and put
into the BST
The
second part of the pair is the value (often a class or struct)
The
key of the elements inside a map is constant, but the value
is not
Also used when you have a
function that you want to return two values.
The pair class template
Must #include <utility> to use
the pair class
template<class T1, classT2 >
struct pair
{ T1 first; T2 second;
//data members
pair(); //constructors
pair(const T1& key, const
T2& value)
}
Example of use (calls the constructor):
pair<int,
string>(34, Hi)
This is defined as a struct
so the data members will be public
Function make_pair( )
The
template function make_pair
makes it easier to create pairs
make_pair
takes two objects as arguments, and returns an object of type pair
Example: make_pair(56, 3.14159)
This
is the same as
pair<int, double>(56,
3.14159)
This
enables your to create a value pair without writing
the types explicitly.
Unqualified floating literals have type double
Using pairs
Create an object of type pair:
make_pair(1649, whatever) myPair;
Access
the data members of the pair:
string
st = myPair.second;
int myKey = myPair.first;
Searching
Maps
sort their elements automatically according to the elements keys
Searching
for elements with certain keys have good performance but
Searching for elements with certain value have bad performance.
Special search operations
count (elem)
returns the number of element with value elem
find (elem) returns an iterator where the first
occurrence of elem is found
lower_bound (elem) returns an iterator for
the first element that has the same or greater value than elem
upper_bound (elem) returns an iterator for
the first element that has a greater value than elem
equal_range(elem) returns a pair, the
first and last position where elem would get inserted
Search operation for values
If
you want to search for a value, not a key then you cant use member
function find( )
You
can use the general algorithm find_if( ) or program it yourself as
follows
Multimap<string, float>::iterator pos;
For (pos = coll.begin();
pos != coll.end(); ++pos)
{ if (pos->second
== value)
{do_something( ); }
}
The
iterator returned is pointing to a pair, not a single item
Examples using a maps
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
// type of the
collection
typedef multimap<int,string> IntStringMMap;
IntStringMMap
coll; // set container for int/string
values
// insert some elements
in arbitrary order a
value with key 1 gets inserted twice
coll.insert(make_pair(5,"tagged"));
coll.insert(make_pair(2,"a"));
coll.insert(make_pair(1,"this"));
coll.insert(make_pair(4,"of"));
coll.insert(make_pair(6,"strings"));
coll.insert(make_pair(1,"is"));
coll.insert(make_pair(3,"multimap"));
// print all element values
- iterate over all
element member second is the value
IntStringMMap::iterator pos;
for (pos = coll.begin(); pos != coll.end(); ++pos)
{ cout
<< pos->second << ' '; }
cout
<< endl;}
Another map example *************************************************
#include <string>
#include <map>
#include <iostream>
using namespace std;
// this is just
to give you an idea of the syntax necessary to get the map (or multimap) to work
// with objects
as both the key and value. The code is
very incomplete
class personName // objects of this type will be the key
{
private:
string
const lastName; string const firstName;
public:
personName(string
first="", string last=""):
lastName(last),firstName(first){
} //constructor
bool operator < (personName
p) const
{ if (lastName == p.lastName)
return firstName < p.firstName;
else
return lastName < p.lastName;
}
}; // end presonName
class
class personInfo // objects of this type will be the value
{
private:
string
phone;
string
address;
public:
personInfo( ) { phone=""; address="";}// default constructor
personInfo(string ph, string
ad) {
phone=ph; address=ad;} // constructor
};
int main ( )
{
map<personName,
personInfo> adBook; // the types of the pair
map<personName,
personInfo>::iterator pos;
personName
tempP("Anne", "DeFrance");// getting information for the key
personInfo
tempI("585-2693", "601 W. Arnold"); // getting information for the value
//this is an alternative
to using make_pair
// pair<personName
const, personInfo> onePair(tempP,
tempI);
// adBook.insert(onePair);
//
or
adBook.insert(make_pair(tempP,
tempI)); // this uses make_pair insert
the data into the multimap
return 0;
}
Access to elements
The usual way to access elements in a map is via
iterators
But, maps can also be used as an associative
array
An
associative arrays index can be any type, not just an integer
You
cannot have a wrong index with an associative array
If you use a key as the index, and that does not
exist, it will be created and inserted into the map
This
is the one associative container that allows direct access with the subscript
operator
Look at using a map as an associative array and
as a dictionary
Maps and Multimaps can be used as associative arrays
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
typedef map<string,float> StringFloatMap;
StringFloatMap
stocks; // create empty container
stocks["BASF"]
= 369.50; // insert some elements
stocks["VW"] = 413.50;
stocks["Daimler"] = 819.00;
stocks["BMW"] = 834.00;
stocks["Siemens"] = 842.20;
//
print all elements
StringFloatMap::iterator pos;
for (pos = stocks.begin(
); pos != stocks.end( ); ++pos) {
cout
<< "stock: " << pos‑>first <<
"\t"
<< "price: " << pos‑>second << endl;
} cout
<< endl;
// boom (all prices doubled)
for (pos = stocks.begin();
pos != stocks.end(); ++pos)
{ pos‑>second
*= 2; }
// print all elements
for (pos = stocks.begin();
pos != stocks.end(); ++pos) {
cout << "stock: " << pos‑>first
<< "\t"
<< "price: " << pos‑>second << endl;
} cout
<< endl;
// rename key from "VW" to
"Volkswagen"
stocks["Volkswagen"] =
stocks["VW"];
stocks.erase("VW");
// print all elements
for (pos = stocks.begin();
pos != stocks.end(); ++pos) {
cout
<< "stock: " << pos‑>first <<
"\t"
<< "price: " << pos‑>second << endl;
}
}
********* Other information about the STL not covered in
class***********************
Making the STL more generic
The algorithms arguments are of a specific
type
sometimes you want to use the algorithm, but your argument is of a
different type than expected
Container
adaptors;
stacks,
queues,
priority queues (heaps)
Container adapters :
The
container adapters are
stacks
queues
priority queues
They
are not containers but
classes that provide a limited subset of container operations
They
are implemented in terms of an underlying container
Stacks
There
is no way to access any of a stacks elements except for the top element; stack
does not allow iteration through its elements
This
restriction is the only reason stack exists, since you could implement a stack
with a sequence container, using back, push_back, and
pop_back
Implementing a stack
The
default stack is implemented using a deque
Template
<class T, class Containter = deque<T> >
class stack;
Member
function of stack
push( ), pop( ), top( ), empty( ), size( )
The
stack implementation maps these operations to function calls to the deque
container
top( ) & pop( )
pop( ) does not return a value because it is inefficient to
do so.
If
you want to process the top of the stack after it is popped you must use top( )
This
is true for all three container adapters, queues and priority queues as well a
stacks
Queues
A
queue is a first in first out data structure
By
default uses a deque as the underlying container
There
is no way to access any of a queues elements except for the ones at the front
and back; it has no iterators
The
only reason to use the container adaptor queue rather than deque is to make it
clear you are using only queue operations.
Member functions of queue
push( ) inserts an element at the back of the queue
back( ) returns the last element in the queue
pop( ) removes the element at the front of the queue
front( ) returns the element at the front of the queue
size( ) returns the actual number of elements
empty( ) returns true when the queue is empty