Skip to main content

Functions

Functions help you organize code, avoid repetition, and make your solutions cleaner.

Defining functions

// Function with return value
int add(int a, int b) {
    return a + b;
}

// Void function (no return)
void printArray(vector<int>& v) {
    for (int x : v) cout << x << " ";
    cout << "\n";
}

// Function with default parameters
int power(int base, int exp = 2) {
    int result = 1;
    for (int i = 0; i < exp; i++) result *= base;
    return result;
}

Pass by reference vs value

// Pass by value — creates a copy (slow for large data)
void modify(int x) {
    x = 10; // Original is NOT changed
}

// Pass by reference — modifies the original (fast)
void modify(int& x) {
    x = 10; // Original IS changed
}

// Pass by const reference — fast, read-only
int sum(const vector<int>& v) {
    int s = 0;
    for (int x : v) s += x;
    return s;
}
Always pass vectors and strings by reference (&) to avoid copying. Use const& if you don’t need to modify them.

Structs

Structs let you group related data together.
struct Point {
    int x, y;
};

struct Student {
    string name;
    int grade;
    double gpa;
};

int main() {
    Point p = {3, 4};
    cout << p.x << " " << p.y;

    Student s;
    s.name = "Ahmed";
    s.grade = 95;

    // Array of structs
    vector<Student> students(n);
    for (int i = 0; i < n; i++) {
        cin >> students[i].name >> students[i].grade;
    }
}

Sorting structs

struct Event {
    int start, end;
};

// Custom comparator
bool cmp(Event a, Event b) {
    return a.end < b.end; // Sort by end time
}

sort(events.begin(), events.end(), cmp);

// Or with lambda
sort(events.begin(), events.end(), [](Event& a, Event& b) {
    return a.start < b.start;
});

Pairs and tuples

// Pair — holds two values
pair<int, int> p = {3, 5};
cout << p.first << " " << p.second;

// make_pair
pair<string, int> student = make_pair("Ahmed", 95);

// Vector of pairs
vector<pair<int, int>> edges;
edges.push_back({1, 2});

// Pairs sort by first, then by second automatically
sort(edges.begin(), edges.end());

// Tuple — holds three or more values
tuple<int, int, int> t = {1, 2, 3};
cout << get<0>(t) << get<1>(t) << get<2>(t);

Time complexity

Understanding time complexity tells you whether your solution will pass within the time limit.

Big-O notation

ComplexityNameMax n (≈ 1 sec)Example
O(1)ConstantAnyArray access, math
O(log n)Logarithmic10¹⁸Binary search
O(n)Linear10⁸Single loop
O(n log n)Linearithmic10⁶Sorting
O(n²)Quadratic10⁴Nested loops
O(n³)Cubic500Triple nested loops
O(2ⁿ)Exponential20–25Subsets
O(n!)Factorial10–12Permutations

How to estimate

The rule of thumb: 10⁸ simple operations per second.
n ≤ 10     → O(n!) or O(2ⁿ) is fine
n ≤ 20     → O(2ⁿ) is fine
n ≤ 500    → O(n³) is fine
n ≤ 5000   → O(n²) is fine
n ≤ 10⁶    → O(n log n) is needed
n ≤ 10⁸    → O(n) is needed
n > 10⁸    → O(log n) or O(1) is needed
Before coding, always check the constraints and calculate if your approach is fast enough. A TLE (Time Limit Exceeded) means your algorithm is too slow.

Analyzing loops

// O(n)
for (int i = 0; i < n; i++) { ... }

// O(n²)
for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++) { ... }

// O(n log n)
sort(v.begin(), v.end());

// O(log n)
while (n > 0) { n /= 2; }

Practice

Sheet 4: Functions, Structs & Time Complexity

Practice problems for this tutorial.