Upload
poppy-hodges
View
225
Download
4
Embed Size (px)
Citation preview
Containers and Iterators
The safe array class VectorInfo is an example of a“container” class:
#include <iostream.h>#include <assert.h>
typedef int Integer;typedef Integer * IntegerArray;typedef class VectorInfo * Vector;
class VectorInfo {private: IntegerArray p; Integer size;public: VectorInfo(Integer n); ~VectorInfo(); Integer& element(Integer i);};
VectorInfo Class ImplementationVectorInfo::VectorInfo(Integer n){ assert ( n >= 1 ); size = n; p = new Integer[size]; assert (p != 0); for (Integer i = 0; i < n; i++) { // for demo purposes p[i] = i; // we fill array } // with index values}
VectorInfo::~VectorInfo() { delete [] p; }
Integer& VectorInfo::element(Integer i){ assert (i >= 0 && i <= size); return (p[i]);}
VectorInfo Class ApplicationFind the median value in a sorted Vector of n valuesby moving pointers from endpoints until they meet:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
front back0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
front back
backfront......
VectorInfo Class Application (cont'd)
Vector a = new VectorInfo(n);
Integer front_index = 0; Integer back_index = n;
Integer front = a->element(front_index++); Integer back = a->element(--back_index);
while ( front < back ) { cout << setw(5) << front << setw(5) << back << endl; front = a->element(front_index++); back = a->element(--back_index); } cout << "Middle value is " << front << endl;
Program Output for n = 15
6% a.out 0 14 1 13 2 12 3 11 4 10 5 9 6 8Middle value is 77%
Decoupling Index Handling from Loops
● The example has 6 places where Vector loop indexes have to be (confusingly!) managed:– front_index points directly to the next value– back_index does not– front_index is incremented after its use– back_index is decremented before its use
● Therefore, 6 opportunities for errors● Good OOP style prefers to keep index management
hidden from the implementor of the loop
Decoupling Index Handling: First Try
Instead of: Integer n = 15; Vector a = new VectorInfo(n); for (Integer i = 0; i < n; i++) cout << setw(4) << a->element(i); cout << endl;
Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
We would like: Integer n = 15; Vector a = new VectorInfo(n); for (Integer i = 0; i < n; i++) cout << setw(4) << a->next(); cout << endl;
Revised VectorInfo Class Definition
class VectorInfo {private: IntegerArray p; Integer size; Integer index; // Store index as part // of the objectpublic: VectorInfo(Integer n); ~VectorInfo(); Integer& element(Integer i); Integer& next(); // Let this method keep}; // track of the index
Revised VectorInfo Methods
VectorInfo::VectorInfo(Integer n){ assert ( n >= 1 ); size = n; p = new Integer[size]; assert (p != 0); for (Integer i = 0; i < n; i++) { p[i] = i; } index = 0; // Point index to beginning}
Integer& VectorInfo::next() { // Note circular if (index == size) // visitation return element(index = 0); else return element(index++);}
Eliminating Index Handling Entirely
Integer n = 15; Vector a = new VectorInfo(n); while ( a->hasNext() ) cout << setw(4) << a->next(); cout << endl;
Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
New VectorInfo Class
class VectorInfo {private: IntegerArray p; Integer size; Integer index; public: VectorInfo(Integer n); ~VectorInfo(); Integer& element(Integer i); Integer& next(); Boolean hasNext(); };
Boolean VectorInfo::hasNext() { return (index < size); }
Trade-Offs● Advantage: index handling is performed by the class,
so coding is safer● Disadvantage 1: container class is beginning to not
have a single purpose● Disadvantage 2: Consider original example, in which
two indexes are needed:– front_index– back_index– However, as it stands, visitation over a Vector object is
restricted to one use, since there is one index– Like restricting a museum to one patron at a time
Possible Remedies
● Create multiple copies of the object– like creating multiple musems for multiple patrons
● Add multiple indexes to the class definition– How many? Can't know in advance– Makes container class even more complex
● Best solution: create an iterator class:– Sole purpose is to handle visitation of a container object– Multiple iterators can be created for one container– Like letting anyone in to the museum without knowing
how many
Iterator Classes
● Iterator classes handle the details of container visitation:– keep track of current index– implement hasNext() method– implement next() method
● Iterator class object must contain a pointer to the container it is iterating over
● When more than one iterative use of a container is needed, create more iterator objects (not more containers)
Class Diagram
VectorInfo
p: IntegerArrarysize: Integer
VectorInfo(n: Integer)~VectorInfo()element(i: Integer): IntegergetSize(): Integer
VectorIterInfo
index: Integer
VectorIterInfo(v: Vector)next(): IntegerhasNext(): Boolean
Vector *
VectorIterInfo Implementation
VectorIterInfo::VectorIterInfo(Vector v) { vector = v; index = 0;}
Integer& VectorIterInfo::next() { if ( index == vector->getSize() ) return vector->element(index = 0); else return vector->element(index++); }
Boolean VectorIterInfo::hasNext() { return (index < vector->getSize()); }
VectorIterInfo Example
Vector a = new VectorInfo(15); VectorIter iter = new VectorIterInfo(a); while ( iter->hasNext() ) cout << setw(4) << iter->next(); cout << endl;
Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Using Multiple Iterators
Vector a = new VectorInfo(5); VectorIter iter1 = new VectorIterInfo(a); VectorIter iter2 = new VectorIterInfo(a);
while ( iter1->hasNext() ) { Integer outer = iter1->next(); while ( iter2->hasNext() ) { Integer inner = iter2->next(); cout << setw(5) << outer * inner; } cout << endl; iter2->next(); } cout << endl; 0 0 0 0 0
0 1 2 3 4 0 2 4 6 8 0 3 6 9 12 0 4 8 12 16
Output:
Enhancing the VectorIterInfo Class
● Our initial example (finding median value in a sorted vector) requires iterating backward as well as forward
● So we can add the methods:– previous()– hasPrevious()
● We also need the ability to reset the index to a desired value (not just 0):– reset(Integer)
● We will also eliminate circular visitation
New VectorIterInfo Class
class VectorIterInfo { private: Vector vector; Integer index; public: VectorIterInfo(Vector v); Integer& next(); Integer& previous(); Boolean hasNext(); Boolean hasPrevious(); void reset(Integer i);};
New VectorIterInfo Methods
Integer& VectorIterInfo::previous() {// Note no circular if ( index == 0 ) // visitation. This throw "There is no previous."; // throw should be else // caught by the app return vector->element(--index); }
Boolean VectorIterInfo::hasPrevious() { return (index > 0); }
void VectorIterInfo::reset(Integer i) { index = i;}
Note that the next() method should also throw if there is no next element.
Original Example Again Integer n = 15; Vector a = new VectorInfo(n); VectorIter forward = new VectorIterInfo(a); forward->reset(0); VectorIter backward = new VectorIterInfo(a); backward->reset(n);
Integer front = forward->next(); Integer back = backward->previous();
while ( front < back ) { cout << setw(5) << front << setw(5) << back << endl; front = forward->next(); back = backward->previous(); } cout << "Middle value is " << front << endl;
This code should be enclosed in a try block with anappropriate catch clause.
Output
0 14 1 13 2 12 3 11 4 10 5 9 6 8Middle value is 7
Application: quickSort
10
716
4 918
-3 812
-3 4 7 8 910
12
16
18
Input:
Output:
Constraints: Perform the sort ``in place'' (no other memory is used)
quickSort Trace
10
716
4 9
comparisonelement
from to
18
-3 812
quickSort Trace (cont'd)
10
716
4 9
comparisonelement
from to
18
-3 812
18
16
12-3 7 8 4
109
partitioneverything less thancomparison element
everything greater thanor equal to comparison element
quickSort Trace (cont'd)
10
716
4 9
comparisonelement
from to
18
-3 812
18
16
12-3 7 8 4
109
partition
-3 7 8 4 9
partition16
12
18
partition
everything less thancomparison element
everything greater thanor equal to comparison element
quickSort Trace (cont'd)
10
716
4 9
comparisonelement
from to
18
-3 812
18
16
12-3 7 8 4
109
partition
-3 7 8 4 9
partition
4 7 8 9
16
12
18
partition
12
16
swappartition
everything less thancomparison element
everything greater thanor equal to comparison element
quickSort Trace (cont'd)
10
716
4 9
comparisonelement
from to
18
-3 812
18
16
12-3 7 8 4
109
partition
-3 7 8 4 9
partition
4 7 8 9
16
12
18
partition
12
16
swap
-3 4 7 8 910
12
16
18
partition
everything less thancomparison element
everything greater thanor equal to comparison element
final sorted vector
The partition Procedure
10
716
4 9
comparisonelement
from to
18
-3 812
10
716
4 918
-3 812
iterate up until out-of-place element found:
10
716
4 918
-3 812
iterate down until out-of-place element found:
10
7 8 4 918
-316
12
swap:
10
7 8 4 9 -318
16
12repeat:
-3 7 8 4 910
18
16
12
swap comparison elementwith appropriate element:
A SortInfo Class
class SortInfo {public: void quickSort(Vector v, Integer from, Integer to);private: void swap(Integer& i, Integer& j); Integer partition(Vector v, Integer from, Integer to);};
Example Using SortInfo Integer n; cout << "Enter Size: "; cin >> n; cout << "\nEnter elements: ";
Vector v = new VectorInfo(n); VectorIter front = new VectorIterInfo(v); front->reset(0);
while ( front->hasNext() ) { cin >> front->getItem(); front->next(); } cout << endl;
v->print(); Sort s = new SortInfo(); s->quickSort(v, 0, n-1); v->print();
Example Output
Enter Size: 9
Enter elements: 10 7 16 4 9 18 -3 8 12
10 7 16 4 9 18 -3 8 12 -3 4 7 8 9 10 12 16 18
Implementing quickSort with Iterators
● quickSort is easy to write recursively● The more complex procedure is partition,
which we will implement with iterators● Changes necessary for VectorInfo:
– Add print() method● Changes necessary for VectorIterInfo:
– Add getIndex() accessor– Add getItem() that returns the item at the current
index
SortInfo::quickSort Method
void SortInfo::quickSort(Vector v, Integer from, Integer to) { Integer mid; if (from < to) { if (from == to - 1) { //2 elements if ( v->element(from) > v->element(to) ) swap(v->element(from), v->element(to)); } else { mid = partition(v, from, to); quickSort(v, from, mid - 1); quickSort(v, mid + 1, to); } }}
SortInfo::Partition MethodInteger SortInfo::partition(Vector v, Integer from, Integer to) { VectorIter front = new VectorIterInfo(v); VectorIter back = new VectorIterInfo(v); Integer compare;
back->reset(to); front->reset(from); compare = front->next(); //comparison element
while (front->getIndex() < back->getIndex()) {
// Search forward while ((front->getIndex() < back->getIndex()) &&(compare > front->getItem())) front->next();
// Search backward while ((front->getIndex() < back->getIndex()) &&(compare <= back->getItem())) back->previous();
// Exchange items swap(front->getItem(), back->getItem()); } ...
SortInfo::Partition Method (cont'd)
... //insert mid position comparison element
if (compare >= front->getItem()) { swap(v->element(from), front->getItem()); return (front->getIndex()); } else { // in case pointers crossed in last iteration swap(v->element(from), v->element(front->getIndex() - 1) ); return (front->getIndex() - 1); }}
Analysis of quickSort
We will count the number of data movements thatquickSort does.
To do this we will use a recursion tree. We start withthe original vector as root:
0 N-1
Analysis of quickSort (cont'd)
0 N-1
After one call to partition:
Q: What is the maximum number of data movements necessary by this call to partition?
A: Movements per swap * Maximum swaps = 3 * N = O(N)
Analysis of quickSort (cont'd)
0 N-1
After two more calls to partition:
Q: What is the maximum number of data movements necessary for the second level?
A: movements per swap * maximum swaps = 3 * (2 * N/2) = 3 * N O(N)
Analysis of quickSort (cont'd)
● There are O(N) data movements per level of the recursion tree
● So the total number of data movements is:– O(N) * the number of levels
● How many levels are there?– Answer: the height of the recursion tree, which
depends upon the nature of the original data
Assuming Vector Values are Randomly Distributed0 N-1
Q: What is the height of this tree?
Assuming Vector Values are Randomly Distributed (cont'd)
N Levels 2 1 4 2 8 316 4 ... N log2N
Assuming Vector is Already Sorted or Nearly Sorted
0 N-1
. . .
.
.
.
Q: What is the height of this tree?A: N
Analysis of quickSort (Summary)
● The total number of data movements done by quickSort is:– O(Nlog(N)) if vector values are randomly distributed– O(N2) if vector is already sorted or nearly sorted
● In worst case, quickSort is no better than bubble sort, insertion sort, or selection sort
● In average case, quickSort is among the best● Analysis can proceed by counting other actions,
like comparisons, but results are similar
Efficiency Comparisons
0 N
O(N)array implementationof priority queue
O(logN)binary heapimplementationof priority queue
O(bN)minimaxValue
time
O(NlogN)quicksort
O(N2)bubblesort