CEN207 Data Structures · Week 2

Arrays, Matrices, and Linked Lists

CEN207 Data Structures — Week 2

Asst. Prof. Dr. Uğur CORUH · Fall 2026–2027

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Today's plan (3 hours)

Hour Topic
1 Arrays: insert/delete, dynamic growth Anim 1–2 · 2-D layout, rotation, rearrange Anim 3–5
2 Sparse matrices Anim 6–8 · lists: node/head/NULL, singly ops Anim 9–12
3 Doubly, circular, Josephus, XOR, skip list Anim 13–17 · arrays vs. lists

Learning outcomes: LO.1 (explain fundamental data structures) · LO.2 (analyze complexity) · LO.7 (choose the right structure)

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

This week's concepts — where

Concept Where
Arrays: shifting, dynamic growth Section 1
2-D arrays, rotation, rearrangement Sections 2–3
Sparse matrices Section 4
Singly/doubly/circular lists Sections 5–8
XOR list, skip list, arrays vs. lists Sections 9–11
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

How the code examples work

  • Every idea has a complete C and Java program
  • C: gcc -std=c11 -Wall -Wextra -o /tmp/x file.c && /tmp/x
  • Java: javac -d /tmp/j File.java && java -cp /tmp/j File
  • Sources: code/week-02/c/ and code/week-02/java/
  • Each program's expected output is in the week notes
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Recap — Week 1: memory and pointers

  • A variable is a labeled box in memory
  • A pointer stores an address — it points at a box
  • malloc(n): reserve n bytes, returns an address
  • free(p): give the box back; then set p = NULL
  • NULL: a pointer that points at nothing
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Map of the week — at a glance

Arrays & matrices Linked lists
Shifting, dynamic growth Node, head, NULL
Row-major, rotation, rearrange Singly, doubly, circular
Sparse: triplet, transpose, add Josephus, XOR, skip list
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

1. Arrays in Memory: Insertion, Deletion, Growth

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

An array is n boxes in a row. Insert one
value in the middle — what has to move
before the new value can even go in?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Intuition — a row of numbered lockers

  • Each box sits at index × box size from the start
  • arr[k] is pure arithmetic — no searching needed
  • But lockers do not slide apart by themselves
  • Making room means moving every locker after it
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

size vs. capacity

  • A fixed array reserves CAP boxes up front
  • size tracks how many boxes are actually used
  • insert_at(k, v): make room at index k
  • delete_at(k): close the gap at index k
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Array insert and delete, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — overflow: the array is full

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — insert_at()

