Vault Notes

Programming & Data Structures

Subject 3 of 10 Β· C fundamentals, pointers, the classic data structures, and the complexity table that ties them together.

1. C Programming Basics

Data types and variables

A variable is a named storage location; its data type decides how many bytes it occupies and what operations are legal. Declaration introduces the variable, definition allocates storage, initialization gives it a value. A const qualifier makes a variable read-only after initialization.

Storage classes

ClassStorageScopeLifetime
autoStackLocal to blockBlock execution
registerCPU register (hint)Local to blockBlock execution
staticData segmentLocal to block/fileWhole program
externData segmentGlobal (all files)Whole program

Scope is where a name is visible; lifetime is how long its storage exists. A static local keeps its value between calls β€” the classic function-call counter. Block scope: a variable declared inside { } dies at the closing brace. Shadowing: an inner declaration hides an outer one with the same name.

2. Operators & Expressions

The operator families

FamilyOperators
Arithmetic+ - * / % (plus ++ --)
Relational< <= > >= == !=
Logical&& || !
Bitwise& | ^ ~ << >>
Assignment= += -= *= /= %=

Short-circuit evaluation: in a && b, if a is false, b is never evaluated. Same idea for ||. This makes guards like if (p && p->data) safe.

Precedence traps

! > * / % > + - > relational > == > && > || > assignment. When in doubt, add parentheses β€” they're free and unambiguous.

3. Control Flow

if / else if / else for branching, switch for multi-way dispatch on an integer or character constant (every case needs its own break unless fall-through is intended). Loops: while (test first), do-while (runs at least once), for (init; condition; update). break exits the loop, continue skips to the next iteration.

switch (grade) {
    case 'A': printf("Excellent"); break;
    case 'B': printf("Good"); break;
    default:  printf("Keep trying");
}

4. Recursion

Recursion solves a problem by solving smaller instances of itself. Every recursive function needs a base case (stops the recursion) and a recursive case (shrinks toward the base). Each call gets its own stack frame β€” that's why deep recursion can overflow the stack.

// Factorial
int fact(int n) {
    if (n <= 1) return 1;          // base case
    return n * fact(n - 1);        // recursive case
}

// Fibonacci
int fib(int n) {
    if (n <= 1) return n;
    return fib(n - 1) + fib(n - 2);
}

Tower of Hanoi: move n disks from source to destination using an auxiliary peg. Move nβˆ’1 disks aside, move the largest, move nβˆ’1 back on top β€” total moves = 2n βˆ’ 1.

GCD (Euclid): gcd(a, b) = gcd(b, a % b), base case gcd(a, 0) = a.

5. Pointers & Memory

Pointer basics

A pointer holds a memory address. & takes an address, * dereferences it. Pointer arithmetic moves in units of the pointed-to type: p + 1 jumps one whole element, not one byte.

Pointer typeWhat it is
NULL pointerPoints nowhere β€” the safe "uninitialized" state
Void pointer (void *)Generic address; cast before dereferencing
Wild pointerUninitialized pointer β€” points at garbage, dereferencing is undefined behavior
Dangling pointerPoints at memory already freed β€” the classic use-after-free bug
Rule of thumb

Initialize every pointer (to NULL if you have nothing yet), and set it to NULL right after free(). Those two habits kill most pointer bugs.

Dynamic memory allocation

int *p = malloc(n * sizeof(int));   // uninitialized block
int *q = calloc(n, sizeof(int));    // zero-initialized block
p = realloc(p, m * sizeof(int));    // resize (may move the block)
free(p); p = NULL;                  // release and null out

A memory leak is allocated memory never freed β€” the program slowly bleeds RAM until it dies or gets killed.

6. Arrays & Strings

An array is a contiguous block of same-type elements with O(1) random access. In C, a string is just a char array terminated by '\0'.

1D array addressAddress(i) = Base + i Γ— size
i = index, size = bytes per element

2D arrays: row-major vs column-major

Row-major (C, C++)Address(i, j) = Base + (i Γ— cols + j) Γ— size
Column-major (Fortran, MATLAB)Address(i, j) = Base + (j Γ— rows + i) Γ— size

Row-major lays each row contiguously, so iterating row-by-row is cache-friendly in C. Get the order wrong and performance falls off a cliff on large matrices.

7. Linked Lists

Singly linked list

Each node holds data plus a pointer to the next node. Memory is allocated per node at runtime (no fixed capacity), but there's no random access β€” reaching element k costs O(k).

struct Node {
    int data;
    struct Node *next;
};
OperationTime
Insert at headO(1)
Delete at headO(1)
Insert/delete at position kO(k) β€” must walk there first
SearchO(n)

Doubly linked list

Adds a prev pointer to each node, so traversal runs both directions and deletion is O(1) once you hold the node. The price: extra memory per node and more pointer updates on every insert/delete (four pointer fixes for a middle insertion).

struct DNode {
    int data;
    struct DNode *prev;
    struct DNode *next;
};

8. Stacks

LIFO β€” last in, first out. You only ever touch the top: push adds, pop removes and returns, peek looks without removing, isEmpty checks.

void push(int x) {
    if (top == MAX - 1) { /* stack overflow */ return; }
    stack[++top] = x;
}
int pop(void) {
    if (top == -1) { /* stack underflow */ return -1; }
    return stack[top--];
}

Applications: function call management (the call stack), expression evaluation and infix→postfix conversion, undo/redo, backtracking (DFS is a stack in disguise).

Tower of Hanoi, again

The classic recursive demo needs 2n βˆ’ 1 moves for n disks. Each recursive call is a stack frame β€” run it for n = 64 and the universe ends first.

9. Queues

FIFO β€” first in, first out. enqueue adds at the rear, dequeue removes from the front.

Circular queue

