Search algorithms locate items or paths within data structures. They answer questions like: "Is this key present? Where is it? What is the shortest path from A to B?" These algorithms span from basic linear scans to advanced, heuristic-guided searches used in routing, games, and information retrieval.
Scan elements one by one until the target is found or the structure ends. Works on any iterable; no preprocessing required.
function linear_search(a, target):
for i from 0 to length(a)-1:
if a[i] == target:
return i
return -1
On a sorted array, repeatedly halve the search interval.
function binary_search(a, target):
lo = 0
hi = length(a) - 1
while lo <= hi:
mid = lo + (hi - lo) // 2
if a[mid] == target:
return mid
else if a[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
Variants: lower_bound/upper_bound, searching for first/last occurrence, searching insertion point.
Use a hash function to map keys to buckets. Average O(1) lookup; worst O(n) with many collisions. Performance depends on load factor and hash quality. Variants include chaining and open addressing.
Store keys with ordering invariant; search follows left/right pointers.
Great for finding min/max in O(1) and extracting in O(log n), but arbitrary search is O(n).
Explores neighbors level by level. On unweighted graphs, BFS finds the shortest path (fewest edges).
function bfs(graph, start):
visited = set([start])
q = queue([start])
parent = {}
while q not empty:
u = q.pop_front()
for v in graph.neighbors(u):
if v not in visited:
visited.add(v)
parent[v] = u
q.push_back(v)
return parent
Explores as far as possible along each branch before backtracking. Useful for connectivity, cycle detection, topological sort.
function dfs(graph, u, visited):
if u in visited: return
visited.add(u)
for v in graph.neighbors(u):
dfs(graph, v, visited)
Finds shortest paths in graphs with non-negative edge weights using a priority queue.
Guided by a heuristic h(n), prioritizing nodes by f(n) = g(n) + h(n). If h is admissible (never overestimates) and consistent, A* is complete and optimal.
function a_star(start, goal, neighbors, cost, heuristic):
open = priority_queue()
open.push(start, priority=heuristic(start))
g = { start: 0 }
parent = {}
while open not empty:
u = open.pop_min()
if u == goal:
return reconstruct_path(parent, u)
for v in neighbors(u):
tentative = g[u] + cost(u, v)
if v not in g or tentative < g[v]:
g[v] = tentative
parent[v] = u
f = tentative + heuristic(v)
open.push_or_decrease_key(v, f)
return failure
Common heuristics: Euclidean/Manhattan distance in grids, straight-line distance in maps.