KEMBAR78
C++ Vector and Iterator Guide | PDF | C++ | Pointer (Computer Programming)
0% found this document useful (0 votes)
41 views36 pages

C++ Vector and Iterator Guide

1) STL vectors can dynamically resize unlike C++ arrays. Principal vector functions include size(), empty(), resize(), reserve(), operator[], at(), front(), back(), push_back(), and pop_back(). 2) The vector abstract data type supports functions like at(), set(), insert(), and erase() to access and modify elements. It also includes standard size() and empty() functions. 3) Vectors can be implemented using an array-based approach but this has limitations on capacity. The standard solution is to use an extendable array strategy, dynamically allocating more space when needed.

Uploaded by

Majd AL Kawaas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
41 views36 pages

C++ Vector and Iterator Guide

1) STL vectors can dynamically resize unlike C++ arrays. Principal vector functions include size(), empty(), resize(), reserve(), operator[], at(), front(), back(), push_back(), and pop_back(). 2) The vector abstract data type supports functions like at(), set(), insert(), and erase() to access and modify elements. It also includes standard size() and empty() functions. 3) Vectors can be implemented using an array-based approach but this has limitations on capacity. The standard solution is to use an extendable array strategy, dynamically allocating more space when needed.

Uploaded by

Majd AL Kawaas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 36

List and Iterators

Reading: Chapter 6
Data Structures and Algorithms in C++
Second Edition

1
C++ STL Vector
• STL vector objects behave in many respects like C++ arrays, but
unlike C++ arrays, STL vectors can be dynamically resized
• Principal member functions of the vector class:
vector(n) : Construct a vector with space for n elements; if no argument is
given, create an empty vector.
size() : Return the number of elements in V.
empty() : Return true if V is empty and false otherwise.
resize(n) : Resize V, so that it has space for n elements.
reserve(n) : Request that the allocated storage space be large enough to hold
n elements.
operator[i] : Return a reference to the ith element of V.
at(i): Same as V[i], but throw an out of range exception if i is out of
bounds, that is, if i < 0 or i ≥V.size().
front(): Return a reference to the first element of V.
back() : Return a reference to the last element of V.
push_back(e): Append a copy of the element e to the end of V, thus increasing
its size by one.
pop_back() : Remove the last element of V, thus reducing its size by one. 2
#include <iostream>
#include <vector> C++ STL Vector
using namespace std;
int main (){ Example
vector<int> myvector(10);
int sum = 0, sz = myvector.size();
cout << "Vector sise :"<< myvector.size() <<endl;
for (unsigned i=0; i<sz; i++) myvector[i]=i;
for (unsigned i=0; i<sz/2; i++) {
int temp;
temp = myvector[sz-1-i];
myvector[sz-1-i]=myvector[i];
myvector[i]=temp;
}
myvector.push_back (100);
myvector.push_back (200);
myvector.push_back (300);
cout << "Vector sise :"<< myvector.size()<<endl;
cout << "myvector contains:";
for (unsigned i=0; i<sz; i++) cout << ' ' << myvector[i];
cout << '\n';
while (!myvector.empty()){
sum+=myvector.back();
myvector.pop_back();
}
cout << "The elements of myvector add up to " << sum << '\n';
return 0;
} 3
The Vector Abstract Data Type
• vector, also called an array list, is an ADT that supports the
following fundamental functions :
at(i): Return the element of V with index i; an error condition occurs if
i is out of range.
set(i,e): Replace the element at index i with e; an error condition occurs
if i is out of range.
insert(i,e): Insert a new element e into V to have index i; an error condition
occurs if i is out of range.
erase(i): Remove fromV the element at index i; an error condition occurs
if i is out of range.
– in addition to the standard size() and empty() functions.
– index parameter i is assumed to be in the range 0 ≤ i ≤ size()−1.

4
The Vector Abstract Data Type
at(i): Return the element of V with index i; an error condition occurs if i is out
of range.
set(i,e): Replace the element at index i with e; an error condition occurs if i is out
of range.
insert(i,e): Insert a new element e into V to have index i; an error condition occurs
if i is out of range.
erase(i): Remove fromV the element at index i; an error condition occurs if i is out
of range.

5
Array-Based Implementation
• Start with a fixed array A of size N,
– A[i] stores the element at index i.
– n < N of elements in the vector.
• implementation of the functions is simple.
– To implement the at(i) operation, for example, just return A[i].
– insert(i,e) and erase(i) algorithms
Algorithm insert(i,e):
for j = n−1,n−2, . . . , i do
A[ j+1]←A[ j] {make room for the new element}
A[i]←e
n←n+1

