Linear Structures
dsa.fetehadin.comArrays store items side by side in memory, which is why reading any index is instant. 2-D arrays are stored row by row.
| Access a[i] | O(1) |
| Search (unsorted) | O(n) |
| Insert / delete at position i | O(n): shift items |
| Insert / delete at the end | O(1) if there is space |
| Traverse an R Γ C matrix | O(RΒ·C) |
| Space | O(n) for n items |
- βUsing 1-based indexes. C++ arrays start at 0, so the last valid index is n β 1.
- βWriting
a[n](out of bounds). C++ does not check, so it silently corrupts memory. - βUsing
sizeof(a)/sizeof(a[0])inside a function. There,ais a pointer, so the trick gives the wrong size. Passninstead.
Nodes on the heap, each pointing to the next. Insert at front, end or a position, delete by value, search and traverse, plus the dummy-node trick and the classic pitfalls.
| Insert at front | O(1) |
| Insert at end | O(n), or O(1) with a tail pointer |
| Insert at position k | O(k) |
| Delete by value / search | O(n) |
| Access the i-th item | O(n): no random access |
| Space | O(n) plus one pointer per node |
- βLosing the list by updating
headbefore linking the new node (head = n;first). - βDereferencing
temp->nextwithout checkingtemp != nullptr. - βForgetting the empty-list and delete-the-head special cases.
Each node points both forward and backward. You can walk in both directions and delete a node knowing only its address, at the cost of one extra pointer per node.
| Insert / delete at front or end (with tail) | O(1) |
| Delete a node you already have a pointer to | O(1): no need to find prev |
| Search / delete by value | O(n) |
| Traverse backward | O(n): impossible in a singly list without extra memory |
| Space | O(n), with 2 pointers per node |
- βUpdating
nextbut forgettingprev(or the reverse). Each insert or delete touches both directions. - βForgetting
cur->next->prevwhencur->nextisnullptr(deleting the tail) β crash. - βNot updating
tailwhen deleting the last node.
Last in, first out. The stack ADT (push, pop, peek, isEmpty, isFull) built on a fixed array with a top index, plus overflow and underflow.
| push | O(1) |
| pop | O(1) |
| peek / isEmpty / isFull | O(1) |
| search for a value | O(n): not a stack operation |
| Space | O(MAX), fixed when the array is declared |
- βWriting
st[top++] = xwithtop = -1(writes to index β1). Usest[++top] = x. - βPopping without checking
isEmpty()(underflow). - βPushing without checking
isFull()(writes past the array).
The same stack ADT, but the top is the head of a linked list. No fixed capacity, push and pop stay O(1), and the bracket-matching application.
| push (insert at head) | O(1) |
| pop (delete head) | O(1) |
| peek / isEmpty | O(1) |
| Space | O(n), one node (data + pointer) per item |
- βUsing the tail as the top, making pop O(n).
- βForgetting
deletein pop (memory leak). - βReading
top->dataafter deleting the node. Save the value first.
First in, first out. Why a naive array queue makes dequeue O(n), how a front index makes it O(1), and the wasted-slot problem that leads to the circular queue.
| enqueue (at rear) | O(1) |
| dequeue, naive (shift left) | O(n) |
| dequeue, with front index | O(1) |
| front / isEmpty | O(1) |
| Space | O(MAX) |
- βConfusing front and rear (items enter at the rear, leave at the front).
- βDequeuing without checking for empty.
- βWith a front index, testing
rear == -1for empty. The correct test isfront > rear.
Keep two pointers, front and rear. Enqueue at the rear and dequeue at the front, both O(1), with no capacity limit.
| enqueue (insert after rear) | O(1) |
| dequeue (delete front) | O(1) |
| peek / isEmpty | O(1) |
| Space | O(n) |
- βDequeuing from the tail (needs the previous node, so O(n)).
- βForgetting
rear = nullptrwhen the queue becomes empty. The next enqueue then writes through a dangling pointer. - βEnqueue into an empty queue without setting both front and rear.
Let front and rear wrap around with the modulo operator, so freed slots are reused. All operations O(1) on a fixed array.
| enqueue | O(1) |
| dequeue | O(1) |
| front / isEmpty / isFull | O(1) |
| Space | O(N), and every slot is usable |
- βWriting
rear++instead ofrear = (rear + 1) % N. - βTesting
front == rearfor both empty and full. Without acount(or one spare slot) they look the same. - βPrinting from
0torearinstead of starting atfrontand wrapping.
The standard library versions you will use in real code and on LeetCode, their method names, and the classic traps (pop returns void, empty checks).
| stack push / pop / top | O(1) |
| queue push / pop / front / back | O(1) |
| deque push/pop at either end | O(1) |
| deque d[i] | O(1) |
| Space | O(n) |
- βWriting
int x = s.pop();. It does not compile;pop()returnsvoid. Readtop()first. - βCalling
top()/front()/pop()on an empty container (undefined behaviour). Checkempty(). - βUsing
q.top()on a queue (it isfront()), ors.front()on a stack (it istop()).
- Row-major order:
- How C++ stores a 2-D array: row 0 first, then row 1. Address of a[i][j] = base + (iΒ·cols + j)Β·size.
- Node:
- One element of a linked structure: data plus pointer(s) to other nodes.
- Head:
- Pointer to the first node of a linked list.
- Dummy (sentinel) node:
- A fake node placed before the head so every real node has a predecessor.
- Doubly linked list:
- Each node has both next and prev pointers, allowing backward traversal and O(1) delete given a node.
- Stack:
- LIFO collection: push and pop at the top only.
- LIFO:
- Last In, First Out.
- Overflow / underflow:
- Pushing onto a full stack / popping from an empty one.
- Queue:
- FIFO collection: enqueue at the rear, dequeue from the front.
- FIFO:
- First In, First Out.
- Circular queue:
- Array queue whose indices wrap around with % N so freed slots are reused.
- Deque:
- Double-ended queue: insert and remove at both ends in O(1).