Hey everyone!
I was scrolling through an Instagram reel showing how an air conditioner works under the hood, and my DSA brain immediately kicked in. I realized that the logic an AC uses to sample temperature and toggle its compressor is literally a Sliding Window / Queue problem!
While we already have LeetCode 346: Moving Average from Data Stream (which is a premium question), this real-world adaptation introduces physical hardware constraints like Hysteresis (Deadband) to protect the compressor from short-cycling. I thought it would make an amazing interview question for both DSA and Low-Level Design (LLD).
Below is the LeetCode-style problem framework, followed by a LLD simulation in Go using a concurrent ticker.
Difficulty: Medium
Tags: Design, Queue, Sliding Window, Data Stream
Design an air conditioner controller system that decides whether to turn a cooling compressor ON or OFF based on the moving average of the last temperature readings.
To prevent the compressor from rapidly flipping on and off (which damages the physical hardware motor), the system uses a hysteresis threshold of around the targetTemp.
Implement the ACCmpController class:
ACCmpController(int size, double targetTemp) Initializes the object with the window size for the moving average and the desired targetTemp.string addReading(double temp) Adds a new temperature reading to the stream.size readings, it should return "WAITING"."ON"."OFF"."NO_CHANGE".Example:
Input:
["ACCmpController", "addReading", "addReading", "addReading", "addReading"]
[[3, 24.0], [26.0], [25.5], [25.0], [22.0]]
Output:
[null, "WAITING", "WAITING", "ON", "OFF"]
Instead of the standard LeetCode class template, here is a real-world Low-Level Design (LLD). In a real smart device, temperatures arrive asynchronously via a sensor. This implementation uses a concurrent Ticker in Go to poll a sensor at regular intervals, calculates the average thread-safely using a circular queue, and triggers the hardware compressor state.
package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
// 1. DOMAIN COMPONENT: The Hardware Layer (Interfaces)
type TemperatureSensor interface {
ReadCurrentTemp() float64
}
type Compressor interface {
TurnOn()
TurnOff()
GetState() string
}
// 2. CORE DATA STRUCTURE: Thread-Safe Circular Queue (Moving Average Window)
type MovingAverageWindow struct {
mu sync.Mutex
readings []float64
size int
head int
tail int
count int
sum float64
}
func NewMovingAverageWindow(size int) *MovingAverageWindow {
return &MovingAverageWindow{
readings: make([]float64, size),
size: size,
}
}
func (m *MovingAverageWindow) AddAndCalculate(val float64) (float64, bool) {
m.mu.Lock()
defer m.mu.Unlock()
if m.count == m.size {
m.sum -= m.readings[m.head]
m.head = (m.head + 1) % m.size
m.count--
}
m.readings[m.tail] = val
m.sum += val
m.tail = (m.tail + 1) % m.size
m.count++
// Only return a valid average once the sliding window is fully primed
if m.count < m.size {
return 0.0, false
}
return m.sum / float64(m.size), true
}
// 3. SYSTEM CONTROLLER: Manages Ticker, Sensor Data, and Logic
type SmartACController struct {
sensor TemperatureSensor
compressor Compressor
window *MovingAverageWindow
targetTemp float64
}
func NewSmartACController(sensor TemperatureSensor, comp Compressor, windowSize int, target float64) *SmartACController {
return &SmartACController{
sensor: sensor,
compressor: comp,
window: NewMovingAverageWindow(windowSize),
targetTemp: target,
}
}
// StartMonitoring spawns a background routine driven by a ticker
func (ac *SmartACController) StartMonitoring(ctx context.Context, interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
fmt.Printf("[System] Monitoring started. Target Temp: %.1f°C\n", ac.targetTemp)
fmt.Println("------------------------------------------------------------------")
for {
select {
case <-ctx.Done():
fmt.Println("[System] Monitoring stopped.")
return
case <-ticker.C:
// 1. Read temperature from the hardware sensor
currTemp := ac.sensor.ReadCurrentTemp()
// 2. Feed it into our moving average window
avg, initialized := ac.window.AddAndCalculate(currTemp)
if !initialized {
fmt.Printf("[Sensor] Reading: %.2f°C | Gathering initial readings...\n", currTemp)
continue
}
// 3. Evaluate Hysteresis Logic to toggle Compressor state
ac.evaluateState(currTemp, avg)
}
}
}
func (ac *SmartACController) evaluateState(curr float64, avg float64) {
fmt.Printf("[Sensor] Reading: %.2f°C | Running Avg: %.2f°C | Comp State: %s\n",
curr, avg, ac.compressor.GetState())
// Hysteresis bound checks
if avg > ac.targetTemp+0.5 {
ac.compressor.TurnOn()
} else if avg < ac.targetTemp-0.5 {
ac.compressor.TurnOff()
}
}
// 4. MOCK HARDWARE IMPLEMENTATIONS FOR SIMULATION
type MockSensor struct {
currentTemp float64
}
func (m *MockSensor) ReadCurrentTemp() float64 {
// Simulate minor fluctuations in the room temperature
m.currentTemp += (rand.Float64() - 0.48) // Drifts slowly up or down
return m.currentTemp
}
type MockCompressor struct {
isOn bool
}
func (m *MockCompressor) TurnOn() {
if !m.isOn {
fmt.Println(" >>> [HARDWARE UPDATE] Room too hot. Compressor kicked ON.")
m.isOn = true
}
}
func (m *MockCompressor) TurnOff() {
if m.isOn {
fmt.Println(" >>> [HARDWARE UPDATE] Room cooled down. Compressor kicked OFF.")
m.isOn = false
}
}
func (m *MockCompressor) GetState() string {
if m.isOn {
return "ON"
}
return "OFF"
}
// 5. RUNNING THE LLD SIMULATION
func main() {
// Initialize hardware defaults
sensor := &MockSensor{currentTemp: 26.5} // Room starts hot at 26.5°C
compressor := &MockCompressor{isOn: false}
// Create Controller: Window size 5, Target 24.0°C
acSystem := NewSmartACController(sensor, compressor, 5, 24.0)
// Context to limit the life of our simulation run
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
defer cancel()
// Ticker triggers a sensor reading every 1 second
acSystem.StartMonitoring(ctx, 1*time.Second)
}
Would you classify this as an Easy or a Medium?
Also I think we should feature more of these common, daily-use real-world problems on platforms like LeetCode. There are so many things we interact with in our everyday lives that we don’t even think twice about, yet they can be beautifully translated into code.