Algorithm erase(i):
for j = i+1, i+2, . . . ,n−1 do
A[ j−1]←A[ j] {fill in for the removed element}
n←n−1
Array-Based Implementation
• Performance
• Weakness: fixed capacity, N,
– If the actual number of elements, n, is much
smaller than N, then waste space.
– if n increases past N, then program will crash.

• Solution: extendable Array Strategy


– in C++ we cannot actually extend the array A; but we can do a work around to
increase the capacity of the vector,
– when n=N and function insert is called, perform the following steps:

1. Allocate a new array B of capacity 2N

2. Copy A[i] to B[i], for i = 0, . . . , N −1

3. Deallocate A and reassign A to point to the new array B


#include <iostream>
using namespace std;
typedef int Elem; // base element type
class ArrayVector {
public:
ArrayVector(); // constructor
int size() const; // number of elements
bool empty() const; // is vector empty?
Elem& operator[ ](int i); // element at index
Elem& at(int i); // element at index
void erase(int i); // remove element at index
void insert(int i, const Elem& e); // insert element at index
void reserve(int N); // reserve at least N spots
private:
int capacity; // current array size
int n; // number of elements in vector
Elem* A; // array storing the elements
};
ArrayVector::ArrayVector(): capacity(0), n(0), A(NULL) { }

int ArrayVector::size() const { return n;}

bool ArrayVector::empty() const { return size() == 0; }

Elem& ArrayVector::operator[ ](int i) { return A[i]; }


Elem& ArrayVector::at(int i) {
if (i < 0 || i >= n)
throw std::out_of_range("illegal index in function at()");
return A[i];
}
void ArrayVector::erase(int i) { // remove element at index
for (int j = i+1; j < n; j++) // shift elements down
A[j-1] = A[j];
n--; // one fewer element
}
void ArrayVector::reserve(int N) { // reserve at least N spots
if (capacity >= N) return; // already big enough
Elem* B = new Elem[N]; // allocate bigger array
for (int j = 0; j < n; j++) // copy contents to new array
B[j] = A[j];
if (A != NULL) delete [ ] A; // discard old array
A = B; // make B the new array
capacity = N; // set new capacity
}
void ArrayVector::insert(int i, const Elem& e) {
if (n >= capacity) // overflow?
reserve(max(1, 2 * capacity)); // double array size
for (int j = n-1; j >= i; j--) // shift elements up
A[j+1] = A[j];
A[i] = e; // put in empty slot
n++; // one more element
}
int main (){
ArrayVector myvector;
for (unsigned i=0; i<10; i++)
myvector.insert(i, 2*i);
cout << "Vector sise :"<< myvector.size()<<endl;
int sz = myvector.size();
for (unsigned i=0; i<sz/2; i++) {
int temp;
temp = myvector[sz-1-i];
myvector[sz-1-i]=myvector[i];
myvector[i]=temp;
}

cout << "myvector contains:";


for (unsigned i=0; i<myvector.size(); i++)
cout << ' ' << myvector[i];
cout << '\n';

return 0;
}
C++STL List
list(n): Construct a list with n elements; if no argument list is given, empty list is created.
size(): Return the number of elements in L.
empty(): Return true if L is empty and false otherwise.
front(): Return a reference to the first element of L.
back(): Return a reference to the last element of L.
push_front(e): Insert a copy of e at the beginning of L.
push_back(e): Insert a copy of e at the end of L.
pop_front(): Remove the fist element of L.
pop_back(): Remove the last element of L.
#include <iostream>
#include <list>
using namespace std;
int main () {
list<int> mylist;
mylist.push_back (100);
mylist.push_back (200);
mylist.push_back (300);
cout << "Popping out the elements in mylist:";
while (!mylist.empty()){
cout << ' ' << mylist.front();
mylist.pop_front();
}
cout << "\nFinal size of mylist is " << mylist.size() << '\n';
return 0; 11
}
Containers and Iterators
• Container is a data structure that stores a collection of elements.
• The STL provides a variety of different container classes such as
vector, deque, list , stack, queue , set, map

• A container supports element access through iterators.


• Why?
– This example style is valid for some containers
– Not all containers can be accessed through indexing.
• List for example. int vectorSum1(const vector<int>& V) {
int sum = 0;
for (int i = 0; i < V.size(); i++)
sum += V[i];
return sum;
}

• We need a uniform mechanism for accessing elements in all


