Lecture 7
std::vector
std::vector is the standard library's resizable array. Unlike C-style arrays from Lecture 6, a
vector grows automatically as you add elements — no manual new/delete required.
std::vector<T> is a template — you specify the element type inside <>. Any type works:
std::vector<double>, std::vector<std::string>, even std::vector<std::vector<int>> for a 2D
grid.
Adding and removing elements
push_back appends to the end. pop_back removes the last element. Both run in amortized O(1).
Size and capacity
size() is how many elements you have. capacity() is how much space has been reserved. When size
hits capacity, the vector reallocates to a larger block (typically doubling). You almost always only
care about size().
Element access
Like C-style arrays, v[i] performs no bounds check. Out-of-range access is undefined behavior.
Use v.at(i) when you want a guaranteed exception instead of silent corruption.
Iterators
An iterator is an object that points into a container and supports the same operations as a
pointer: dereference (*), increment (++), and comparison (==, !=). Every standard container
provides begin() and end():
v.end() is a sentinel — it points past the last valid element, just like ptr + n sits past
the end of a C-style array.
The type of it is std::vector<int>::iterator, which is verbose. auto is idiomatic here.
Range-for under the hood
The range-for loop you've already seen is sugar for iterator-based traversal:
Use range-for when you don't need the iterator position explicitly.
Lambdas
A lambda is an anonymous function you write inline. The syntax is:
Lambdas are most useful as arguments to <algorithm> functions that accept a predicate or
comparator. Instead of defining a named function somewhere else, you write the logic right where it's
used.
Captures
The [] is the capture list — it controls which variables from the surrounding scope the lambda
can see.
Common shorthand: [=] captures all locals by value, [&] captures all by reference.
Capturing by reference ([&]) is safe when the lambda doesn't outlive the enclosing scope.
If you store a lambda and the captured variable goes out of scope, the reference dangles — same
problem as any other dangling reference.
The <algorithm> Header
<algorithm> provides a large library of operations that work on iterator ranges. The pattern is
always the same: pass begin and end and the function does the work over that range.
Sorting
std::sort runs in O(n log n). The optional third argument is a comparator — any callable that
returns true if the first argument should sort before the second.
Reversing
Searching
std::find returns an iterator to the first match, or v.end() if not found. Always check against
v.end() before dereferencing.
std::find_if takes a predicate instead of a value — this is where lambdas shine:
Counting
Min and max
Both return iterators, not values — dereference to get the value.
Filling
Replacing
Predicates: any, all, none
Accumulate (from <numeric>)
std::accumulate lives in <numeric>, not <algorithm>. It folds the range with + by default
but accepts a custom binary operation — a lambda — as a fourth argument.
Exercises
Q1.
Without running the code, predict what this program prints. Then verify.
Q2.
Without running the code, predict what this program prints.
Q3.
Without running the code, predict what this program prints.
Q4.
Trace through this program step by step. At each checkpoint, write the contents of v.
Q5.
Each snippet has a bug. Identify what's wrong and what happens at runtime.
Q6.
Write a function above_average(const std::vector<int>& v) that returns a new vector containing
only the elements strictly greater than the average of all elements. Use std::accumulate to
compute the sum.
Q7.
Write a function top_k(std::vector<int> v, int k) that returns a vector of the k largest
elements in descending order. If k exceeds the vector size, return all elements sorted descending.
Take v by value so the caller's copy is not modified.
Practice Problems
- Move Zeroes — Easy
- Find All Numbers Disappeared in an Array — Easy
- Sort Colors — Medium