bool insert_at(int k, int v) {
    if (size == CAP)
        return false;          /* full: overflow */
    for (int i = size; i > k; i--)
        arr[i] = arr[i - 1];   /* shift right */
    arr[k] = v;
    size++;
    return true;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — delete_at()

bool delete_at(int k) {
    if (size == 0)
        return false;          /* empty: underflow */
    for (int i = k; i < size - 1; i++)
        arr[i] = arr[i + 1];   /* shift left */
    size--;
    return true;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • k = 0: shifts every element — O(n)
  • k = size (append): no shift at all — O(1)
  • Mistake: forgetting the overflow/underflow check first
  • Mistake: shifting in the wrong direction, overwriting data
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

You insert 5 values, always at index 0.
How many total element-shifts happen?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

0+1+2+3+4 = 10 shifts. Each front-insert
shifts every element already there — the
classic O(n) worst case, repeated five times.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

But what if the size is unknown?

A fixed array's CAP is a hard ceiling.
A dynamic array grows its own capacity
on demand, instead of rejecting the insert.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Dynamic array growth, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — shrinking back down

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — da_resize()

static void da_resize(DynArray *a, int new_cap) {
    int *fresh = malloc(new_cap * sizeof(int));
    for (int i = 0; i < a->size; i++)
        fresh[i] = a->data[i];   /* copy every value */
    free(a->data);               /* old block freed */
    a->data = fresh;
    a->cap = new_cap;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — da_append()

void da_append(DynArray *a, int v) {
    if (a->size == a->cap) {
        int new_cap = a->cap * 2;   /* growth factor 2 */
        da_resize(a, new_cap);
    }
    a->data[a->size++] = v;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • A growing append copies size elements: O(n)
  • Most appends just write one slot: O(1)
  • Over n appends, total copies stay under 2n
  • So the amortized cost per append is O(1)
  • Mistake: treating every single append as O(n)
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

Starting from cap = 1 and doubling, how
many times does the array grow while 100
values are appended one by one?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

7 times (1→2→4→8→16→32→64→128).
log2(100) ≈ 6.6, rounded up — growth is
logarithmic in the final size, not linear.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

2. 2-D Arrays: Row-Major vs Column-Major

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

Memory is one long row of bytes. A matrix
looks two-dimensional — rows and columns.
How does mat[i][j] become one address?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Intuition — a bookshelf, one shelf at a time

  • Picture the matrix's rows laid end to end
  • Row 0's cells, then row 1's cells, and so on
  • That layout is called row-major order
  • Some languages (Fortran, MATLAB) lay out columns first
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

The address formulas

  • Row-major: addr(i,j) = i * COLS + j
  • Column-major: addr(i,j) = j * ROWS + i
  • C and Java: row-major; Fortran, MATLAB: column-major
  • The formula is arithmetic — no searching at all
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Matrix in memory, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — column-major storage, row-by-row walk

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — addr() and a row-major walk

int mat[ROWS][COLS];
/* address of mat[i][j], in ints from the start */
int addr(int i, int j) {
    return i * COLS + j;   /* row-major */
}

void traverse_row_major(int mat[ROWS][COLS]) {
    for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++)
            visit(mat[i][j]);
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • Address lookup: pure arithmetic — O(1)
  • Row-major walk, row-major storage: every step Δ=1
  • Wrong layout for the walk: a jump every step
  • Mistake: assuming every language is row-major
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

A row-major matrix has 5 columns. mat[3][2]
sits at address 17. What is mat[3][3]'s address?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

18. Moving one column right adds exactly
1 in row-major order — the next cell is
always the very next address.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

3. Rotation and Rearrangement: Two Pointers

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

Rotate a 12-value array left by 4, using
no second array. Where would you even
start, with only O(1) extra space allowed?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Intuition — reversal, three times

  • Reverse the first part, reverse the rest
  • Then reverse the whole array once more
  • Three reversals land every value exactly right
  • No second array, no shifting one step at a time
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

rotate_left: three reversals

  • d = d % n — the real rotation amount
  • Reverse arr[0..d-1]
  • Reverse arr[d..n-1]
  • Reverse arr[0..n-1] — done
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Array rotation, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — d larger than n

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — reverse()

void reverse(int arr[], int lo, int hi) {
    while (lo < hi) {
        int tmp = arr[lo];
        arr[lo] = arr[hi];
        arr[hi] = tmp;
        lo++;
        hi--;
    }
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — rotate_left()

void rotate_left(int arr[], int n, int d) {
    if (n == 0) return;        /* empty: nothing to do */
    d = d % n;
    reverse(arr, 0, d - 1);    /* first d */
    reverse(arr, d, n - 1);    /* the rest */
    reverse(arr, 0, n - 1);    /* whole array */
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • Three passes over the array, each O(n) — still O(n)
  • O(1) extra space: no second array anywhere
  • Mistake: forgetting d = d % n first
  • Mistake: off-by-one bounds inside reverse
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Rearranging: two pointers close in

  • Goal: negatives left, non-negatives right
  • left skips values already negative
  • right skips values already non-negative
  • When both stop, swap and step inward
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Array rearrangement, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — already segregated

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — segregate()

void segregate(int arr[], int n) {
    int left = 0, right = n - 1;
    while (left < right) {
        while (left<right && arr[left]<0) left++;
        while (left<right && arr[right]>=0) right--;
        if (left < right) {
            int tmp = arr[left];
            arr[left] = arr[right];
            arr[right] = tmp;
            left++; right--;
        }
    }
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • Both pointers move inward only — O(n) total
  • One pass, O(1) extra space
  • Mistake: treating 0 as negative — it is not
  • Mistake: forgetting the left < right guard inside
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

After segregate, is the array sorted
within each half, or just split in two?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

Just split. Negatives end up left of
non-negatives, but neither half is sorted —
segregate never compares values to order them.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

4. Sparse Matrices

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

A 1000×1000 matrix has a million cells.
Only 200 are nonzero. Why store the other
999,800 zeros at all?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Intuition — a mostly-empty parking lot

  • Most spaces sit empty, all day, every day
  • A clipboard listing only the occupied spots suffices
  • Position + value is all you need to remember
  • That clipboard is the triplet representation
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

The triplet: (row, col, value)

  • One entry per nonzero cell, nothing else
  • Scan row-major, skip every zero found
  • A sparse matrix's triplet table can be tiny
  • Rebuilding the dense matrix just needs the triplets
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Sparse matrix as triplets, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — an all-zero matrix

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — to_triplets()

int to_triplets(int mat[ROWS][COLS], Triplet out[]) {
    int k = 0;
    for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++)
            if (mat[i][j] != 0) {
                out[k].row = i;
                out[k].col = j;
                out[k].value = mat[i][j];
                k++;
            }
    return k;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity

  • Scanning the dense matrix: O(rows × cols)
  • Output size equals the nonzero count, never more
  • A very sparse matrix: triplets are far smaller
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Fast transpose: no re-sorting needed

  • Naive transpose: swap (row,col), then sort — O(nnz log nnz)
  • Better: count nonzeros per column first
  • Turn counts into starting positions (a prefix sum)
  • One more pass places every triplet, already sorted
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Fast transpose, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — a fully dense matrix

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — counting and prefix sums

int count[COLS] = {0};
int pos[COLS];
for (int i = 0; i < nnz; i++)
    count[a[i].col]++;        /* per column */
pos[0] = 0;
for (int c = 1; c < COLS; c++)
    pos[c] = pos[c - 1] + count[c - 1];
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — placing every triplet

for (int i = 0; i < nnz; i++) {
    int c = a[i].col;
    int p = pos[c]++;
    b[p].row = a[i].col;    /* row/col swap */
    b[p].col = a[i].row;
    b[p].value = a[i].value;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • Two passes, O(nnz + COLS) — no sorting at all
  • Far faster than a general sort's O(nnz log nnz)
  • Mistake: skipping the prefix sum — output lands unsorted
  • Mistake: forgetting to swap row and col
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Adding two sparse matrices: a merge

  • Both triplet lists already row-major sorted
  • Walk both together, like merge sort's merge step
  • Earlier (row,col) wins and is copied through
  • Same (row,col) in both: add the values
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Sparse matrix addition, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — values that cancel to zero

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — the merge loop

while (i < na && j < nb) {
    if (/* … a[i]'s (row,col) comes first … */) {
        out[k++] = a[i++];
    } else if (/* … b[j]'s comes first … */) {
        out[k++] = b[j++];
    } else {
        int sum = a[i].value + b[j].value;
        if (sum != 0) /* … out[k++] = sum entry … */;
        i++; j++;
    }
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • One merge pass: O(na + nb), no re-sorting
  • Never touches a cell absent from both lists
  • Mistake: forgetting to drop a cell when the sum is 0
  • Mistake: assuming unsorted triplet lists still merge correctly
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

Cell (1,2) holds 6 in matrix A and −6 in
matrix B. What ends up in the sum's triplet list?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

Nothing. 6 + (-6) = 0, and a zero-sum
cell is dropped entirely — the triplet list
only ever holds nonzero values.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

5. Linked Lists: Node, Head, NULL

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

Every insert into an array can shift up to
n elements. Is there a structure where
inserting never moves existing values?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A short history

  • 1956 — John McCarthy, Lisp's cons cell
  • A cons cell: a value, plus a pointer to the next
  • Six decades later, every language still uses it
  • C's struct Node { data; next; } is the same idea
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Intuition — a treasure hunt

  • Each clue tells you where the next clue is
  • You never see the whole map at once
  • Follow one pointer, arrive, read, follow the next
  • The last clue says "nothing here" — NULL
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

The whole idea, one struct

typedef struct Node {
    int data;
    struct Node *next;
} Node;
  • One value, one pointer — that is a linked list
  • No shifting: a new node just gets a pointer
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

head and the NULL terminator

  • head is the only fixed reference into the list
  • Every other node is reached by following next
  • The last node's next is NULL — "no more nodes"
  • An empty list is just head == NULL
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

The "inception": a struct containing itself

  • struct Node has a field of type struct Node *
  • That is not infinite recursion — it is a pointer
  • A pointer's size is fixed, known before Node is complete
  • This is why struct Node *next; compiles, but Node next; cannot
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Common mistakes

  • Forgetting to check for NULL before ->next
  • Confusing a node (the struct) with a pointer to it
  • Writing Node next; where a pointer was needed
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

Why must next be declared as struct Node *,
never as a plain struct Node?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

A plain Node field would need the size
of Node to already be known — but Node
is not finished being defined yet. A pointer's
size never depends on that.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

6. Singly Linked Lists: Insert, Delete, Search, Reverse

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

Three insert positions — head, tail, and
"after a given node" — cost very different
amounts. Which is cheap, and which is not?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Three ways to insert

  • insert_head: new node points at the old head — O(1)
  • insert_tail: no tail pointer here — walk to the end
  • insert_after(prev, v): two pointer writes, in order
  • Getting that order backward breaks the list
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Singly-list insertion, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — the two steps, swapped

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — insert_head()

Node *insert_head(Node *head, int value) {
    Node *n = malloc(sizeof(Node));
    n->data = value;
    n->next = head;    /* points at the old head */
    return n;           /* new node is head now */
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — insert_after(): order matters

void insert_after(Node *prev, int value) {
    Node *n = malloc(sizeof(Node));
    n->data = value;
    n->next = prev->next;   /* STEP 1: new first */
    prev->next = n;         /* STEP 2: then link */
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • insert_head: O(1) — no walking at all
  • insert_tail (no tail pointer): walks to the end — O(n)
  • insert_after: O(1) once prev is already found
  • Mistake: swapping STEP 1 and STEP 2 — cuts the list
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Deleting by value

  • Special case: the value is at the head
  • Otherwise: prev/cur walk together, searching
  • Found: prev->next = cur->next — the bypass arrow
  • Then free(cur) — the node is gone
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Singly-list deletion, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — deleting the tail, then a missing value

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — delete_value()

Node *delete_value(Node *head, int value,
                    bool *removed) {
    *removed = false;
    if (head == NULL) return NULL;
    if (head->data == value) {   /* delete head */
        Node *tmp = head;
        head = head->next;
        free(tmp);
        *removed = true;
        return head;
    }
    /* … prev/cur scan, bypass, free(cur) … */
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • Deleting the head: O(1), no search needed
  • Deleting anywhere else: O(n) to find it
  • Mistake: forgetting the head is a special case
  • Mistake: advancing cur before saving prev
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Linear search: no shortcuts

  • No index arithmetic — a list has no arr[k]
  • Walk from head, comparing one node at a time
  • Found: return its position; exhausted: return -1
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Singly-list search, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — searching an empty list

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — search()

int search(Node *head, int value) {
    int index = 0;
    for (Node *cur = head; cur != NULL;
         cur = cur->next) {
        if (cur->data == value)
            return index;      /* found here */
        index++;
    }
    return -1;                 /* not found */
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity

  • Every search: O(n) — no index to jump to
  • Contrast: arr[k] on an array is O(1)
  • This exact gap drives the section 11 comparison
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Reversing in place: three pointers

  • prev trails, curr leads, next looks ahead
  • Save next before overwriting curr->next
  • Flip curr->next to point at prev
  • Both pointers then step one node forward
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Singly-list reversal, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — just two nodes

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — reverse()

Node *reverse(Node *head) {
    Node *prev = NULL;
    Node *curr = head;
    while (curr != NULL) {
        Node *next = curr->next;  /* save rest */
        curr->next = prev;         /* flip arrow */
        prev = curr;
        curr = next;
    }
    return prev;                   /* new head */
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity

  • One pass, one flip per node — O(n) time
  • No extra nodes allocated — O(1) space
  • Compare: reversing an array also needs no extra pointers
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

If you flip curr->next to prev before
saving curr->next into a temporary next
variable, what breaks?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

The rest of the list is lost. Once
curr->next points backward, there is no
way left to reach the nodes that used to
follow it.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

7. Doubly Linked Lists

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

A singly list can only walk forward.
What has to change to walk backward too?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Intuition — a two-way street

  • Singly list: a one-way street, forward only
  • Doubly list: a two-way street, both directions
  • Every node carries two signs: next and prev
  • Turning around costs nothing extra — just follow prev
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

insert_after: four pointers to fix, not two

  • Singly list's insert_after: fix 2 pointers
  • Doubly list: also fix the new node's prev
  • And the old next's prev, if it exists
  • If cur was the tail, update list->tail too
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Doubly-list insertion, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — inserting right after the tail

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — insert_after()

bool insert_after(List *list, int target, int v) {
    for (Node *cur = list->head; cur; cur = cur->next) {
        if (cur->data == target) {
            Node *n = malloc(sizeof(Node));
            n->data = v; n->prev = cur; n->next = cur->next;
            if (cur->next) cur->next->prev = n;
            else list->tail = n;   /* cur was the tail */
            cur->next = n;
            return true;
        }
    }
    return false;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — delete_value(): no scan for prev

bool delete_value(List *list, int value) {
    for (Node *cur = list->head; cur; cur = cur->next) {
        if (cur->data == value) {
            if (cur->prev) cur->prev->next = cur->next;
            else list->head = cur->next;
            if (cur->next) cur->next->prev = cur->prev;
            else list->tail = cur->prev;
            free(cur);
            return true;
        }
    }
    return false;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • Finding the target: O(n), same as singly
  • Relinking, once found: O(1) either direction
  • Mistake: forgetting to update tail after the tail moves
  • Mistake: skipping the new node's prev link
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

Why does a doubly list's delete_value
need no separate prev-tracking variable,
unlike the singly version?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

Every node already stores its own prev.
The singly version had to track prev by
hand while scanning; the doubly version just
reads cur->prev directly.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

8. Circular Lists and the Josephus Problem

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

What if the last node's next pointed
back at the first node, instead of NULL?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Intuition — people seated in a circle

  • No "first" seat and no "last" seat, really
  • Walk far enough and you are back where you started
  • A circular list has no NULL to stop a walk
  • Something else — a count, a condition — must stop it
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

One pointer, no separate head

  • tail points at the last-inserted node
  • tail->next is the head — no extra field
  • A single node points at itself — a tiny loop
  • Deleting needs a forward scan — there is no prev
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Circular-list operations, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — the last node, deleted

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — insert_tail()

Node *insert_tail(Node *tail, int value) {
    Node *n = malloc(sizeof(Node));
    n->data = value;
    if (tail == NULL) {
        n->next = n;       /* points at itself */
        return n;
    }
    n->next = tail->next;  /* new node -> old head */
    tail->next = n;        /* old tail -> new node */
    return n;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • Insert at the tail: O(1), same trick as before
  • Delete by value: O(n) scan — no prev link
  • Mistake: checking cur == NULL to stop a traversal
  • A circular list needs a step count, not a NULL
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A short history

  • c. 67 CE — Flavius Josephus, siege of Yodfat
  • Legend: 41 soldiers, every 3rd one eliminated
  • Josephus reportedly placed himself at the survivor's spot
  • A single circular list solves the whole puzzle today
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Every k-th person is eliminated

  • n people stand in a circle, numbered 1..n
  • Count k-1 steps forward from the last survivor
  • Eliminate the person you land on
  • Repeat until only one person remains
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

The Josephus problem, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — a circle of one

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — josephus()

int josephus(int n, int k) {
    Node *cur = head;
    int remaining = n;
    while (remaining > 1) {
        for (int s = 1; s < k; s++) {  /* k-1 steps */
            prev = cur;
            cur = cur->next;
        }
        prev->next = cur->next;   /* remove cur */
        cur = prev->next;
        remaining--;
    }
    return cur->id;                /* the survivor */
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity

  • Simulating directly: O(n · k) in the worst case
  • A closed-form recurrence gives the survivor in O(n)
  • J(1)=0, J(n) = (J(n-1) + k) mod n
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

With k = 1, every count is just "the next
person". Who survives a circle of 10?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

Person 10 — the last one in line. With
k=1 there is no skipping at all: elimination
just proceeds in plain order, 1 through 9.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

9. XOR Linked Lists: A Curiosity

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

A doubly list spends two pointer fields
per node. Could one field somehow hold
both neighbors at once?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

npx = prev XOR next

  • Store one field: prev's address XOR next's address
  • Arriving from a known neighbor known, recover the other:
  • other = npx XOR known
  • XOR quietly "cancels" the neighbor you came from
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

XOR list traversal, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — inserting at the tail of an empty list

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — the struct and the XOR trick

typedef struct Node {
    int data;
    uintptr_t npx;   /* XOR of prev and next */
} Node;

static Node *xor_node(uintptr_t npx, Node *known) {
    return (Node *)(npx ^ (uintptr_t)known);
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — traverse_forward()

void traverse_forward(Node *head) {
    Node *prev = NULL, *cur = head;
    while (cur != NULL) {
        printf(" %d", cur->data);
        Node *next = xor_node(cur->npx, prev);
        prev = cur; cur = next;
    }
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity

  • Insert at head or tail: O(1), same as before
  • Every traversal step: one extra XOR — still O(1) each
  • Memory saved: one pointer field per node
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Why this is a curiosity, not a habit

  • C does not guarantee pointer↔integer round-trips this way
  • uintptr_t casts back to a pointer are implementation-defined
  • A garbage-collected language (Java) cannot do this at all
  • Real code: use a plain doubly linked list instead
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

Why can a garbage collector never support
an XOR linked list the way C's malloc can?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

A GC needs to find every live pointer to
trace and possibly move it.
An address
hidden inside an XOR'd integer is invisible
to that scan — the GC cannot see it as a pointer.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

10. Skip Lists

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A question to start

A sorted array gets binary search, O(log n).
A sorted linked list cannot jump to the
middle. Is O(log n) search possible anyway?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

A short history

  • 1990 — William Pugh, University of Maryland
  • A randomized alternative to balanced search trees
  • Each key gets a random "coin-flip" height
  • Simpler to implement correctly than a balanced tree
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Intuition — a local road and an express lane

  • Level 0: the full sorted list — every key
  • Level 1: an express lane — only some keys
  • Start on the express lane; drop down when it overshoots
  • Fewer stops than checking every key one by one
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Search: go right, or drop down

  • At the current level, is cur->forward[i]->value < target?
  • Yes: step right, staying on this level
  • No: drop down one level and try again
  • Reaching level 0 and stepping once more: the answer
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Skip list search, step by step

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Edge case — only one node on the express lane

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Code — sl_search()

int sl_search(SkipList *sl, int value, int *cmp) {
    Node *cur = sl->header;
    for (int i = MAX_LEVEL-1; i >= 0; i--) {
        while (cur->forward[i] &&
               cur->forward[i]->value < value) {
            cur = cur->forward[i];   /* go right */
            (*cmp)++;
        }
        /* … else: drop down one level … */
    }
    cur = cur->forward[0];
    return cur && cur->value == value;
}
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Complexity and common mistakes

  • Enough express levels: O(log n) expected search
  • Every key stuck at level 0 only: degrades to O(n)
  • Mistake: stopping instead of dropping down a level
  • This demo's levels are fixed, not really flipped live
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

If every key in a skip list happened to
get level 1 only (no express lane at all),
what does search cost, in Big-O?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

O(n). With no express lane, level 0 is
the only level left — search degrades to the
same linear scan as a plain linked list.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

11. Arrays vs. Linked Lists

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Arrays vs. linked lists

Operation Array Linked list
Access by index O(1) O(n)
Insert/delete at front O(n) O(1)
Search by value O(n) O(n)
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

When to choose which

  • Need arr[k] constantly? Arrays win outright
  • Frequent front-inserts, unknown final size? Lists win
  • Cache-friendly, contiguous scans? Arrays win
  • Never shift existing data on insert? Lists win
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Mini-quiz

You are building a stack that only ever
grows and shrinks at one end. Array or list?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Answer

Either works well — both give O(1) at
one end: a dynamic array's append, or a
list's insert_head. The real deciding
factor is whether you also need arr[k].

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Summary — arrays and matrices

Idea Key fact
Array insert/delete Shifting; O(n) worst, O(1) at the end
Dynamic array Doubling; O(1) amortized append
Row-major layout i*COLS+j; match the walk to the layout
Sparse matrix Triplets; fast transpose O(nnz+COLS)
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Summary — linked lists

Idea Key fact
Singly list insert_head O(1); insert_after: order matters
Doubly / circular Two links; tail->next is the head
Josephus Circular list, one bypass per elimination
XOR / skip list One-field trick; randomized O(log n)
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

The big picture

Arrays trade flexible insertion for O(1)
indexing; linked lists trade indexing for
O(1) insertion anywhere you already are.
Every structure this week picks one side
of that same trade-off.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Self-check round

Four short questions. Think before the
answer appears on the next slide. Full
exercises are in the week notes.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

1. Inserting at index 0 in a 20-element array: how many elements move?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

All 20 — the entire array shifts right by one slot.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

2. Why is a dynamic array's append still called O(1), if growing costs O(n)?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

The O(n) growth is rare, and its cost amortizes: averaged over n appends, the total stays under 2n.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

3. In insert_after, why must n->next = prev->next happen before prev->next = n?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Reversed, prev->next already equals n by the time step one reads it — the new node ends up pointing at itself.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

4. Why does a skip list's search cost degrade to O(n) with no express lane?

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

With no upper level, level 0 is the only level left — search becomes the same one-key-at-a-time walk as a plain linked list.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

Next week

Week 3 — Stacks and Queues

LIFO and FIFO built directly on the array
and linked-list tools from this week — the
same nodes, the same shifting array, now
disciplined into one entry/exit point.

RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

References (1/2)

  • Course syllabus, Week 2: docs/syllabus/syllabus.en.md
  • Cormen, Leiserson, Rivest, Stein. Introduction to
    Algorithms
    , 4th ed. MIT Press
  • Sedgewick, Wayne. Algorithms, 4th ed. Addison-Wesley
  • Knuth. The Art of Computer Programming, Vol. 1, 3rd ed.
RTEU Computer Engineering · Fall 2026–2027
CEN207 Data Structures · Week 2

References (2/2)

  • Pugh (1990) — skip lists
  • McCarthy (1956) — Lisp cons cells
  • Flavius Josephus, The Jewish War — the elimination problem
  • williamfiset/Algorithms · Programiz DSA
RTEU Computer Engineering · Fall 2026–2027

Speaker note: Last week a box held one value, made and destroyed by malloc and free. This week two ideas grow out of that single box: many boxes side by side, reached by arithmetic — the array — and one box pointing at the next — the linked list.

Speaker note: Seventeen short animations carry the whole lecture; each appears once, exactly where its idea is introduced, and every single one gets a second look at its trickiest edge case.

Speaker note: Every term gets a full definition the first time it appears; this table just says where to find it again.

Speaker note: Open a terminal now if you want to run these live; every snippet on today's slides compiles and runs exactly as shown.

Speaker note: Everything today builds on exactly these five facts — nothing new about memory itself gets introduced this week, only new shapes built from it.

Speaker note: Every box on this map gets its own slides below, most with a short animation and a complete C/Java program.

Speaker note: Section 1 asks what it costs to change an array's contents, not just read them — and what to do when the array's size was guessed wrong.

Speaker note: Let the class guess before the next slide answers it: everything after the insertion point.

Speaker note: That O(1) index lookup is the array's whole appeal — and its whole cost shows up the moment something has to move.

Speaker note: CAP never changes in this first version; size is the only thing that moves, and only within 0..CAP.

Speaker note: Watch how many cells light up on a front insert versus an append — that difference is the whole lesson.

Speaker note: A full array rejects the insert outright rather than crash — that check has to come before any shifting starts.

Speaker note: The shift loop runs backward, from the end toward k — forward would overwrite values before they are copied.

Speaker note: This loop runs forward instead — the mirror image of insert's backward shift, and just as easy to get backward by mistake.

Speaker note: The exact same function costs anywhere from O(1) to O(n), purely depending on which index k the caller picks.

Speaker note: Let the class add it up before the next slide.

Speaker note: Ten shifts to place five values — inserting at the front is expensive precisely because it repeats the worst case every time.

Speaker note: This is exactly what Java's ArrayList and C++'s std::vector do under the hood — the same trick, industrial strength.

Speaker note: Watch the temporary row appear below the array each time it grows — that row is the copy, before it slides up to replace the old block.

Speaker note: Shrinking is optional and symmetric to growing — capacity halves once usage drops to a quarter full, to give memory back.

Speaker note: Every single existing value gets copied into the fresh block — that full copy is exactly where the O(n) cost of growing comes from.

Speaker note: The resize only happens when the block is already full — most calls skip straight to the one-line write at the end.

Speaker note: Amortized means averaged over a long run of operations — one append can be expensive, but the average never is.

Speaker note: Have the class count the doublings: 1, 2, 4, 8... before the next slide.

Speaker note: Doubling means the number of growths grows only as the logarithm of the final size — that is exactly why the total copying stays cheap.

Speaker note: Section 2 asks what a "two-dimensional" array actually looks like underneath, in memory that has only ever been one-dimensional.

Speaker note: There is no such thing as genuinely 2-D memory — every "2-D" array is really a 1-D array wearing a disguise.

Speaker note: Row-major versus column-major is purely a convention — nothing about "rows" or "columns" is more natural to memory than the other.

Speaker note: Two formulas, and every "2-D array" question this section asks comes down to picking the right one and applying it.

Speaker note: Watch the memory row below the grid — a row-major walk lands on consecutive memory cells, one step at a time.

Speaker note: The traversal order here fights the storage order — every step now jumps across memory instead of landing next door.

Speaker note: The outer loop over i and the inner loop over j exactly retrace the row-major formula, one visit per address in order.

Speaker note: The formula's cost never changes — what changes is how far apart consecutive visits land in real memory, which is what your CPU's cache actually feels.

Speaker note: Let the class apply the formula themselves before the next slide.

Speaker note: That one-step adjacency is exactly what "row-major" buys you, and exactly what a column-major walk would throw away.

Speaker note: Section 3 covers two array tricks that share one idea — moving values around using only O(1) extra space, never a second array.

Speaker note: The obvious approach — copy into a new array — is exactly the approach this section rules out.

Speaker note: This trick feels like magic the first time; watching it on the animation, one reversal at a time, is what makes it click.

Speaker note: Each reversal on its own looks wrong; only the third one, over the whole array, straightens everything back out.

Speaker note: Each reversal's result is kept as a new row below, so all three phases stay visible at once, side by side.

Speaker note: d=23 on 10 values still works, because d % n reduces it to 3 before a single reversal even begins.

Speaker note: This one helper function, called three times with three different ranges, is the entire rotation algorithm.

Speaker note: Three calls, three ranges — nothing else in this function does any work at all.

Speaker note: Three passes sounds like it should be three times slower, but three times O(n) is still just O(n).

Speaker note: This is the exact same two-pointer shape as a quicksort partition step — the same idea shows up again next semester.

Speaker note: Watch left and right walk toward each other, each skipping values that are already on the correct side.

Speaker note: The pointers still walk the whole array here, even though no swap is ever needed — the check itself still costs O(n).

Speaker note: Two inner while-loops skip past values already on the right side; only when both stop does an actual swap happen.

Speaker note: Every inner loop also checks left < right, or the two pointers could cross and read past each other.

Speaker note: Let the class think about what segregate actually compares.

Speaker note: Segregating and sorting are different jobs; this algorithm only ever asks "is it negative", never "which is bigger".

Speaker note: Section 4 asks what to do when a matrix is mostly zeros — a very common case in real scientific and graph computing.

Speaker note: A million ints is 4 MB just for one matrix that is 99.98% empty — the waste is not hypothetical.

Speaker note: Nobody photographs every empty parking space to prove it is empty — they just write down where the cars are.

Speaker note: Three numbers per nonzero cell replace an entire row of mostly zeros — the saving grows with how sparse the matrix is.

Speaker note: Watch how many cells get skipped silently — only the handful of nonzero ones ever produce a triplet row.

Speaker note: Every cell gets scanned and skipped — the triplet table stays completely empty, which is itself a valid, correct result.

Speaker note: The nested loops scan every cell regardless, but the if-check means only nonzero cells ever get written into out[].

Speaker note: The scan itself cannot be cheaper than the full matrix, but everything built from the result afterward benefits from the small output.

Speaker note: This is a genuinely clever trick — knowing in advance exactly where each entry belongs means never needing to sort at all.

Speaker note: Watch count[] fill first, then pos[] turn those counts into starting offsets, before a single output triplet is placed.

Speaker note: With every cell nonzero, fast transpose still works — it just has as many triplets to place as the matrix has cells.

Speaker note: pos[c] is the running total of every column before c — exactly where column c's triplets should start in the output.

Speaker note: pos[c]++ both reads the next free slot for column c and reserves it for the next triplet that lands there.

Speaker note: Skip the prefix-sum step and every triplet still gets placed somewhere — just not in the sorted order the algorithm promises.

Speaker note: Because both inputs are already sorted, addition never needs to search — it only ever needs to compare two current positions.

Speaker note: Watch pointers i and j advance independently, each stepping only through its own list, exactly like merging two sorted runs.

Speaker note: A cancelled cell is not written to the output at all — the result stays sparse, never picking up new zero entries.

Speaker note: Three cases only: a is earlier, b is earlier, or they land on the exact same cell and their values add together.

Speaker note: This whole approach depends on both lists already being sorted — feed it unsorted triplets and the merge silently gives the wrong answer.

Speaker note: Let the class work out the arithmetic before the next slide.

Speaker note: Keeping a zero-valued triplet around would quietly break the "only nonzero cells" promise the whole representation depends on.

Speaker note: Section 5 introduces the second major idea of the week — a structure where inserting never has to shift anything else.

Speaker note: Give the class a moment — the answer is exactly what the rest of today builds.

Speaker note: McCarthy was building a language for symbolic reasoning, not thinking about "data structures" as a subject — this idea simply turned out to be everywhere.

Speaker note: An array is a map you hold all at once; a linked list is a trail you can only walk one step at a time.

Speaker note: Every single linked-list program this week and next builds on exactly this five-line struct.

Speaker note: Lose head, and every node after it becomes unreachable — head is the single thread the whole list hangs from.

Speaker note: This self-reference trips up a lot of students the first time — the key is that a pointer is always the same small, fixed size, whatever it points at.

Speaker note: Dereferencing a NULL pointer is the single most common crash in every linked-list program this semester.

Speaker note: Let the class connect this back to the "inception" slide before the answer.

Speaker note: A pointer is always the same handful of bytes, no matter what it points at — that is precisely what breaks the circular dependency.

Speaker note: Section 6 builds the four operations every later list variant — doubly, circular, XOR, skip — reuses or extends.

Speaker note: The answer depends entirely on whether the list keeps a separate pointer to its tail, which this version does not.

Speaker note: "In order" is not a stylistic preference here — it is the difference between a working list and a severed one.

Speaker note: Watch insert_tail specifically — with no tail pointer, it has to walk past every existing node first.

Speaker note: This illustration never touches the real list — it exists purely to show why the write order in insert_after truly matters.

Speaker note: One malloc, one pointer write, one return — insert_head never even looks at the rest of the list.

Speaker note: Swap these two lines and prev->next already equals n by the time step one reads it — the new node ends up pointing at itself.

Speaker note: insert_after itself is O(1), but finding prev in the first place, via search, usually is not.

Speaker note: The bypass arrow is the entire trick: nothing points at cur any more, so it is simply unreachable, ready to be freed.

Speaker note: Watch prev and cur move together — prev always trails one step behind cur, ready to be relinked the moment cur is found.

Speaker note: Deleting the tail is not a separate case in this code at all — it falls naturally out of the same prev/cur scan.

Speaker note: The head case is handled separately here because head itself, not just some node's next field, has to change.

Speaker note: If prev is not updated in lockstep with cur, the bypass arrow ends up pointing from the wrong node entirely.

Speaker note: This is the single biggest thing a list gives up compared to an array — there is no way to jump straight to position k.

Speaker note: Watch the comparison counter climb — every node visited costs one comparison, whether or not it is the one being searched for.

Speaker note: An empty list means the for-loop's condition, cur != NULL, fails immediately — search returns -1 without ever comparing anything.

Speaker note: This entire function fits on one slide — nothing in today's lecture gets cut from it.

Speaker note: Keep this O(n) number in mind — it is the single biggest argument in the arrays-versus-lists table two sections from now.

Speaker note: Three pointers doing a coordinated dance, one node at a time, is the entire algorithm — no recursion, no extra memory.

Speaker note: Watch every arrow flip one at a time, always in the same order: save next, flip curr's arrow, advance both pointers.

Speaker note: Even the smallest non-trivial case runs through the exact same three-pointer dance, just for one iteration instead of many.

Speaker note: This whole function also fits on one slide, and it is worth reading it line by line, out loud, at least once.

Speaker note: In-place reversal, with no extra memory beyond three pointers, is the main reason this algorithm is worth learning by heart.

Speaker note: Let the class trace through what curr->next actually holds at each step before the answer.

Speaker note: This is the exact same "save before you overwrite" lesson as insert_after's step order, just showing up again in a different operation.

Speaker note: Section 7 adds a second pointer per node, in exchange for being able to walk in both directions.

Speaker note: The answer is almost too obvious once it's said out loud — but it changes how every operation has to be written.

Speaker note: The extra pointer is not free, though — it is one more field to keep correct on every single insert and delete.

Speaker note: Every pointer that used to skip over the insertion point now has a matching pointer coming back the other way — both need fixing.

Speaker note: Watch the two arrows on every node — next curving above the row, prev curving below — updated together, never just one.

Speaker note: Inserting after the current tail means the new node becomes the new tail — list->tail itself has to be updated, not just a next pointer.

Speaker note: Four pointer writes in total: the new node's own prev and next, the old next's prev (or list->tail), and cur's next.

Speaker note: cur->prev is read directly here — the singly version had to track a separate prev variable by hand while scanning.

Speaker note: The search cost never improves with a second pointer — only the relinking step, and the ability to walk backward, changes.

Speaker note: Point back to the delete_value code slide before the answer.

Speaker note: That stored prev field is the entire reason doubly lists exist — everything else follows from having it available at every node.

Speaker note: Section 8 removes NULL entirely — the list wraps around instead of ending — and uses that shape to solve a very old puzzle.

Speaker note: There is no obvious reason not to try this — and it turns out to be exactly what the Josephus problem needs.

Speaker note: Forgetting this is the classic circular-list bug: a loop written to stop at NULL simply never stops at all.

Speaker note: Keeping only tail, and deriving head from tail->next, is a small but deliberate design choice this program makes.

Speaker note: Watch the wrap-around arrow, drawn as a curve under the row, connecting the last node straight back to the first.

Speaker note: A one-node circular list points at itself; deleting that single node has to leave the list genuinely empty, tail set back to NULL.

Speaker note: The empty-list case is special precisely because there is no head->next relationship yet to preserve — the new node has to loop back to itself.

Speaker note: This mistake alone causes more infinite loops than any other bug in this week's material.

Speaker note: Whether the legend is exactly true or not, the elimination pattern it describes is precisely what this algorithm simulates.

Speaker note: Every elimination is just one bypass arrow, exactly like circular delete — the whole problem reduces to the operation just shown.

Speaker note: Watch the circle shrink by exactly one node per elimination, the wrap-around arrow redrawn each time.

Speaker note: With only one person in the circle, the elimination loop's remaining > 1 condition is false immediately — nobody is ever removed.

Speaker note: The inner for-loop counts exactly k-1 steps forward — the same bypass arrow from circular delete then removes whoever it lands on.

Speaker note: The simulation is what the animation shows step by step; the recurrence is a shortcut to just the final answer, no circle required.

Speaker note: Let the class walk through what k=1 actually means before the answer.

Speaker note: k=1 is the simplest possible case, and a good sanity check that the general algorithm still behaves the way plain intuition expects.

Speaker note: Section 9 is a memory-saving trick worth knowing about, but the mistakes slide near the end is the part that matters most.

Speaker note: It sounds impossible at first — you cannot literally store two addresses in the space of one — and yet there is a trick.

Speaker note: XOR-ing a value with itself always gives zero — that single fact is the entire trick behind this whole structure.

Speaker note: Watch each node's hex address and npx value on screen — the traversal literally computes the next address, live, at every step.

Speaker note: A single node in an empty list is both head and tail at once — its npx is just its one real neighbor, XOR'd with NULL.

Speaker note: xor_node is the one helper every other function in this program calls — it is where the trick actually lives.

Speaker note: prev starts as NULL, exactly the way an ordinary singly traversal starts — nothing else about the loop's shape has changed.

Speaker note: The time complexity does not actually improve over a doubly list — the entire benefit here is memory, not speed.

Speaker note: This is the single most important slide in this section — the trick is clever, but it is not something to actually ship.

Speaker note: Let the class connect this back to what a garbage collector actually has to do.

Speaker note: Java's simulation in the demo code works around exactly this by using array indices instead of real memory addresses.

Speaker note: Section 10 asks whether a linked list can ever get binary search's O(log n), and answers yes, with one extra idea.

Speaker note: The obstacle is that a list has no index at all, so "jump to the middle" is not even a meaningful operation — yet.

Speaker note: Pugh's own selling point was exactly this: the expected performance of a balanced tree, with far less code to get right.

Speaker note: More express lanes, more levels, keep shrinking the number of stops — this demo uses just two levels to keep it visible.

Speaker note: Every search starts at the highest level and works its way down, never going back up once it has dropped.

Speaker note: Watch the search start on the express lane, drop to the full list only once it overshoots, then take one final step.

Speaker note: With almost nothing on the express lane, most of the search still has to happen down at level 0.

Speaker note: The outer for-loop counts levels down from the top; the inner while-loop is the only place that ever moves cur to the right.

Speaker note: A real implementation flips a coin for each key's level at insert time — this demo fixes the levels in advance so every run is reproducible.

Speaker note: Let the class connect this back to what level 0 alone actually is.

Speaker note: This is worth sitting with: a skip list's speed is never guaranteed, only expected — its worst case is exactly a plain list.

Speaker note: Section 11 puts everything from today side by side, as one table, to make the trade-off explicit.

Speaker note: Only two rows actually differ — index access and front insertion — and they differ in opposite directions.

Speaker note: There is no universally "better" structure here — the right choice depends entirely on which operation the program actually does most.

Speaker note: Let the class think about which single operation a stack actually needs.

Speaker note: Next week's stacks and queues will make this choice concrete, with real implementations built on both.

Speaker note: Four ideas, and every one of them is really about the same question: what has to move, and how much, when the data changes.

Speaker note: Every one of these five list variants is still, underneath, just nodes and pointers — nothing here required any new kind of memory.

Speaker note: If a student remembers only one sentence from today, this is the one worth remembering.

Speaker note: These mirror the self-check quiz at the end of the written notes, one question per slide, with a shorter set here.

Speaker note: Ask, wait, then advance.

Speaker note: The same worst case from the very first mini-quiz of the day, just with a bigger array.

Speaker note: Recall the amortized-cost argument from section 1.

Speaker note: Amortized is an average over many operations, never a promise about any single one of them.

Speaker note: Recall the order-swap edge case from section 6.

Speaker note: A self-loop like that silently cuts off everything that used to follow prev in the list.

Speaker note: Recall the last mini-quiz of section 10.

Speaker note: A skip list's speed always depends on how many express levels actually exist above level 0.

Speaker note: Every stack and queue next week is built from exactly one of today's two structures, with the operations simply restricted to one end.

Speaker note: These are the same references listed at the end of the week's written notes.

Speaker note: The historical references — Pugh, McCarthy, Josephus — are what today's "short history" slides drew on.