containers
Containers and Iterators
• Every STL container defines special associated class called iterator.
• iterator is an object that specifies a position within a container with
the ability to navigate to other positions.
– iterator type: container<base>::iterator
– Example: typedef vector<int>::iterator Iterator;

• Each STL container provides two member functions to facilitate


access through iterators
– begin(): returns an iterator to the first element
– end(): return an iterator to an imaginary position just after the last element

• An iterator behaves like a pointer to an element


– If p is an iterator that refers to some position within a container
• *p: returns the element referenced by this iterator
• ++p (or p++): advances to the next element

– Extends the concept of position by adding a traversal capability


Iterating through a Container
• Let V be a container and p be an iterator for V
for (p = V.begin(); p != V.end(); ++p)
loop_body
• Example: (with an STL vector)

int vectorSum2(const vector<int> V) {


typedef vector<int>::iterator Iterator;
int sum = 0;
for (Iterator p = V.begin(); p != V.end(); ++p)
sum += *p;
return sum;
}

14
#include <iostream>
#include <vector>
using namespace std;
Iterating through a
int main (){ Container
vector<int> myvector (3,100); //100 100 100
vector<int>::iterator it;
it = myvector.begin();
it = myvector.insert ( it , 200 ); // 200 100 100 100

myvector.insert (it,2,300); //300 300 200 100 100 100

// "it" no longer valid, get a new one:


it = myvector.begin();

vector<int> anothervector (2,400);


myvector.insert (it+2,anothervector.begin(),anothervector.end());
//300 300 400 400 200 100 100 100
int myarray [] = { 501,502,503 };
myvector.insert (myvector.begin(), myarray, myarray+3);

cout << "myvector contains:";


for (it= myvector.begin(); it<myvector.end(); it++) cout << ' ' << *it;

cout << '\n';


//501 502 503 300 300 400 400 200 100 100 100
return 0;
} 15 Iterators and Sequences
Containers and Iterators
• Different containers provide iterators with different capabilities.
• Most containers (including lists, sets, and maps) provide the
ability to move not only forwards, but backwards as well. Hence
various notions of iterator exist:
– (standard) iterator: allows read-write access to elements
– const iterator: provides read-only access to elements
– bidirectional iterator: supports both ++p and --p
– random-access iterator: supports both p+i and p-i
• programmer should make sure that an iterator points to a valid
element before attempting to dereference it.
– Attempting to dereference an invalid iterator can result in your program
aborting.
• iterators can become invalid if the position that it refers to is deleted
Containers and Iterators
• In vectorSum3, argument passed by value
– Better to passing by reference
• const_iterator: provides read-only access to elements
– if p is a const iterator, it is possible to read the value of *p, but you cannot
assign it a value.
– Denoted as vector<int>::const_iterator.

int vectorSum3(const vector<int> &V) {


typedef vector<int>::const_iterator Iterator;
int sum = 0;
for (Iterator p = V.begin(); p != V.end(); ++p)
sum += *p;
return sum;
}
Implementing Iterators
• Array-based
– array A of the n elements
– index i that keeps track of the cursor
– begin() = 0
– end() = n (index following the last element)
• Linked list-based
– doubly-linked list L storing the elements, with sentinels for header and
trailer
– pointer to node containing the current element
– begin() = front node
– end() = trailer node (just after last node)

18 Iterators and Sequences


STL Iterator-Based Container Functions
• Using Vector as an example
– Let V be an STL vector of some given base type,
– Let e be an object of this base type.
– Let p and q be iterators over this base type.

• Each STL container type V supports iterators:


– V::iterator – read/write iterator type
– V::const_iterator – read-only iterator type
– V.begin(), V.end() – return start/end iterators

• iterator-based operators:
– *p: access current element
– ++p, --p: advance to next/previous element

19
STL Iterator-Based Container Functions
• STL list and STL deque supportsthese functions (constructors
are named differently).
• set, multiset, map, and multimap support all of the above
functions except assign

vector(p,q) Construct a vector by iterating between p and q,


copying each of these elements into the new vector.

assign(p, q) replace V with contents referenced by the iterator


range [p, q) (from p up to, but not including, q)

insert(p, e) insert e prior to position p.


erase(p) remove element at position p
erase(p, q) remove elements in the iterator range [p, q)
clear() Delete all these elements of V

20
STL Iterator-Based Container Functions
• Iterators p and q do not need to be drawn from the same type of
container as V, bu the container they are drawn from has the same
base type.
• Example:
– suppose L is an STL list container of integers.
– We can create a copy of L in the form of an STL vector V as follows:
list<int> L; // an STL list of integers
// . . .
vector<int> V(L.begin(), L.end()); // initialize V to be a copy of L

