.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.