07. Arrays and vector
Most OSN problems give you a sequence of data: exam scores, tree
heights, coordinates, item weights. Storing them in separate variables
is clearly impossible. Arrays and vector are containers for
sequences of data like that.
Arrays
int a[5]; // five boxes, contents not yet defined
int b[5] = {3, 1, 4, 1, 5};
cout << b[0] << "\n"; // 3 -- the first index is 0
cout << b[4] << "\n"; // 5 -- the last index is length - 1
b[2] = 99;An array is n boxes of the same type laid out
contiguously in memory. What makes it fast: the computer only needs to
know where the first box is, and the i-th box is then found
with a single calculation — the start address plus
i times the size of one box. That is why indices
start at 0: a[0] means "zero boxes from the
start".
index: 0 1 2 3 4
+-----+-----+-----+-----+-----+
b: | 3 | 1 | 4 | 1 | 5 |
+-----+-----+-----+-----+-----+
Accessing b[3] takes the same time as accessing
b[0], whether the array holds five boxes or a million.
Important rules:
- Indices start at 0. An array of size
nhas indices0throughn - 1. - The size must be known at compile time (a fixed number or a
const int). - Reading or writing out of bounds is not prevented by the compiler. The program still runs, with garbage values, or crashes.
Out-of-bounds indices are the number one cause of Runtime
Error in OSN. Writing a[n] on an array of size
n means touching a box that belongs to another variable —
sometimes the program crashes, sometimes it keeps running with a wrong
answer on your computer but a correct one on the grader's computer (or
the other way around). The typical symptom: the program works correctly
on small examples, then gets Runtime Error on the large
subtasks.
Two ways to prevent it: use
for (int i = 0; i < n; i++) (not <=),
and give the array a size slightly larger than the problem's limit.
The pattern for reading n numbers:
const int MAKS = 100005;
int a[MAKS];
int n;
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];In contests, arrays are usually made global (outside
main) with a size slightly larger than the problem's limit,
for example 100005 for a limit of 100,000. Global arrays
can be very large, whereas local arrays inside a function use the
limited stack memory — int a[1000000]; inside
main can make the program crash.
Basic patterns on arrays
// sum
long long total = 0;
for (int i = 0; i < n; i++) total += a[i];
// largest and smallest
int besar = a[0], kecil = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > besar) besar = a[i];
if (a[i] < kecil) kecil = a[i];
}
// counting elements that meet a condition
int genap = 0;
for (int i = 0; i < n; i++) if (a[i] % 2 == 0) genap++;
// searching for a value, storing its position
int posisi = -1;
for (int i = 0; i < n; i++) {
if (a[i] == target) { posisi = i; break; }
}Note the "largest" pattern: its initial value is a[0],
not 0. If all the data is negative, starting from
0 gives a wrong answer. Since the initial value is already
a[0], the loop starts from i = 1 — checking
a[0] again is not wrong, just wasteful.
All four patterns above have the same shape: one pass, one
accumulator, one decision per element. The number of steps is O(n), so
they all stay fast up to n in the millions. Only when a
problem demands comparing each element with every other
element does the work jump to n² — and that is covered in
Chapter 11.
Open
exercises/07/01-array.cpp.
Add one loop that prints the array's contents from back to front, then
another that prints only the elements with even indices.
Open
exercises/07/03-pola-array.cpp,
run it with the input 5 then -7 -2 -9 -4 -1.
Change the initial value of besar ("largest") to
0 and watch the answer become wrong.
vector
A vector is an array whose size can change while the
program runs.
#include <vector>
vector<int> v; // empty
v.push_back(10); // append at the back
v.push_back(20);
cout << v.size() << "\n"; // 2
cout << v[0] << "\n"; // 10
vector<int> w(5, 0); // 5 elements, all 0
vector<int> u = {3, 1, 4};| Operation | Meaning |
|---|---|
v.push_back(x) |
appends x at the end |
v.size() |
number of elements |
v[i] |
the i-th element (starting from 0) |
v.clear() |
empties it |
v.resize(n) |
changes the size to n |
Iterating over a vector:
for (int i = 0; i < (int)v.size(); i++) cout << v[i] << " ";
for (int x : v) cout << x << " "; // short formv.size() is of an unsigned type. Writing
for (int i = 0; i < v.size() - 1; i++) on an empty
vector makes v.size() - 1 a huge number, not
-1, and the loop runs millions of times. Change it to
(int)v.size() as in the example above.
push_back adds one element at the back, and the
vector manages growing its own memory. The average cost
stays small, so appending a million elements one at a time is still
fast.
| Consideration | Plain array | vector |
|---|---|---|
| size | fixed, known at compile time | can change at run time |
| its own size | must be stored yourself in a variable n |
v.size() |
| passing to a function | must also pass n |
just vector<int> &v |
| irregular two-dimensional arrays | hard | easy (vector<vector<int>>) |
| speed | slightly faster | practically the same |
Use a plain array when the size is already known from the problem's
limits, and a vector when the size is only known at run
time or when it needs to be passed to a function.
Open
exercises/07/02-vector.cpp.
Add v.pop_back(); after several push_back
calls, print v.size() before and after, then note which
element disappears.
Sorting
sort from the <algorithm> library
sorts from smallest to largest:
#include <algorithm>
sort(a, a + n); // array
sort(v.begin(), v.end()); // vectorFor descending order:
sort(v.begin(), v.end(), greater<int>());The two values given to sort are the
start and one past the end. So
sort(a, a + n) sorts a[0] through
a[n-1], and sort(a + 2, a + 5) sorts only the
three elements in the middle. This "one past the end" pattern is used
throughout the C++ standard library.
Sorting n items takes about n log n steps —
for 100,000 items about 1.7 million steps, very fast. Compare that with
sorting by hand using two nested loops, which takes about 5 billion
steps for the same n. In contests, writing your own sort is
almost never the right choice.
Sorting often turns a problem that looks hard into an easy one: "the
two most similar numbers" becomes "two adjacent numbers" (Chapter 08),
"take the most profitable" becomes "take from the front" (Chapter 12).
Many greedy problems in Chapter 12 do begin with a single
sort line.
Open
exercises/07/06-urut.cpp.
Run it as is, then change one sort call to the descending
version with greater<int>() and note the difference
in the output.
Two-dimensional arrays
For tables or maps:
int grid[100][100];
for (int i = 0; i < baris; i++)
for (int j = 0; j < kolom; j++)
cin >> grid[i][j];grid[i][j] means row i, column
j — row first, then column. This order is
a frequent source of confusion, because in mathematical coordinates
x (horizontal) is usually named first.
j=0 j=1 j=2
+-----+-----+-----+
i=0 | 7 | 2 | 9 | grid[0][2] is 9
+-----+-----+-----+
i=1 | 4 | 1 | 3 | grid[1][0] is 4
+-----+-----+-----+
The amount of work is rows × columns, so a 1000 × 1000
map means a million steps for a single pass.
Two-dimensional arrays get large quickly:
int grid[1000][1000] already uses about 4 MB, and
int grid[5000][5000] about 100 MB — more than the memory
limit of most problems. If the row limit is large while the column count
is small, write grid[100005][5], not the other way
around.
Open
exercises/07/04-dua-dimensi.cpp.
Add a loop that prints the sum of each column (not
row). Hint: swap the roles of i and j in the
nested loop.
Arrays as markers: the prime number sieve
One of the most frequent uses of arrays in OSN: using one as a list
of markers. The Sieve of Eratosthenes finds all prime numbers
up to n by crossing out the multiples of each prime
number.
const int MAKS = 1000001;
bool coret[MAKS];
void saring(int n) {
coret[0] = coret[1] = true;
for (int i = 2; (long long)i * i <= n; i++) {
if (!coret[i]) {
for (int j = i * i; j <= n; j += i) coret[j] = true;
}
}
}Trace it for n = 30. Initially nothing is crossed out,
then:
i |
Crossed out | Remaining, not yet crossed out |
|---|---|---|
| 2 | 4, 6, 8, …, 30 | 2 3 5 7 9 11 13 15 17 19 21 23 25 27 29 |
| 3 | 9, 12, 15, …, 30 | 2 3 5 7 11 13 17 19 23 25 29 |
| 4 | skipped (4 already crossed out) | — |
| 5 | 25, 30 | 2 3 5 7 11 13 17 19 23 29 |
The loop stops at i = 6 because 6 × 6 exceeds 30. What
remains is exactly the ten prime numbers up to 30.
Two details are worth noting. Crossing out starts from
i * i, not from 2 * i, because the smaller
multiples have certainly been crossed out by smaller prime numbers
already. And numbers that are already crossed out are skipped entirely,
because their multiples have also been crossed out.
After it runs, coret[x] == false (coret =
"crossed out") means x is prime. Checking one at a time
with the square-root method takes about n √n steps; the
sieve only about n log log n — for n = 10⁶,
that is the difference between several seconds and several
milliseconds.
Open
exercises/07/05-saringan.cpp
and run it for n = 1000000. Compare its time with the
one-at-a-time check from the Chapter 06 homework.
Homework
Problem 1 — Simple statistics
Open
exercises/07/homework/01-statistik.cpp.
Read n then n integers. Print three lines: the
smallest value, the largest value, and the total sum.
Problem 2 — Reversing the order
Open
exercises/07/homework/02-balik-array.cpp.
Read n then n numbers. Print them all in
reverse order, separated by spaces, with no space at the end of the
line.
Problem 3 — Searching for a value
Open
exercises/07/homework/03-cari.cpp.
Read n, then n numbers, then one number
target. Print the first index where
target appears (indices start at 0), or -1 if
it does not appear.
Problem 4 — Sum of each row
Open
exercises/07/homework/04-matriks.cpp.
Read b and k (the number of rows and columns),
then b × k numbers. Print the sum of each row, one per
output line.
Challenge — Sieve of Eratosthenes
Open
exercises/07/homework/05-saringan.cpp.
Read n (up to 1,000,000), and print the number of prime
numbers from 1 to n using the sieve. For
n = 1,000,000 the answer is 78,498, and your program must
finish in under one second.
Most common mistakes
| What was written | Consequence |
|---|---|
for (int i = 1; i <= n; i++) a[i] on a 0-indexed
array |
skips a[0], touches a[n] |
int a[n]; with n from
cin |
array size must be fixed; use a vector or a large
global array |
int a[1000000]; inside main |
stack memory runs out, the program crashes |
besar = 0 for data that can be negative |
wrong answer |
i < v.size() - 1 on an empty
vector |
loops millions of times; use (int)v.size() |
forgetting sort before
next_permutation |
some arrangements never appear |
sort(a, a + n) written as
sort(a, a + n - 1) |
the last element is not sorted |
OSN tips
A counting array is the most frequently used
technique. If the values in a problem are bounded to be small —
for example 1 to 1000 or 1 to 10⁶ — there is almost always a solution
that uses an array as long as that value limit, not as long as the
number of data items. Reading the sentence "1 ≤ Aᵢ ≤ 1000" as a signal
to "make cacah[1001]" (cacah = "count") saves
a lot of thinking time.
The memory limit also shapes the solution. One
int takes 4 bytes, one long long 8 bytes. From
that, array sizes can be calculated:
| Array | Memory |
|---|---|
int a[1000000] |
4 MB |
int a[10000000] |
40 MB |
long long a[10000000] |
80 MB |
int a[1000][1000] |
4 MB |
int a[5000][5000] |
100 MB |
The memory limit is written in the problem header. If the calculation goes over that limit, then the intended solution is not one that stores everything.
Large arrays are declared outside main.
Local variables are stored on the stack, which is much smaller
than the program's memory. int a[10000000]; inside
main usually makes the program stop on its own before the
first line runs. A declaration outside main uses global
memory, and as a bonus its contents are automatically zero.
Out-of-bounds indices are not always immediately
visible. C++ does not check array bounds. a[n] on
an array of size n sometimes runs smoothly on your own
computer and produces a Runtime Error or strange answers on the
grading machine. If your answer is correct at home but rejected by the
judge, check every index at the boundaries.
Sort first when the problem talks about "nearest", "pairs",
or "consecutive". Sorting N items takes about
N log N steps — for N = 200,000 that is under
four million steps, practically free compared with comparing all pairs,
which takes 2 × 10¹⁰ steps.
Problems that mention 1 to N are often easier with 1-based
indices. Making an array of size n + 1 and leaving
index 0 empty removes all the error-prone −1 adjustments.
The memory wasted is one element.
vector<int> v(n); creates n elements
with value zero, whereas vector<int> v; v.reserve(n);
creates an empty vector with space only reserved.
Writing v[i] = x on an empty vector is an out-of-bounds
write, even though the capacity has been reserved.
Summary
- An array stores
nvalues of the same type contiguously; its indices are0throughn - 1, and access is always equally fast. - Touching an out-of-bounds index is not prevented by the compiler and is the most frequent cause of Runtime Error.
- Large arrays are made global with a size slightly above the problem's limit.
vectoris used when the size is only known at run time;v.size()is unsigned, so cast it with(int)before subtracting.sortworks on the range from the start to one past the end, takes aboutn log nsteps, and often turns a hard problem into an easy one.- Two-dimensional arrays are accessed as
grid[row][column]; their size quickly uses up memory. - A marker array (
bool) is a versatile tool; the Sieve of Eratosthenes is its most important example.
OSN-style exercises
The following three problems cover the three array techniques that appear most often in OSN: counting arrays, sorting, and summing over a two-dimensional array.
The starter files for all three are in
exercises/07/osn/.
Problem 1 — Score Mode
The committee collects the exam scores of N students.
Determine the score that appears most often along with the number of
times it appears. If more than one score has the same highest count,
choose the smallest score.
Input format. The first line contains an integer
N. The second line contains N integers
A₁ A₂ … A_N.
Output format. One line containing two integers: the most frequent score, then the number of times it appears.
Constraints.
- 1 ≤
N≤ 200,000 - 1 ≤
Aᵢ≤ 1000
Sample input
7
3 1 3 2 1 3 2
Sample output
3 3
Explanation. The score 3 appears three times, the
scores 1 and 2 twice each. Note the constraint Aᵢ ≤ 1000:
there are far fewer distinct values than data items, and that is the
hint for how to solve it.
Starter file:
exercises/07/osn/01-modus-nilai.cpp
Problem 2 — Smallest Difference
Given N integers, determine the smallest possible
difference between two numbers at different positions. The difference is
always computed as a non-negative number.
Input format. The first line contains an integer
N. The second line contains N integers.
Output format. One line containing the smallest difference.
Constraints.
- 2 ≤
N≤ 200,000 - −10¹⁸ ≤
Aᵢ≤ 10¹⁸
Sample input
5
10 2 7 3 9
Sample output
1
Explanation. The pair (2, 3) and the pair (9, 10) both have a difference of 1, and no pair is closer. If two numbers are exactly equal, the answer is 0.
Starter file:
exercises/07/osn/02-selisih-terkecil.cpp
Problem 3 — Largest Row and Column
A table with R rows and C columns contains
integers. Determine the number of the row whose sum is largest and the
number of the column whose sum is largest. Rows and columns are numbered
starting from 1. If there is a tie, choose the smallest number.
Input format. The first line contains two integers
R and C. Each of the next R lines
contains C integers.
Output format. One line containing two integers: the row number and the column number, separated by a single space.
Constraints.
- 1 ≤
R,C≤ 500 - −10⁶ ≤ table entry ≤ 10⁶
Sample input
2 3
1 2 3
4 5 6
Sample output
2 3
Explanation. The sum of the first row is 1 + 2 + 3 = 6, of the second row 4 + 5 + 6 = 15, so the largest row is row 2. The column sums are 5, 7, and 9 respectively, so the largest column is column 3.
Starter file:
exercises/07/osn/03-baris-kolom-terbesar.cpp
Stuck or already done? Open the homework solutions page.