• Thus, we can initialize the contents of an STL container from a


standard C++ array

21
STL Vectors and Algorithms
• STL also provides algorithms that operate on containers in general, and
vectors in particular.
• Let p and q be iterators over some base type, and let e denote an object of this
base type. As above, these operations apply to the iterator range that starts at p
and ends just prior to q.
sort(p,q) Sort the elements in the range from p to q in ascending order.
It is assumed that less-than operator (“<”) is defined for the
base type.
random_shuffle(p,q) Rearrange elements in the range from p to q in random order
reverse(p,q) Reverse the elements in the range from p to q.
find(p,q,e) Return an iterator to the first element in the range from p to q
that is equal to e; if e is not found, q is returned
min_element(p,q) Return an iterator to minimum element in range from p to q.
max_element(p,q) Return an iterator to maximum element in range from p to q.
for each(p,q, f ) Apply the function f the elements in the range from p to q.

To access these functions use #include <algorithm>


An example of the use of the STL vector and iterators
#include <cstdlib> // provides EXIT SUCCESS
#include <iostream> // I/O definitions
#include <vector> // provides vector
#include <algorithm> // for sort, random shuffle
using namespace std; // make std:: accessible
int main () {
int a[ ] = {17, 12, 33, 15, 62, 45};
vector<int> v(a, a + 6); // v: 17 12 33 15 62 45
cout << v.size() << endl; // outputs: 6
v.pop_back(); // v: 17 12 33 15 62
cout << v.size() << endl; // outputs: 5
v.push back(19); // v: 17 12 33 15 62 19
cout << v.front() << " " << v.back() << endl; // outputs: 17 19
sort(v.begin(), v.begin() + 4); // v: (12 15 17 33) 62 19
v.erase(v.end() − 4, v.end() − 2); // v: 12 15 62 19
cout << v.size() << endl; // outputs: 4
char b[ ] = {’b’, ’r’, ’a’, ’v’, ’o’};
vector<char> w(b, b + 5); // w: b r a v o
random shuffle(w.begin(), w.end()); // w: o v r a b
w.insert(w.begin(), ’s’); // w: s o v r a b
for (vector<char>::iterator p = w.begin(); p != w.end(); ++p)
cout << *p << " "; // outputs: s o v r a b
cout << endl;
return EXIT SUCCESS;
}
Sequence ADT
• The Sequence ADT is the • List-based methods:
union of the Array List and
– begin(), end()
Node List ADTs
– insertFront(o),
• Elements accessed by insertBack(o)
– Index, or – eraseFront(),
eraseBack()
– Position
– insert (p, o),
• Generic methods: erase(p)
– size(), empty()
• Bridge methods:
• ArrayList-based methods: – atIndex(i),
– at(i), set(i, o), indexOf(p)
insert(i, o), erase(i)

24
Applications of Sequences
• The Sequence ADT is a basic, general-purpose, data
structure for storing an ordered collection of elements
• Direct applications:
– Generic replacement for stack, queue, vector, or list
– small database (e.g., address book)
• Indirect applications:
– Building block of more complex data structures

25 Iterators and Sequences


Linked List Implementation
• A doubly linked list provides a reasonable implementation of the Sequence ADT
• Nodes implement Position and store:
– element
– link to the previous node
– link to the next node
• Special trailer and header nodes: two sentinel nodes, created when the sequence
is first constructed. other elements are inserted between these sentinels.
• Position-based methods run in constant time
• Index-based methods require searching from header or trailer while keeping track
of indices; hence, run in linear time

header nodes/positions trailer

elements
26
Iterators and Sequences
typedef string Elem; // list element type
struct Node { // doubly linked list node
Elem elem; // node element value
Node* prev; // previous node in list
Node* next; // next node in list
};

class NodeSequence { // doubly linked list


public:
class Iterator { // an iterator for the sequence
public:
Elem& operator*(); // reference to the element
bool operator==(const Iterator& p) const; // compare positions
bool operator!=(const Iterator& p) const;
Iterator& operator++(); // move to next position
Iterator& operator--(); // move to previous position
friend class NodeSequence; // give NodeList access
private:
Node* v; // pointer to the node
Iterator(Node* u); // create from node
}; 27
NodeSequence(); // constructor
~NodeSequence(); // destructor
bool empty() const; // is list empty?
int size() const; // list size
Iterator begin() const; // beginning position
Iterator end() const; // (just beyond) last position
void insertFront(const Elem &e); // add to front of list
void insertBack(const Elem &e); // add to back of list
void insert(const Iterator& p, const Elem& e); //insert e before p
void eraseFront(); // remove from front
void eraseBack(); // remove from back
void erase(const Iterator& p); // remove p
Iterator atIndex(int i) const; // get position from index
int indexOf(const Iterator& p) const; // get index from position
private: // local type definitions
Node* header; // head-of-list sentinel
Node* trailer; // tail-of-list sentinel
int n;
};

