Why Engineers Need This
In 2026, AI writes a significant fraction of production code. That’s precisely why you need to understand data structures and algorithms at a conceptual level. AI can generate code that works but is catastrophically slow — and if you can’t recognize the difference between O(n) and O(n²), you won’t catch it until it melts your server.
Consider a real scenario: you ask an AI to “find duplicate node IDs in a mesh.” It writes a nested loop that compares every node to every other node — O(n²). For a mesh with 1 million nodes, that’s 1012 comparisons. It will run for hours. The correct approach uses a hash set and completes in seconds. The AI’s code is correct. It’s also useless.
You don’t need to implement a red-black tree from scratch. You need to know when to reach for one.
Big-O Notation
Big-O describes how an algorithm’s running time or memory usage grows as the input size grows. It’s not about exact milliseconds — it’s about the shape of the growth curve.
| Big-O | Name | Example | n = 1,000,000 operations |
|---|---|---|---|
| O(1) | Constant | Hash table lookup | 1 |
| O(log n) | Logarithmic | Binary search in sorted array | 20 |
| O(n) | Linear | Scan all elements once | 1,000,000 |
| O(n log n) | Linearithmic | Efficient sorting (merge sort, timsort) | 20,000,000 |
| O(n²) | Quadratic | Nested loops over all pairs | 1,000,000,000,000 |
| O(n³) | Cubic | Naive matrix multiplication | 1018 |
| O(2n) | Exponential | Brute-force subset enumeration | Effectively infinite |
Key takeaway: The jump from O(n) to O(n²) is the most common performance disaster in engineering code. At n = 1,000 both are fast. At n = 1,000,000 the O(n) version takes seconds and the O(n²) version takes days. Always ask: “Does this loop contain another loop over the same data?”
Core Data Structures
Arrays (Lists)
An ordered, contiguous collection of elements. Access by index is O(1). Search by value is O(n). Insertion at the end is O(1) amortized; insertion in the middle is O(n) because everything shifts.
# Python: Arrays are called "lists"
node_coords = [0.0, 1.5, 3.0, 4.5, 6.0]
# O(1) - access by index
third_node = node_coords[2] # 3.0
# O(n) - search by value
idx = node_coords.index(4.5) # scans until found
# O(1) amortized - append
node_coords.append(7.5)
# NumPy arrays for engineering: contiguous memory, vectorized ops
import numpy as np
coords = np.array([0.0, 1.5, 3.0, 4.5, 6.0])
displaced = coords + 0.01 # vectorized, no Python loop
Engineering context: NumPy arrays are the workhorse of engineering computation. They store data contiguously in memory, enabling vectorized operations that are 10–100x faster than Python loops. When you see a for loop over a NumPy array, that’s almost always a performance bug.
Hash Tables (Dictionaries)
A key-value store with O(1) average lookup, insertion, and deletion. The hash function converts the key into an array index. Collisions degrade performance but are rare with good hash functions.
# Python: Hash tables are called "dicts"
node_results = {
"N001": {"displacement": 0.023, "stress": 145.2},
"N002": {"displacement": 0.018, "stress": 132.7},
"N003": {"displacement": 0.031, "stress": 178.4},
}
# O(1) - lookup by key
stress = node_results["N002"]["stress"] # 132.7
# O(1) - check membership
has_node = "N004" in node_results # False
# O(1) - insert
node_results["N004"] = {"displacement": 0.015, "stress": 121.0}
# O(n) to iterate all values
max_stress_node = max(node_results, key=lambda n: node_results[n]["stress"])
Engineering context: Hash tables are ideal when you need to look up results by an identifier — node ID, element ID, sensor name, material code. The “find duplicate node IDs” problem from the intro is solved by inserting IDs into a set (a hash table without values) and checking for collisions: O(n) instead of O(n²).
Trees
Hierarchical data structures where each node has a parent and zero or more children. Different tree types serve different purposes:
- Binary Search Tree (BST): Left child < parent < right child. Lookup, insert, delete are O(log n) for balanced trees. Used when you need sorted data with fast insertion.
- B-Tree: A self-balancing tree optimized for disk I/O. Each node can have many children. The underlying data structure of most databases. When you query a database index, you’re traversing a B-Tree.
- Trie (prefix tree): Specialized for string lookups. Each node represents a character. Useful for autocomplete, spell checking, and looking up sensor names by prefix.
- Quad-tree / Octree: Spatial partitioning trees. Quad-trees divide 2D space into four quadrants; octrees divide 3D space into eight octants. Used in mesh generation, collision detection, and spatial queries (“find all sensors within 100 meters of this point”).
Engineering context: Octrees are fundamental to computational geometry. When a meshing algorithm needs to find nearby nodes, it doesn’t scan all million nodes — it queries the octree and gets O(log n) spatial lookups. FEA pre-processors, CFD mesh generators, and point cloud processing all use spatial trees.
Graphs
A collection of nodes (vertices) connected by edges. Graphs model relationships and connections.
- Directed graph: Edges have direction. A depends on B, but B doesn’t depend on A. Used for dependency graphs, workflow DAGs, and data flow.
- Undirected graph: Edges go both ways. Node A is connected to Node B, and B is connected to A. Used for finite element meshes, social networks, and structural connectivity.
- Weighted graph: Edges have costs or distances. Used for shortest-path problems, network routing, and cost optimization.
# A structural connectivity graph
# Adjacency list representation
structure = {
"beam_1": ["beam_2", "column_1"],
"beam_2": ["beam_1", "beam_3", "column_2"],
"beam_3": ["beam_2", "column_3"],
"column_1": ["beam_1", "foundation"],
"column_2": ["beam_2", "foundation"],
"column_3": ["beam_3", "foundation"],
"foundation": ["column_1", "column_2", "column_3"],
}
Engineering context: Every finite element mesh is a graph. Every structural system is a graph. Every dependency chain (module A imports B, B imports C) is a directed graph. When you ask “is this structure still connected if we remove this beam?” you’re asking a graph connectivity question. When you ask “what is the critical path through this project schedule?” you’re asking a graph shortest-path question.
Stacks and Queues
- Stack (LIFO — Last In, First Out): Think of a stack of plates. The last plate placed on top is the first one removed. Used for undo operations, parsing expressions, recursive algorithm implementation, and depth-first search.
- Queue (FIFO — First In, First Out): Think of a line at a counter. First person in line is first served. Used for job scheduling, breadth-first search, and message processing.
- Priority Queue: Like a queue, but elements have priorities. The highest-priority item is dequeued first, regardless of insertion order. Used for task scheduling (urgent alerts before routine reports), Dijkstra’s algorithm, and event simulation.
Engineering context: Job queues in FEA platforms are priority queues — a critical safety analysis jumps ahead of a routine parameter study. Message brokers (Kafka, RabbitMQ) are fundamentally queues. The “undo” button in any engineering GUI is a stack.
Choosing the Right Data Structure
| You Need To… | Use | Why |
|---|---|---|
| Look up by key/ID | Hash table (dict) | O(1) average lookup |
| Store ordered numeric data | Array (NumPy) | Contiguous memory, vectorized ops |
| Find nearby points in space | Octree / KD-tree | O(log n) spatial queries |
| Model connections/relationships | Graph (adjacency list) | Natural representation of networks |
| Process items in order | Queue | FIFO guarantees fairness |
| Process items by priority | Priority queue (heap) | O(log n) insert and extract-min |
| Check membership (“have I seen this?”) | Set (hash set) | O(1) average membership test |
| Keep data sorted with fast insert | Balanced BST / sorted container | O(log n) insert, search, delete |
Exercise 8.1: Data Structure Selection
Data Structure Selection for Engineering Scenarios
For each scenario below, identify the best data structure and explain why. Consider the Big-O complexity of the operations required.
- Sensor Registry: You have 10,000 sensors, each with a unique ID. You need to frequently look up a sensor’s calibration data by its ID, add new sensors, and remove decommissioned ones.
- Job Scheduler: An FEA cloud platform receives solver jobs with different priority levels (critical, high, normal, low). Jobs should be processed in priority order, not arrival order.
- Collision Detection: A robotics simulation has 50,000 objects in 3D space. For each time step, you need to find all objects within 0.5 meters of each other.
- Build Dependencies: Your engineering software has 200 modules. Each module depends on zero or more other modules. You need to determine a valid build order.
- Duplicate Detection: You received a mesh file with 2 million nodes. You suspect some nodes are duplicated (same coordinates, different IDs). Find all duplicates.
Answer
- Hash table (dictionary). O(1) lookup, insert, and delete by sensor ID. Perfect for key-value access patterns.
- Priority queue (min-heap or max-heap). O(log n) insertion and O(1) to peek at highest priority. Processes critical jobs first regardless of when they were submitted.
- Octree. Divides 3D space hierarchically. Spatial query “find all objects within radius r of point p” is O(log n + k) where k is the number of results, instead of O(n²) for brute-force all-pairs comparison.
- Directed acyclic graph (DAG) + topological sort. Modules are nodes, dependencies are directed edges. Topological sort produces a valid build order in O(V + E) time. If the sort fails, there’s a circular dependency.
- Hash set (or dictionary) keyed by coordinate tuple. For each node, compute a hash of its coordinates (rounded to appropriate precision). If the hash already exists in the set, it’s a duplicate. O(n) total instead of O(n²) pairwise comparison.
Quiz
An engineer writes code to find matching sensor IDs between two lists (each with 100,000 entries) using nested for loops. The code runs for 45 minutes. What is the Big-O complexity of this approach, and how would you fix it using a different data structure?
Answer
The nested loop approach is O(n²) — for each of the 100,000 IDs in list A, it scans all 100,000 IDs in list B. That’s 10,000,000,000 comparisons.
Fix: Convert one list to a hash set, then iterate over the other list checking membership in the set. Building the set is O(n). Checking membership for each item is O(1) average. Total: O(n). This runs in under a second instead of 45 minutes.
# O(n^2) - BAD
matches = []
for id_a in list_a:
for id_b in list_b:
if id_a == id_b:
matches.append(id_a)
# O(n) - GOOD
set_b = set(list_b) # O(n) to build
matches = [id_a for id_a in list_a if id_a in set_b] # O(n) to check