Wraps the array around so freed front slots get reused β€” no shifting needed. Indices advance with modulo arithmetic:

Double-ended queue (Deque)

Insert and delete at both ends. Variants: input-restricted (insert at one end only) and output-restricted (delete at one end only). Powers sliding-window algorithms and work-stealing schedulers.

Applications: BFS traversal, process scheduling (ready queue), buffering (I/O queues, print spooling), resource management.

10. Trees

Terminology

TermMeaning
RootThe top node; the only node with no parent
LeafA node with no children
DepthEdges from the root to a node
HeightLongest downward path to a leaf (a lone node has height 0)
DegreeNumber of children of a node

Binary tree properties

Traversals

TraversalOrderYields
InorderLeft β†’ Root β†’ RightSorted sequence on a BST
PreorderRoot β†’ Left β†’ RightPrefix expression; tree copy structure
PostorderLeft β†’ Right β†’ RootPostfix expression; safe deletion order
Level orderLevel by level (uses a queue)Breadth-first visit order

Binary Search Tree (BST)

The ordering invariant: left subtree < root < right subtree, recursively. Search, insert, and delete all run in O(h) where h is the height β€” O(log n) on average, O(n) if insertions arrive sorted and the tree degenerates into a line. Deletion cases: leaf (just remove), one child (bypass), two children (replace with inorder successor, then delete it).

11. Balanced BSTs & AVL

Balance is what keeps tree operations at O(log n). Two families: height-balanced trees (AVL, Red-Black) and multiway trees (B-trees).

B-trees

A B-tree of order m: every node holds at most mβˆ’1 keys and m children; every node except the root holds at least ⌈m/2βŒ‰βˆ’1 keys; the root has at least 2 children unless it's a leaf; all leaves sit at the same level. That's what makes them the backbone of databases and filesystems β€” one node = one disk block.

AVL trees

A BST with a strict balance rule: for every node, |height(left) βˆ’ height(right)| ≀ 1. The balance factor is stored per node; insertions and deletions that violate it trigger rotations:

CaseRotation
Left-Left (LL)Single right rotation
Right-Right (RR)Single left rotation
Left-Right (LR)Left rotation on child, then right rotation
Right-Left (RL)Right rotation on child, then left rotation

AVL keeps the height at ~1.44 log n, so every operation stays O(log n) with slightly costlier inserts than Red-Black trees β€” pick AVL for read-heavy workloads.

12. Heaps

A binary heap is a complete binary tree (every level full except possibly the last, filled left to right) with the heap property: in a min-heap every parent ≀ its children (root is the minimum); in a max-heap every parent β‰₯ its children. Stored compactly in an array: children of index i at 2i+1, 2i+2.

OperationTime
InsertO(log n) β€” append at end, bubble up
Extract min/maxO(log n) β€” take root, move last element up, heapify down
Peek min/maxO(1)
Build heap (heapify)O(n)

Uses: priority queues, heap sort (O(n log n), in-place), scheduling. Note: a heap is not a BST β€” only the root is ordered; siblings are unrelated.

13. Hashing

Hashing maps keys to table indices with a hash function, giving average O(1) search, insert, and delete. Common functions: division (h(k) = k mod m), multiplication (multiply by a constant, take the fractional part), and universal hashing (randomized family to defeat adversarial keys).

Load factorΞ± = n / m
n = elements stored, m = table slots. Free space = (1 βˆ’ Ξ±) Γ— 100%

Lower Ξ± β†’ fewer collisions β†’ better performance. When Ξ± crosses a threshold (commonly ~0.7 for open addressing), the table rehashes into a bigger one. Hard constraint: with open addressing, Ξ± can never exceed 1 β€” everything must fit inside the table.

Collision resolution: chaining

Each slot holds a linked list of all keys that hash there β€” colliding elements simply join the chain. No table-overflow problem (chains grow indefinitely), simple to implement, and performance degrades gracefully as Ξ± rises.

MethodAvg successful search
Chaining1 + Ξ±/2
Linear probingΒ½(1 + 1/(1 βˆ’ Ξ±))
Exam favorite

Chaining beats linear probing once Ξ± > 0.5, which is why it's the right pick for high-utilization tables. Linear probing suffers primary clustering β€” occupied slots clump together and probes get longer.

14. Graphs

A graph is vertices plus edges. Adjacency matrix: a VΓ—V matrix with a 1 where an edge exists β€” O(1) edge lookup, but O(VΒ²) space even for sparse graphs. Adjacency list: each vertex stores its neighbors β€” O(V + E) space, the default for sparse graphs.

Traversal

15. Complexity Cheat Sheet

Average-case access / search / insert / delete, worst case, and space β€” the table every exam draws from:

Data StructureAccessSearchInsertDeleteSpace
ArrayO(1)O(n)O(n)O(n)O(n)
StackO(n)O(n)O(1)O(1)O(n)
Doubly Linked ListO(n)O(n)O(1)O(1)O(n)
Skip ListO(log n)O(log n)O(log n)O(log n)O(n log n)
Hash Tableβ€”O(1)O(1)O(1)O(n)
Binary Search TreeO(log n)O(log n)O(log n)O(log n)O(n)
AVL TreeO(log n)O(log n)O(log n)O(log n)O(n)
Red-Black TreeO(log n)O(log n)O(log n)O(log n)O(n)
B-TreeO(log n)O(log n)O(log n)O(log n)O(n)
Read the row, not just the cell

Arrays: instant access, expensive insert/delete (shifting). Linked lists: cheap insert/delete at a known position, slow search. Hash tables: best for key-based lookup. BST degrades to O(n) unbalanced β€” AVL, Red-Black, and B-trees exist to guarantee the O(log n). Skip lists are the probabilistic middle ground: expected O(log n) with far simpler code.

← Back to all subjects