28
// constructor from Node*
NodeSequence::Iterator::Iterator(Node* u)
{ v = u; }

// reference to the element


Elem& NodeSequence::Iterator::operator*()
{ return v->elem; }

// compare positions
bool NodeSequence::Iterator::operator==(const Iterator& p) const
{ return v == p.v; }
bool NodeSequence::Iterator::operator!=(const Iterator& p) const
{ return v != p.v; }

// move to next position


NodeSequence::Iterator& NodeSequence::Iterator::operator++()
{ v = v->next; return *this; }

// move to previous position


NodeSequence::Iterator& NodeSequence::Iterator::operator--()
{ v = v->prev; return *this; }

29
NodeSequence::NodeSequence() {
n = 0; // initially empty
header = new Node; // create sentinels
trailer = new Node;
header->next = trailer; // have them point to each other
trailer->prev = header;
}

bool NodeSequence::empty() const{


return (n==0);
}

int NodeSequence::size() const{ return n;}

// begin position is first item


NodeSequence::Iterator NodeSequence::begin() const
{return Iterator(header->next); }

// end position is just beyond last


NodeSequence::Iterator NodeSequence::end() const
{ return Iterator(trailer); }
30
// insert e before p
void NodeSequence::insert(const Iterator& p, const Elem& e) {
Node* w = p.v; // p’s node
Node* u = w->prev; // p’s predecessor
Node* v = new Node; // new node to insert
v->elem = e;
v->next = w; w->prev = v; // link in v before w
v->prev = u; u->next = v; // link in v after u
n++;
}
// insert at front
void NodeSequence::insertFront(const Elem& e) { insert(begin(), e); }
// insert at rear
void NodeSequence::insertBack(const Elem& e) { insert(end(), e); }

trailer
header
n=0
n=1

next null
next

null prev prev

u e

next
w
31
v prev
// remove p
void NodeSequence::erase(const Iterator& p) {
Node* v = p.v; // node to remove
Node* w = v->next; // successor
Node* u = v->prev; // predecessor
u->next = w; w->prev = u; // unlink p
delete v; // delete this node
n--; // one fewer element
}

// remove first
void NodeSequence::eraseFront() { erase(begin()); }
// remove last
void NodeSequence::eraseBack() { erase(--end()); }

trailer
header
n=1
n=0 e
next next null
next

null prev prev prev

u v w
32
// get position from index
NodeSequence::Iterator NodeSequence::atIndex(int i) const {
Iterator p = begin();
for (int j = 0; j < i; j++) ++p;
return p;
}

// get index from position


int NodeSequence::indexOf(const Iterator& p) const {
Iterator q = begin();
int j = 0;
while (q != p) { // until finding p
++q; ++j; // advance and count hops
}
return j;
}

33
int main() {
NodeSequence d;
string first = "Laure";

d.insertFront("you");
d.insertFront("are");
d.insertFront("How");
d.insertBack("handsom");
for (NodeSequence::Iterator p=d.begin(); p!=d.end(); ++p)
cout <<*p<<" ";
cout <<endl;
cout << *d.atIndex(0) <<" ";
NodeSequence::Iterator i= d.atIndex(d.size()-1);
cout <<*i<<" \n";
d.insert(i,first);
for (NodeSequence::Iterator p=d.begin(); p!=d.end(); ++p)
cout <<*p<<" ";
cout <<endl;
cout<<d.indexOf(i);

34
}
Array-based Implementation

• We use a circular
elements
array storing
positions
• A position object
stores:
– Element
– Index 0 1 2 3
• Indices f and l keep positions
track of first and last
positions
S
f l
35
Comparing Sequence Implementations

Operation Array List


size, empty 1 1
atIndex, indexOf, at 1 n
begin, end 1 1
set(p,e) 1 1
set(i,e) 1 n
insert(i,e), erase(i) n n
insertBack, eraseBack 1 1
insertFront, eraseFront n 1
insert(p,e), erase(p) n 1
36 Iterators and Sequences

You might also like