@@ -1 +1,199 @@
# Lecture 9
# CSE332S Lecture 9
## Sequential Containers
Hold elements of a parameterized type (specified when the container variable is
declared): `vector<int> v; vector<string> v1;`
Elements are inserted/accessed based on their location (index)
- A single location cannot be in more than 1 container
- Container owns elements it contains - copied in by value, contents of container are destroyed when the container is destroyed
Containers provide an appropriate interface to add, remove, and access elements
- Interface provided is determined by the specifics of the container - underlying data structure
_usually the provided interface of the container runs in constant time_
### Non-random access containers
Cannot access elements in constant time, must traverse the container to get to the desired element.
#### Forward list
- implemented as a singly linked list of elements
- Elements are not contiguous in memory (no random access)
- Contains a pointer to the first element (can only grow at front, supplies a `forward_iterator` )
#### List
- implemented as a doubly linked list of elements
- Elements are not contiguous in memory (no random access)
- Contains a pointer to front and back (can grow at front or back, supplies a `bidirectional_iterator` )
### Random access containers
Add, remove, and access elements in constant time.
#### Vector
- implemented as a dynamically sized array of elements
- Elements are contiguous in memory (random access)
- Can only grow at the back via `push_back()` (amortized constant time, _may expand the array takes linear time_ )
#### Deque
- double-ended queue of elements
- Elements do not have to be contiguous in memory, but must be accessible in constant time (random access)
- Can grow at front or back of the queue
## Iterators and iterator types
Could use the subscript/indexing (operator[]) operator with a loop
- Not all containers supply an [] operator, but we should still be able to traverse and access their elements
Containers provide iterator types:
- `vector<int>::iterator i; // iterator over non-const elements`
- `vector<int>::const_iterator ci; // iterator over const elements`
Containers provide functions for creating iterators to the beginning and just past
the end of the container:
```cpp
vector < int > v = { 1 , 2 , 3 , 4 , 5 };
auto start = v . cbegin (); // cbegin() gives const iterator, you can't modify the elements, you can use .begin() to get a non-const iterator
while ( start != v . cend ()) { // over const elements, v.cend() is not a valid element, it's just one pass the end.
cout << * start << endl ;
++ start ;
}
```
### More on iterators
- Iterators generalize different uses of pointers
- Most importantly, define left-inclusive intervals over the ranges of elements in a container `[begin, end)`
- Iterators interface between algorithms and data structures (Iterator design pattern)
- Algorithms manipulate iterators, not containers
- An iterator’ s value can represent 3 kinds of `states` :
- `dereferencable` (points to a valid location in a range), eg `*start`
- `past the end` (points just past last valid location in a range), eg `v.cend()`
- `singular` (points to nothing), eg `nullptr`
- Can construct, compare, copy, and assign iterators so that native and library types
can inter-operate
### Properties of Iterator Intervals
- Valid intervals can be traversed safely with an iterator
- An empty range `[p,p)` is valid
- If `[first, last)` is valid and non-empty, then `[first+1, last)` is also valid
- Proof: iterative induction on the interval
- If `[first, last)` is valid
- and position `mid` is reachable from `first`
- and `last` is reachable from `mid`
- then `[first, mid)` and `[mid, last)` are also valid
- Proof: divide and conquer induction on the interval
- If `[first, mid)` and `[mid, last)` are valid, then `[first, last)` is valid
- Proof: divide and conquer induction on the interval
### Interface supplied by different iterator types
- Output iterators: used in output operations (write),
- "destructive" read at head of stream (istream)
- Input iterators: used in input operations (read),
- "transient" write to stream (ostream)
- Forward iterators: used in forward operations (read, write), common used in forward linked list
- Value _persists_ after read/write
- Bidirectional iterators: used in bidirectional operations (read, write), common used in doubly linked list
- Value have _locations_
- Random access iterators: used in random access operations (read, write), common used in vector
- Can express _distance_ between two iterators
| Category/Operation | Output | Input | Forward | Bidirectional | Random Access |
| ------------------ | -------------- | -------------- | -------------- | -------------- | ----------------- |
| Read | N/A | `=*p` (r-value) | `=*p` (r-value) | `=*p` (r-value) | `=*p` (r-value) |
| Access | N/A | `->` | `->` | `->` | `->,[]` |
| Write | `*p=` (l-value) | N/A | `*p=` (l-value) | `*p=` (l-value) | `*p=` (l-value) |
| Iteration | `++` | `++` | `++` | `++,--` | `++,--,+,-,+=,-=` |
| Comparison | N/A | `==,!=` | `==,!=` | `==,!=` | `==,!=,<,>,<=,>=` |
## Generic algorithms in CPP
A standard collection of generic algorithms
- Applicable to various types and containers
- E.g., sorting integers (`int` ) vs. intervals (`pair<int, int>` )
- E.g., sorting elements in a `vector` vs. in a C-style array
- Polymorphic even without inheritance relationships - interface polymorphism
- Types substituted need not have a common base class
- Must only provide the interface the algorithm requires
- Common iterator interfaces allow algorithms to work with many types of
containers, without knowing the implementation details of the container
- Significantly used with the sequence containers
- To reorder elements within a container’ s sequence
- To store/fetch values into/from a container
- To calculate various values and properties from it
### Organization of C++ Algorithm Libraries
The `<algorithm>` header file contains
- Non-modifying sequence operations
- Do some calculation but don’ t change sequence itself
- Examples include `count` , `count_if`
- Mutating sequence operations
- Modify the order or values of the sequence elements
- Examples include `copy` , `random_shuffle`
- Sorting and related operations
- Modify the order in which elements appear in a sequence
- Examples include `sort` , `next_permutation`
- The `<numeric>` header file contains
- General numeric operations
- Scalar and matrix algebra, especially used with `vector<T>`
- Examples include `accumulate` , `inner_product`
### Using Algorithms
Example using `std::sort()`
- `sort` algorithm
- Reorders a given range
- Can also plug in a functor to change the ordering function
- http://www.cplusplus.com/reference/algorithm/sort/
- Requires random access iterators.
- Requires elements being sorted implement operator `<` (less than)
```cpp
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std ;
int main ( int argc , char * argv []) {
vector < int > v = { 3 , 1 , 4 , 1 , 5 , 9 };
sort ( v . begin (), v . end ()); // sort the vector
for ( int i : v ) {
cout << i << " " ;
}
return 0 ;
}
```
Sort forward list of strings
```cpp
forward_list < string > fl = { "hello" , "world" , "this" , "is" , "a" , "test" };
sort ( fl . begin (), fl . end ());
```
**This is not valid because forward list does not support random access iterators.**
Sort vector of strings
```cpp
vector < string > v = { "hello" , "world" , "this" , "is" , "a" , "test" };
sort ( v . begin (), v . end ());
```