Data Structures, Algorithms, and Complexity - Quick
Notes
1. Linear Data Structures (detailed — simple English)
Below each structure is explained in plain English with: what it is, how it works simply, main
operations with time costs, when to use it, pros/cons, and a tiny code sketch where helpful.
1.1 Arrays (Static / Fixed-size)
•
•
•
•
•
•
What it is: A block of memory storing elements one after another, all same type.
How it works (simple): Each element has an index; the computer computes the memory
address with base + index * element_size so accessing A[i] is direct.
Main operations & time:
o Access A[i]: O(1)
o Update A[i]: O(1)
o Insert/delete at end: O(1) if space, otherwise depends
o Insert/delete in middle: O(n) (need to shift elements)
o Search (unsorted): O(n)
When to use: When you need fast random access by index and size is known or changes
rarely.
Pros: Very fast random access, memory compact.
Cons: Fixed capacity (unless you copy to a bigger array), costly inserts/deletes in middle.
1.2 Dynamic Arrays (Resizable arrays — e.g., Python list, C++ vector)
•
•
•
•
•
•
•
What it is: An array that automatically grows when you add more items.
How it works (simple): Keep size and capacity. When size == capacity and you
append, allocate a bigger array (often double capacity), copy elements, then append.
Main operations & time:
o Append (amortized): O(1)
o Worst-case append (during resize): O(n)
o Random access: O(1)
o Insert/delete in middle: O(n)
When to use: Most general-purpose sequence where you need random access and
variable size.
Pros: Easy API, amortized constant append, random access.
Cons: Occasional expensive resize; extra memory (capacity > size).
Tiny conceptual code (already in doc): shows append and _resize logic.
1.3 Singly Linked List
•
•
•
•
•
•
•
What it is: A chain of nodes; each node has a value and a pointer to the next node.
How it works (simple): You start at head and follow next pointers to visit items in
order.
Main operations & time:
o Insert at head: O(1)
o Insert at tail (if tail pointer): O(1), otherwise O(n)
o Delete (given node pointer): O(1) for fixed node adjustments; deletion by
value/search: O(n)
o Search: O(n)
o Random access by index: O(n)
When to use: Frequent inserts/removals at known positions, memory fragmentation, or
when you don’t need random access.
Pros: Cheap insert/delete at ends (if you keep tail), nodes can be anywhere in memory.
Cons: No O(1) random access, extra memory for pointers.
Python sketch: (present in document) shows Node, SinglyLinkedList, insert_head,
search, delete_after.
1.4 Doubly Linked List
•
•
•
•
•
•
•
What it is: Like a singly list but each node has prev and next pointers, so you can move
both forward and backward.
How it works (simple): Each node points to neighbor on left (prev) and right (next);
head and tail let you access both ends quickly.
Main operations & time:
o Insert at head/tail: O(1)
o Remove known node: O(1) (update two pointers)
o Search: O(n)
o Random access by index: O(n)
When to use: When you need fast removal given a node pointer, or need to traverse
backwards.
Pros: O(1) remove with node pointer; easy reverse traversal.
Cons: More memory per node (two pointers), slightly more pointer updates on
insert/delete.
Python sketch: (present in document) DNode, DoublyLinkedList, append, remove.
1.5 Circular Linked Lists (brief)
•
•
•
What it is: A linked list where the last node points back to the first node (no None end).
Why used: Useful for round-robin scheduling or when you want to loop through items
indefinitely.
Time costs: Similar to singly/doubly lists for basic ops.
1.6 Stacks (abstract + implementations)
•
•
•
•
•
Concept (simple): Last-In, First-Out. Only operate at one end (top).
Common ops: push (add), pop (remove), peek (look at top).
Time costs: push/pop/peek — O(1).
Implementations: Array-backed (dynamic array), linked-list-backed (use head as top).
When to use: Keep track of history (undo), evaluate expressions, function call frames
(recursion).
1.7 Queues (abstract + implementations)
•
•
•
•
•
Concept (simple): First-In, First-Out. Add at rear, remove from front.
Common ops: enqueue, dequeue, peek (front).
Time costs: enqueue/dequeue — O(1) with proper structure.
Implementations: Circular buffer (array with head/tail modulo), linked-list, deque in
Python.
When to use: BFS traversal, job scheduling, buffering.
2. Sorting & Searching: Paradigm classification (simple)
Below is a mapping of each algorithm you asked for to the paradigm it belongs to, and a line
explaining why in simple English.
•
•
•
Brute Force (exhaustive): Try many possibilities directly; often simple but slow on large
inputs.
o Bubble Sort: Brute force — repeatedly compare neighbors and swap; does not
reduce problem size intelligently.
o Linear Search: Brute force — check each item until you find the target.
Decrease and Conquer (reduce by a constant each step): Solve a smaller instance and
extend the solution.
o Selection Sort: Decrease & conquer — pick the smallest and reduce the unsorted
part by one.
o Insertion Sort: Decrease & conquer — grow a sorted prefix by inserting one
element at a time.
o Binary Search: Decrease & conquer — remove half the search space each step
(reduce size drastically).
Divide and Conquer (split into independent subproblems): Split into parts, solve each
part recursively, then combine.
o Merge Sort: Divide & conquer — split in halves, sort each half, then merge.
o Quick Sort: Divide & conquer — partition around pivot, then sort partitions.
Short reason / how it maps (one-liners):
•
•
•
Brute force: does repeated simple checks without recursion or meaningful splits.
Decrease & conquer: reduces the problem size by a constant (usually 1 or some small
factor) and reuses the previous solution.
Divide & conquer: splits the problem into independent subproblems, solves each, and
combines results.
(If you want, I can add a small diagram or one-line code markers in the canvas showing these
groupings visually.)
3. Quick reference — operations & where to pick each structure (short)
•
•
•
•
Choose array / dynamic array when you need fast random access and predictable
iteration.
Choose linked list when you need many fast insertions/deletions at arbitrary points and
you do not need random access.
Choose stack for LIFO tasks (undo, recursion emulation).
Choose queue for FIFO tasks (BFS, scheduling).
Codes from GeeksforGeeks
Merge
Insertion
Selection
Quick
linear
Binary