.cc
files are compiled individually and
linked
together
.h
file and the implementation (definitions) into the
.cc
file
.h
file because the compiler needs to know the size of a class
and because of inlining and templating
.cc
files
(type) expr
static_cast<type>(expr):
change the bits (at compile time)
reinterpret_cast<type>(expr):
change the interpretation (e.g. pointer type)
const_cast<typer>(expr):
add or remove
const
attribute (mostly to remove)
dynamic_cast<type>(expr):
cast a pointer from a pointer-to-base-class to a
pointer-to-derived-class (or return NULL if the object
pointed to is not of the derived class)
Sequential processing is essentially a
first
and
next
operation.
Additional operations include
last,
prev,
insert (first, last, before, after),
delete (current, next, prev, first, last),
find.
Special cases include the stack, queue, and deque.
Sequential operations may be implemented using a linked list
(singly or doubly linked) or a vector. The choice determines the
asymptotic performance of the operations (e.g. insert into a
linked list is O(1) but insert into an array is O(N)). In
practice one uses the STL continer classes
std::list
or
std::vector.
Sequential processing so important, the 2011 language standard
introduced the range-based
for
statement (other languages already had it).
Iterators use operator overloading to mimic pointer arithmetic. The following idiom works for serveral different standard container types:
for(ContainerType::iterator it = container.begin(); it != container.end(); ++it) {
do_something_with(*it);
}
O(n) on linked list
The dictionary abstraction is extremely important. Lookup by data is not the same as finding the nth entry. Typically, we look up a value based on a key.
the main operations are
insert
and
lookup.
Additional operations may include:
This dictionary is so important that, naturally, it has many names:
A dictionary can be implemented using a linked list with O(N) lookup time if N is small or very few lookups are being performed. Naturally, we can do better.
If we can sort the data by the key
O(N log N),
lookup can be performed in
O(log N)
time.
Note that sorting is more expensive than than an
O(N)
one-time lookup, but sorting is justified if performing a large
number of lookups or if the data can be sorted "offline". In some
cases, the data may arrive pre-sorted.
inverted index: separate tables for each key
| data by name | original (raw) data | data by value | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
value = data[ by_name[i].ref ].value
Essentially, the problem is that binary search is inflexible:
insert and delete operations are
O(N).
The binary search tree (BST) is essentially a binary tree
structure that represents the decision pattern of the binary
search:
O(log N)
time
O(N)
using
O(log N)
space (recursion)
O(n)
without preprocessing
(or
O(log N)
with additional processing during insertion and deletion)
The binary search tree worst-case performance is O(N) instead of O(log N) because a BST degenerates to a linked list with pathological input. Unfortunately, two pathological cases are building the tree in sorted order and building the tree in reverse sorted order.
The 2-3 tree is a special case of the general B-tree. The key insight is that each node holds one or two keys, and all non-leaf nodes have two or three children (depending on the number of keys in the node). All leaves are maintained at the same level.