Report Content
×Searching Algorithms: Complete Guide
Master search algorithms from linear to exponential, with complexity analysis, optimization techniques, and real-world applications. Includes implementations for both sorted and unsorted data.
Search Algorithm Usage (2023)
1. Fundamental Search Algorithms
Linear Search
Definition
Linear Search is a sequential searching algorithm that checks every element in a list one by one until the target value is found or the list ends. It works on both sorted and unsorted data.
How It Works
Start at the beginning
- Initialize index i = 0
- Set target value to search for
Compare current element
- Check if arr[i] equals target
- If yes, return index i
Move to next element
- Increment i by 1
- Repeat step 2 until target found or end reached
def linear_search(arr, target):
for i in range(len(arr)):
if arr[i] == target:
return i
return -1
Complexity: O(n) time, O(1) space
Use When: Unsorted or small datasets
Binary Search
Definition
Binary Search is an efficient algorithm that finds a target value within a sorted array by repeatedly dividing the search interval in half. It compares the target to the middle element and eliminates half of the remaining elements each time.
How It Works
Set boundaries
- Initialize left = 0, right = n-1
- Array must be sorted
Find middle element
- Calculate mid = (left + right) / 2
- Compare arr[mid] with target
Narrow the search
- If target equals arr[mid] → return mid
- If target < arr[mid] → search left half (right = mid - 1)
- If target > arr[mid] → search right half (left = mid + 1)
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
Complexity: O(log n) time, O(1) space
Use When: Sorted arrays
Time Complexities
| Algorithm | Best | Average | Worst | Space |
|---|---|---|---|---|
| Linear | O(1) | O(n) | O(n) | O(1) |
| Binary | O(1) | O(log n) | O(log n) | O(1) |
| Jump | O(1) | O(√n) | O(√n) | O(1) |
| Exponential | O(1) | O(log i) | O(log n) | O(1) |
2. Advanced Search Techniques
Jump Search
Definition
Jump Search (or Block Search) is a search algorithm for sorted arrays that works by jumping ahead by fixed steps (optimal block size = √n) and then performing a linear search within the identified block.
How It Works
Calculate block size
- Set blockSize = √(array length)
- Initialize prev = 0
Jump ahead in blocks
- While arr[min(step, n)-1] < target
- Set prev = step, step += blockSize
- If prev >= n, target not found
Linear search in block
- Search from prev to step-1
- If found, return index
- If not, return -1
import math
def jump_search(arr, target):
n = len(arr)
block_size = int(math.sqrt(n))
prev = 0
while arr[min(block_size, n) - 1] < target:
prev = block_size
block_size += int(math.sqrt(n))
if prev >= n:
return -1
for i in range(prev, min(block_size, n)):
if arr[i] == target:
return i
return -1
Optimal Block: √n
Use When: Sorted arrays, better than linear
Exponential Search
Definition
Exponential Search (also called galloping search) is an algorithm that finds the range where the target exists by exponentially increasing the search interval, then performs binary search within that range. It's ideal for unbounded or infinite arrays.
How It Works
Check first element
- If arr[0] == target, return 0
- Initialize i = 1
Exponentially increase range
- While i < n and arr[i] <= target
- Double i each time (i *= 2)
- This finds a range where target could exist
Binary search the range
- Perform binary search from i/2 to min(i, n-1)
- Return index if found, else -1
def exponential_search(arr, target):
if arr[0] == target:
return 0
n = len(arr)
i = 1
while i < n and arr[i] <= target:
i *= 2
left, right = i // 2, min(i, n - 1)
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
Combines: Exponential + Binary
Use When: Infinite/unbounded arrays
Interpolation Search
Definition
Interpolation Search is an improved variant of binary search that works best on uniformly distributed sorted data. Instead of always checking the middle, it calculates a probable position using a formula based on the target value.
How It Works
Calculate probe position
- pos = low + ((target - arr[low]) * (high - low)) / (arr[high] - arr[low])
- This estimates where the target might be
Compare and narrow
- If arr[pos] == target → return pos
- If arr[pos] < target → search right half
- If arr[pos] > target → search left half
Repeat until found
- Continue recalculating pos
- Stop when low > high or target out of range
def interpolation_search(arr, target):
low, high = 0, len(arr) - 1
while low <= high and target >= arr[low] and target <= arr[high]:
pos = low + int(((target - arr[low]) * (high - low)) / (arr[high] - arr[low]))
if arr[pos] == target:
return pos
if arr[pos] < target:
low = pos + 1
else:
high = pos - 1
return -1
Complexity: O(log log n) avg
Use When: Uniformly distributed data
Ternary Search
Definition
Ternary Search is a divide-and-conquer algorithm that splits the array into three parts instead of two. It's primarily used for finding the maximum or minimum of a unimodal function rather than searching for a specific value.
How It Works
Calculate mid points
- mid1 = left + (right - left) / 3
- mid2 = right - (right - left) / 3
Compare with target
- If target equals arr[mid1] or arr[mid2] → return index
- If target < arr[mid1] → search left third
- If target > arr[mid2] → search right third
- Otherwise → search middle third
Repeat recursively
- Update boundaries based on comparison
- Continue until found or boundaries cross
def ternary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid1 = left + (right - left) // 3
mid2 = right - (right - left) // 3
if arr[mid1] == target:
return mid1
if arr[mid2] == target:
return mid2
if target < arr[mid1]:
right = mid1 - 1
elif target > arr[mid2]:
left = mid2 + 1
else:
left = mid1 + 1
right = mid2 - 1
return -1
Comparisons: More than binary
Use When: Finding min/max in unimodal functions
3. Search in Data Structures
Hash Tables
Definition
Hash Tables are data structures that map keys to values using a hash function. They provide O(1) average-time search by computing a hash code that directly points to the location of the data.
How It Works
Apply hash function
- Convert key to hash code using hash function
- Map hash code to array index using modulo
Handle collisions
- Use chaining (linked lists at each index)
- Or open addressing (linear probing)
Search for key
- Go to computed index
- Search through chain/probe sequence
- Return value if key matches
class HashTable:
def __init__(self, size=53):
self.key_map = [[] for _ in range(size)]
def _hash(self, key):
total = 0
PRIME = 31
for i, char in enumerate(str(key)[:100]):
total = (total * PRIME + ord(char)) % len(self.key_map)
return total
def search(self, key):
index = self._hash(key)
for k, v in self.key_map[index]:
if k == key:
return v
return None
Complexity: O(1) average
Use When: Fast lookups needed
Binary Search Trees
Definition
Binary Search Trees (BSTs) are hierarchical data structures where each node has at most two children, and for every node, all left descendants are smaller and all right descendants are larger.
How It Works
Start at root
- Set current node = root
- Compare target with current node value
Traverse left or right
- If target equals current value → found
- If target < current value → go left
- If target > current value → go right
Continue until found or null
- Repeat step 2 until target found
- If current becomes null, target not in tree
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
class BST:
def __init__(self):
self.root = None
def search(self, value, node=None):
if node is None:
node = self.root
if not node:
return False
if value == node.value:
return True
if value < node.value:
return self.search(value, node.left)
return self.search(value, node.right)
Complexity: O(h) time, O(1) space
Balanced: O(log n) height
Search Algorithm Cheat Sheet
| Scenario | Best Algorithm | Time Complexity |
|---|---|---|
| Unsorted small array | Linear Search | O(n) |
| Sorted array | Binary Search | O(log n) |
| Unbounded array | Exponential Search | O(log i) |
| Uniform distribution | Interpolation Search | O(log log n) |
| Key-value pairs | Hash Table | O(1) |
4. Real-World Applications
Database Indexing
B-Trees: Balanced search trees for disk-based storage
class BTreeNode:
def __init__(self, leaf=False):
self.leaf = leaf
self.keys = []
self.values = []
self.children = []
def btree_search(node, key):
i = 0
while i < len(node.keys) and key > node.keys[i]:
i += 1
if i < len(node.keys) and key == node.keys[i]:
return node.values[i]
if node.leaf:
return None
return btree_search(node.children[i], key)
Autocomplete Systems
Trie Search: Prefix-based string searching
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end
def starts_with(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return []
node = node.children[char]
return self._get_words(node, prefix)
Geospatial Data
Quadtree/K-d Tree: Nearest neighbor searches
import math
class KDNode:
def __init__(self, point, depth=0):
self.point = point
self.left = None
self.right = None
self.depth = depth
def distance(p1, p2):
return math.sqrt(sum((a - b) ** 2 for a, b in zip(p1, p2)))
def nearest_neighbor(node, point, best=None):
if not node:
return best
dist = distance(node.point, point)
if not best or dist < distance(best, point):
best = node.point
axis = node.depth % 2
next_branch = node.left if point[axis] < node.point[axis] else node.right
other_branch = node.right if next_branch == node.left else node.left
best = nearest_neighbor(next_branch, point, best)
if abs(point[axis] - node.point[axis]) < distance(best, point):
best = nearest_neighbor(other_branch, point, best)
return best
Search Algorithm Selection Guide
Pro Tip: Modern systems combine multiple search techniques. For example, databases use B-trees (logarithmic search) with hash indexes for exact matches. Always consider your data characteristics before choosing an algorithm.
You need to be logged in to participate in this discussion.