Data Structures for Industrial Applications
What Are Data Structures and Why Do Engineers Need Them?
Imagine a warehouse with thousands of parts thrown in randomly -- finding a single part would take hours. But if you organize them in numbered shelves by type and size, you find anything in seconds.
A data structure is the same idea applied to computer memory -- a way to organize data so it can be accessed and processed as efficiently as possible. Choosing the right structure is the difference between a monitoring system that responds in milliseconds and one that freezes on every alarm.
Array: Sequential Storage
An array is the simplest data structure -- elements stored contiguously in memory, each accessible by an index starting from zero.
Industrial use case: A circular buffer storing the most recent sensor readings to calculate a moving average.
# Circular buffer for last 10 temperature readings
temp_buffer = [0.0] * 10 # fixed-size array
write_index = 0
def add_reading(value):
global write_index
temp_buffer[write_index % 10] = value
write_index += 1
def moving_average():
count = min(write_index, 10)
return sum(temp_buffer[:count]) / count if count > 0 else 0
# Add readings
for temp in [78.5, 79.1, 80.3, 77.8, 81.2]:
add_reading(temp)
print(f"Moving average: {moving_average():.1f}°C")
| Operation | Time Complexity |
|---|---|
| Access by index | O(1) -- instant |
| Search for value | O(n) -- linear |
| Append to end | O(1) |
| Insert at beginning | O(n) -- slow |
Linked List: Flexible Insertions and Deletions
A linked list is a chain of nodes, each containing data and a pointer to the next node. Nodes do not need contiguous memory -- each can live anywhere in the heap.
Industrial use case: An event log that changes size constantly -- fast insertions and deletions without shifting elements.
class EventNode:
def __init__(self, timestamp, event_type, message):
self.timestamp = timestamp
self.event_type = event_type
self.message = message
self.next = None
class EventLog:
def __init__(self):
self.head = None
self.count = 0
def add_event(self, timestamp, event_type, message):
new_node = EventNode(timestamp, event_type, message)
new_node.next = self.head
self.head = new_node # newest event at the front
self.count += 1
def get_recent(self, n=5):
events = []
current = self.head
while current and len(events) < n:
events.append(f"[{current.timestamp}] {current.event_type}: {current.message}")
current = current.next
return events
# Usage
log = EventLog()
log.add_event("14:30:05", "ALARM", "CNC-01 temperature high")
log.add_event("14:30:12", "ACTION", "Speed reduced")
log.add_event("14:31:00", "INFO", "Temperature normalized")
Array vs Linked List comparison:
| Criterion | Array | Linked List |
|---|---|---|
| Access by index | O(1) fast | O(n) slow |
| Insert/delete at front | O(n) slow | O(1) fast |
| Memory usage | Lower | Higher (pointers) |
| Fixed vs dynamic size | Usually fixed | Easily dynamic |
Hash Map: Instant Lookup
Imagine a cabinet with 1,000 drawers, and you have a magic key that tells you exactly which drawer to open. That is a HashMap -- it maps a key to a value and finds any element instantly.
Industrial use case: Mapping a machine ID to its current status -- when an alarm arrives from a specific machine, you retrieve its data immediately.
# Current status of every machine on the floor
machine_status = {
"CNC-01": {"temp": 78.5, "status": "running", "alarms": 0},
"CNC-02": {"temp": 91.2, "status": "warning", "alarms": 3},
"PUMP-05": {"temp": 65.0, "status": "idle", "alarms": 0},
"COMP-03": {"temp": 82.1, "status": "running", "alarms": 1},
}
# Instant access to a machine's status
machine = "CNC-02"
info = machine_status[machine] # O(1) - instant
print(f"{machine}: temp {info['temp']}°C - {info['status']}")
# Update status
machine_status["CNC-02"]["alarms"] += 1
# Find all machines in warning state
warnings = {k: v for k, v in machine_status.items() if v["status"] == "warning"}
| Operation | Time Complexity |
|---|---|
| Lookup by key | O(1) -- instant |
| Insert | O(1) |
| Delete | O(1) |
| Search by value | O(n) -- linear |
Queue: First In, First Out
A queue follows the FIFO principle (First In, First Out) -- just like a production order queue: the first order submitted is the first order processed.
Industrial use case: An alarm queue that ensures alerts are handled in the order they arrive, so nothing gets lost.
from collections import deque
class AlarmQueue:
def __init__(self, max_size=100):
self.queue = deque(maxlen=max_size)
def push_alarm(self, machine_id, severity, message):
alarm = {
"machine": machine_id,
"severity": severity, # "low", "medium", "high", "critical"
"message": message,
"acknowledged": False
}
self.queue.append(alarm)
def process_next(self):
if self.queue:
alarm = self.queue.popleft() # oldest alarm first
print(f"Processing: [{alarm['severity']}] {alarm['machine']}: {alarm['message']}")
return alarm
return None
def pending_count(self):
return len(self.queue)
# Usage
alarms = AlarmQueue()
alarms.push_alarm("CNC-01", "high", "Temperature elevated")
alarms.push_alarm("PUMP-05", "critical", "Pressure too low")
alarms.push_alarm("CONV-02", "low", "Belt needs maintenance")
print(f"Pending alarms: {alarms.pending_count()}")
while alarms.pending_count() > 0:
alarms.process_next()
Stack: Last In, First Out
A stack follows the LIFO principle (Last In, First Out) -- like a stack of plates: the last plate placed on top is the first one you pick up.
Industrial use case: Undo operations in control interfaces, or tracking machine state history.
class MachineStateStack:
"""Track machine state history for undo capability"""
def __init__(self):
self.stack = []
def save_state(self, state_dict):
self.stack.append(state_dict.copy())
def undo(self):
if self.stack:
return self.stack.pop() # most recent state
return None
def peek(self):
"""View the latest state without removing it"""
return self.stack[-1] if self.stack else None
# Usage: operator changing CNC settings
history = MachineStateStack()
# Initial state
history.save_state({"speed": 1200, "feed": 0.15, "depth": 2.0})
# Operator changes speed
history.save_state({"speed": 1500, "feed": 0.15, "depth": 2.0})
# Operator changes depth -- bad results!
history.save_state({"speed": 1500, "feed": 0.15, "depth": 3.5})
# Undo last change
previous = history.undo()
print(f"Reverted to: speed={previous['speed']}, depth={previous['depth']}")
Tree: Hierarchical Organization
A tree is a hierarchical structure -- each node has a parent (except the root) and may have children. Ideal for representing hierarchical relationships.
Industrial use case: Factory structure (departments, production lines, machines), or decision trees for fault diagnosis.
class FactoryNode:
def __init__(self, name, node_type):
self.name = name
self.node_type = node_type # "factory", "department", "line", "machine"
self.children = []
def add_child(self, child):
self.children.append(child)
return child
def display(self, indent=0):
prefix = " " * indent + ("|- " if indent > 0 else "")
print(f"{prefix}[{self.node_type}] {self.name}")
for child in self.children:
child.display(indent + 1)
# Build factory hierarchy
factory = FactoryNode("Dr.Machine Factory", "factory")
dept_mech = factory.add_child(FactoryNode("Machining Department", "department"))
line_cnc = dept_mech.add_child(FactoryNode("CNC Line", "line"))
line_cnc.add_child(FactoryNode("CNC-01 (Lathe)", "machine"))
line_cnc.add_child(FactoryNode("CNC-02 (Mill)", "machine"))
dept_assembly = factory.add_child(FactoryNode("Assembly Department", "department"))
line_a = dept_assembly.add_child(FactoryNode("Assembly Line A", "line"))
line_a.add_child(FactoryNode("Robotic Welding Station", "machine"))
line_a.add_child(FactoryNode("Visual Inspection Station", "machine"))
factory.display()
When to Use Each Structure
| Structure | Best Industrial Use | Access Complexity |
|---|---|---|
Array |
Sensor reading buffer | O(1) by index |
Linked List |
Dynamic-size event log | O(n) search |
HashMap |
Machine ID to status mapping | O(1) by key |
Queue |
Alarm or production order queue | O(1) enqueue/dequeue |
Stack |
Undo operations | O(1) push/pop |
Tree |
Factory hierarchy | O(log n) search |
Choosing the right data structure is a genuine engineering decision -- it affects system speed, memory consumption, and maintainability. Take the time to understand each structure and you will naturally pick the best fit for every situation.