Imagine this: it’s 2 a.m., you’re deep into some Udemy/Coursera/EdX/Master’s homework, and your coffee is running dangerously low. The assignment? “Implement breadth-first search.” At first glance, it looks innocent—almost trivial. But little do you know, you’ve just unlocked the door to one of the oldest and most fascinating adventures in AI: search algorithms.
Welcome, my lovely reader, to part number one of our journey. This is where search reveals itself as the first window into artificial intelligence, the moment where problem-solving stops being a messy human intuition and becomes something programmable. And the wild part? These algorithms don’t just live in code—they’re quietly running the world around you. From finding your way across campus to navigating traffic with Google Maps to routing internet packets across the globe, search is everywhere.
And with one coffee break (or mate, if you’re a fellow Argentinian), you can go from the baby steps of Breadth-First Search (BFS) to the stardom of A*.
When we say “search,” think road-trip planner. Each city is a node, each road is an edge, and each edge has a cost (distance, time, or maybe even fuel!). A search problem, as Artificial Intelligence: A Modern Approach (Russell & Norvig) puts it, has four key ingredients:
Different algorithms are just different trip vibes:
Breadth-First Search is like that friend who insists on exploring systematically: block by block, street by street, never skipping a beat. If you’re at a city and want to reach another one, BFS first checks all cities one step away. Then all cities are two steps away. Then all cities are three steps away.
It’s complete—if there’s a route, BFS will find it. And it’s optimal only if all roads cost the same (like if every bus ticket cost $1 no matter the distance).
In natural terms: BFS is like exploring a maze by leaving a trail of breadcrumbs and fanning out evenly. You won’t miss the exit, but you might exhaust yourself checking every possible corridor on the way.
BFS(Graph G, Node start, Node goal): frontier ← FIFO-QUEUE() frontier.enqueue(start) came_from ← map with start → NULL visited ← set { start } while frontier not empty: current ← frontier.dequeue() if current = goal: return RECONSTRUCT_PATH(came_from, goal) for each neighbor in G.neighbors(current): if neighbor not in visited: visited.add(neighbor) came_from[neighbor] ← current frontier.enqueue(neighbor) return FAILURE RECONSTRUCT_PATH(came_from, goal): path ← empty list node ← goal while node ≠ NULL: prepend node to path node ← came_from[node] return path |
Uniform-Cost Search is a more careful planner. It doesn’t just count the number of stops—it cares about the price of the trip. UCS expands the route with the lowest cost so far, always checking the “cheapest-looking” ticket on the table before committing.
Imagine you’re planning a Eurotrip: Paris → Amsterdam → Berlin. You could hop direct flights or take cheap trains. BFS would just count legs (two flights = two hops). UCS would tally up actual euros spent and pick the route that minimizes cost—even if it means making a longer detour.
UCS is guaranteed to find the cheapest route, but it’s slow if the map is huge, because it needs to carefully evaluate every possibility.
UCS(Graph G, Node start, Node goal): frontier ← MIN-PRIORITY-QUEUE ordered by g frontier.push((g=0, node=start)) came_from ← map with start → NULL best_cost ← map with start → 0 while frontier not empty: (g, current) ← frontier.pop_min() if current = goal: return RECONSTRUCT_PATH(came_from, goal), g for each (neighbor, w) in G.weighted_neighbors(current): new_cost ← g + w if neighbor not in best_cost OR new_cost < best_cost[neighbor]: best_cost[neighbor] ← new_cost came_from[neighbor] ← current frontier.push((new_cost, neighbor)) return FAILURE |
Finally, the star of the show: A*.
A* is UCS with a brain. Like UCS, it cares about the cost so far, but it also peeks ahead with a heuristic—an estimate of how much farther the trip is. The most common heuristic? Straight-line distance (“as the crow flies”).
Think of A* as your GPS app. It knows how far you’ve driven, but it also knows roughly how far you still are from your destination. By combining both numbers, it avoids exploring hopeless detours.
Formally, it computes:
f(n)=g(n) + h(n)
As long as the heuristic never overestimates the real cost, A* guarantees the cheapest path—but way faster than UCS.
Natural analogy: UCS is like a cautious accountant checking every receipt. A* is like an accountant with a compass—it still tallies receipts, but always points you in the right direction.
A_STAR(Graph G, Node start, Node goal, Heuristic h): frontier ← MIN-PRIORITY-QUEUE ordered by f = g + h frontier.push((f=h(start), g=0, node=start)) came_from ← map with start → NULL best_cost ← map with start → 0 while frontier not empty: (f, g, current) ← frontier.pop_min() if current = goal: return RECONSTRUCT_PATH(came_from, goal), g for each (neighbor, w) in G.weighted_neighbors(current): tentative_g ← g + w if neighbor not in best_cost OR tentative_g < best_cost[neighbor]: best_cost[neighbor] ← tentative_g came_from[neighbor] ← current f_new ← tentative_g + h(neighbor) frontier.push((f_new, tentative_g, neighbor)) return FAILURE |
Here’s a toy example, you can find its code here:
We keep coordinates for each city so A* can use straight-line heuristics.
The output of your code will show something like this:
BFS: [‘Arbor’, ‘Brook’, ‘Cedar’, ‘Fargo’, ‘Grove’] (hops: 4 ) UCS: [‘Arbor’, ‘Brook’, ‘Cedar’, ‘Fargo’, ‘Grove’] (km: 9.4 ) A*: [‘Arbor’, ‘Brook’, ‘Cedar’, ‘Fargo’, ‘Grove’] (km: 9.4 ) |
Heuristics are your built-in sense of direction. Humans use them all the time: “The mall is east, so I’ll head east.” In A*, heuristics give search algorithms that same compass.
The beauty of A*: with an admissible heuristic (never overestimates), you get the best of both worlds—fast and optimal.
One coffee later, you’ve sprinted through the hall of fame:
This is just the beginning. Ahead lie exotic beasts: bidirectional search, iterative deepening, even tridirectional UCS (yes, that’s a thing in grad school assignments). Out in the real world, these ideas scale up to self-driving cars, warehouse robots, and even AI planning your Netflix recommendations.
So next time you sip your coffee and launch a navigation app, remember: behind the scenes, some algorithm is tirelessly exploring the map for you, just like you did at 2 a.m. with your first BFS.The adventure has only begun!
Latin America has rapidly emerged as a leading nearshore destination for software outsourcing. The region combines world-class technical talent and education with significantly lower costs than in the US or Europe. Choosing to hire software developers in Latin America allows businesses to tap into a thriving talent pool of skilled professionals, while also benefiting from…
Software organizations are under constant pressure to ship faster, innovate continuously, and keep top talent engaged. Yet, despite the billions poured into new tools and methodologies, productivity gains have plateaued. Developers spend more time battling slow build pipelines, redundant tasks, and siloed communication than writing the code that drives business value. The result? Burnout, rising…
Tech outsourcing is still booming. What started as a response to pandemic-driven uncertainty has become a long-term strategy. Projections show global IT outsourcing revenue will increase by 50% between 2024 and 2029, proving this model is anything but temporary. For companies in the U.S. and Canada, nearshore outsourcing is emerging as a long-term business enabler:…