01Foundations

Eight data structures

The eight structures every pattern is built on, and what each operation costs.

The eight structures every pattern is built on, one slide each. Learn what each one is for and what its operations cost, and every later post reads faster.

The idea

StructureWhat it isPatterns it powers
ArrayA row of boxes, side by side. Every box has a number called its index.two pointers, sliding window, prefix sum
StringA row of characters. You read each one by its index.sliding window, two pointers, palindromes
Hash mapStores key → value pairs. Give it the key, get the value back instantly.frequency count, two sum, grouping
Hash setA bag of unique values. It answers one question fast: have I seen this?finding duplicates, visited tracking, cycles
StackLast in, first out. Like a pile of plates: you only touch the top one.valid parentheses, monotonic stack, DFS
QueueFirst in, first out. Like a ticket line: join at the back, leave from the front.BFS, level-order traversal, shortest path
Linked listNodes chained by pointers. Each node knows only the next one.fast and slow pointers, reversal, merging
Binary treeEach node has up to two children. The top node is the root; a node with no children is a leaf.DFS, BFS, recursion

Pseudocode

Pseudocode · any language
// array
a = [7, 2, 9, 4, 1]
a[2]                    // 9
length(a)               // 5

// string
s = "code"
s[0]                    // 'c'
length(s)               // 4
substring(s, 1)         // "ode"

// hash map
m = empty map
m["apple"] = 3
m["apple"]              // 3
"fig" in m              // false

// hash set
seen = empty set
add 9 to seen
add 9 to seen           // ignored, already in
4 in seen               // false

// stack
st = empty stack
push 5 onto st
top of st               // 5, only looks
pop from st             // 5, removes it

// queue
q = empty queue
add 4 to q
front of q              // 4, only looks
remove front of q       // 4, removes it

// linked list
node has: value, next
cur = head
while cur != none:
    cur = cur.next

// binary tree
node has: value, left, right
root.value              // 8
root.left.value         // 3
root.right.value        // 10

The trap: indexes start at 0, not 1. In a = [7, 2, 9, 4, 1], a[2] is the third box, 9, not the second box, 2. The last box is a[length(a) - 1], and a[length(a)] is past the end.

Code

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

public class EightStructures {
    static class ListNode {
        int value;
        ListNode next;
        ListNode(int value, ListNode next) { this.value = value; this.next = next; }
    }

    static class TreeNode {
        int value;
        TreeNode left, right;
        TreeNode(int value, TreeNode left, TreeNode right) {
            this.value = value; this.left = left; this.right = right;
        }
    }

    public static void main(String[] args) {
        // array
        int[] a = {7, 2, 9, 4, 1};
        System.out.println(a[2]);            // 9
        System.out.println(a.length);        // 5

        // string
        String s = "code";
        System.out.println(s.charAt(0));     // c
        System.out.println(s.length());      // 4
        System.out.println(s.substring(1));  // ode

        // hash map
        Map<String, Integer> m = new HashMap<>();
        m.put("apple", 3);
        System.out.println(m.get("apple"));          // 3
        System.out.println(m.containsKey("fig"));    // false

        // hash set
        Set<Integer> seen = new HashSet<>();
        seen.add(9);
        seen.add(9);                                  // ignored, already in
        System.out.println(seen.contains(4));         // false

        // stack
        Deque<Integer> st = new ArrayDeque<>();
        st.push(5);
        System.out.println(st.peek());       // 5, only looks
        System.out.println(st.pop());        // 5, removes it

        // queue
        Queue<Integer> q = new ArrayDeque<>();
        q.offer(4);
        System.out.println(q.peek());        // 4, only looks
        System.out.println(q.poll());        // 4, removes it

        // linked list: 7 -> 2 -> 9
        ListNode head = new ListNode(7, new ListNode(2, new ListNode(9, null)));
        ListNode cur = head;
        while (cur != null) {
            cur = cur.next;
        }

        // binary tree
        TreeNode root = new TreeNode(8,
            new TreeNode(3, new TreeNode(1, null, null), new TreeNode(6, null, null)),
            new TreeNode(10, null, null));
        System.out.println(root.value);        // 8
        System.out.println(root.left.value);   // 3
        System.out.println(root.right.value);  // 10
    }
}

Complexity

StructureOperationCost
Arrayread by indexO(1)
Arraysearch, insert in the middleO(n)
Stringread by indexO(1)
Stringtake a substring, compare two stringsO(n)
Hash mapadd, look up, removeO(1) on average
Hash setadd, checkO(1) on average
Stackpush, pop, look at topO(1)
Queueadd to back, remove front, look at frontO(1)
Linked listinsert at headO(1)
Linked listreach the k-th node, searchO(n)
Binary treevisit every nodeO(n)
Binary treeheight when balancedO(log n)
Binary treeheight when skewedO(n)

How it was tested

All four programs were compiled and run, and each printed the values in the pseudocode comments: 9, 5, 'c', 4, "ode", and so on down to the tree's 8, 3 and 10. The C++ version prints the stack and queue values once each, because pop() there removes without giving the value back (see post 03).

Practice

Try these in order. Open the worked example first if you got stuck on the slides.