Common Algorithms
Linear Search
Finding a specific item in a collection is one of the most common programming tasks, from looking up a user in a database to checking if an email exists in a list. Linear search provides a straightforward approach that works on any list, checking each element one by one until the target is found.
Linear search checks each element in a list sequentially until finding the target or reaching the end. It works on both sorted and unsorted lists.
Algorithm
- Start at the first element
- Compare each element with the target
- If found, return the index
- If not found after checking all elements, return -1
sequential_search
Examine each element in order from first to last until finding the target
Basic Implementation
basic.py
Replay: real traced execution (multi-file project)
# Basic linear search
def linear_search(arr, target):
"""Search for target in array, return index or -1"""
# Search each element sequentially
for i in range(len(arr)):
if arr[i] == target:
return i # found - return index
return -1 # not found
# Test linear search
numbers = [5, 2, 8, 1, 9, 3]
print("List:", numbers)
print("Search 8:", linear_search(numbers, 8))
print("Search 1:", linear_search(numbers, 1))
print("Search 7:", linear_search(numbers, 7))
# Basic linear search
def linear_search(arr, target):
"""Search for target in array, return index or -1"""
# Search each element sequentially
for i in range(len(arr)):
if arr[i] == target:
return i # found - return index
return -1 # not found
# Test linear search
numbers = [4, 7, 2, 7]
print("List:", numbers)
print("Search 8:", linear_search(numbers, 8))
print("Search 1:", linear_search(numbers, 1))
print("Search 7:", linear_search(numbers, 7))
# Basic linear search
def linear_search(arr, target):
"""Search for target in array, return index or -1"""
# Search each element sequentially
for i in range(len(arr)):
if arr[i] == target:
return i # found - return index
return -1 # not found
# Test linear search
numbers = [10, 20, 30]
print("List:", numbers)
print("Search 8:", linear_search(numbers, 8))
print("Search 1:", linear_search(numbers, 1))
print("Search 7:", linear_search(numbers, 7))
numbers ← [5, 2, 8, 1, 9, 3]
13# Test linear search14numbers→ [5, 2, 8, 1, 9, 3] = [5, 2, 8, 1, 9, 3]15#@numbers=[4, 7, 2, 7], [10, 20, 30]1617print("List:", numbers[5, 2, 8, 1, 9, 3])18print("Search 8:", linear_search(numbers[5, 2, 8, 1, 9, 3], 8))19print("Search 1:", linear_search(numbers, 1))outputList: [5, 2, 8, 1, 9, 3]def linear_search(arr, target):
pass 1 of 34def linear_search(arr[5, 2, 8, 1, 9, 3], target8):5 """Search for target in array, return index or -1"""6 # Search each element sequentiallyAll 3 passes — pass 1 is the card above pass targetarr[i]i1 8 8 2 2 1 1 3 3 7 — — for i in range(len(arr)):
pass 1 of 136# Search each element sequentially7for i0 in range(len(arr[5, 2, 8, 1, 9, 3])):8 if arr[i] == target:9 return i # found - return index13 passes — pass 1 is the card above pass iarr[i]target1 0 — — 2 1 — — 3 2 8 8 4 0 — — 5 1 — — 6 2 — — 7 3 1 1 8 0 — — 9 1 — — ⋯ 2 more passes ⋯ 12 4 — — 13 5 — — if arr[i] == target:
pass 1 of 27for i in range(len(arr)):8 if arr[i]8 == target8:9 return i2 # found - return index10return -1 # not foundprint("Search 8:", linear_search(numbers, 8))
17print("List:", numbers)18print("Search 8:", linear_search(numbers[5, 2, 8, 1, 9, 3], 8))19print("Search 1:", linear_search(numbers[5, 2, 8, 1, 9, 3], 1))20print("Search 7:", linear_search(numbers, 7))outputSearch 8: 2if arr[i] == target:
pass 2 of 27for i in range(len(arr)):8 if arr[i]1 == target1:9 return i3 # found - return index10return -1 # not foundprint("Search 1:", linear_search(numbers, 1))
18print("Search 8:", linear_search(numbers, 8))19print("Search 1:", linear_search(numbers[5, 2, 8, 1, 9, 3], 1))20print("Search 7:", linear_search(numbers[5, 2, 8, 1, 9, 3], 7))outputSearch 1: 3return -1 # not found
9 return i # found - return index10return -1 # not foundprint("Search 7:", linear_search(numbers, 7))
19print("Search 1:", linear_search(numbers, 1))20print("Search 7:", linear_search(numbers[5, 2, 8, 1, 9, 3], 7))outputSearch 7: -1
numbers ← [4, 7, 2, 7]
13# Test linear search14numbers→ [4, 7, 2, 7] = [4, 7, 2, 7]1516print("List:", numbers[4, 7, 2, 7])17print("Search 8:", linear_search(numbers[4, 7, 2, 7], 8))18print("Search 1:", linear_search(numbers, 1))outputList: [4, 7, 2, 7]def linear_search(arr, target):
pass 1 of 34def linear_search(arr[4, 7, 2, 7], target8):5 """Search for target in array, return index or -1"""6 # Search each element sequentiallyAll 3 passes — pass 1 is the card above pass targetarr[i]i1 8 — — 2 1 — — 3 7 7 1 for i in range(len(arr)):
pass 1 of 106# Search each element sequentially7for i0 in range(len(arr[4, 7, 2, 7])):8 if arr[i] == target:9 return i # found - return indexAll 10 passes — pass 1 is the card above pass iarr[i]target1 0 — — 2 1 — — 3 2 — — 4 3 — — 5 0 — — 6 1 — — 7 2 — — 8 3 — — 9 0 — — 10 1 7 7 return -1 # not found
9 return i # found - return index10return -1 # not foundprint("Search 8:", linear_search(numbers, 8))
16print("List:", numbers)17print("Search 8:", linear_search(numbers[4, 7, 2, 7], 8))18print("Search 1:", linear_search(numbers[4, 7, 2, 7], 1))19print("Search 7:", linear_search(numbers, 7))outputSearch 8: -1return -1 # not found
9 return i # found - return index10return -1 # not foundprint("Search 1:", linear_search(numbers, 1))
17print("Search 8:", linear_search(numbers, 8))18print("Search 1:", linear_search(numbers[4, 7, 2, 7], 1))19print("Search 7:", linear_search(numbers[4, 7, 2, 7], 7))outputSearch 1: -1if arr[i] == target:
7for i in range(len(arr)):8 if arr[i]7 == target7:9 return i1 # found - return index10return -1 # not foundprint("Search 7:", linear_search(numbers, 7))
18print("Search 1:", linear_search(numbers, 1))19print("Search 7:", linear_search(numbers[4, 7, 2, 7], 7))outputSearch 7: 1
numbers ← [10, 20, 30]
13# Test linear search14numbers→ [10, 20, 30] = [10, 20, 30]1516print("List:", numbers[10, 20, 30])17print("Search 8:", linear_search(numbers[10, 20, 30], 8))18print("Search 1:", linear_search(numbers, 1))outputList: [10, 20, 30]def linear_search(arr, target):
pass 1 of 34def linear_search(arr[10, 20, 30], target8):5 """Search for target in array, return index or -1"""6 # Search each element sequentiallyAll 3 passes — pass 1 is the card above pass target1 8 2 1 3 7 for i in range(len(arr)):
pass 1 of 96# Search each element sequentially7for i0 in range(len(arr[10, 20, 30])):8 if arr[i] == target:9 return i # found - return indexAll 9 passes — pass 1 is the card above pass i1 0 2 1 3 2 4 0 5 1 6 2 7 0 8 1 9 2 return -1 # not found
9 return i # found - return index10return -1 # not foundprint("Search 8:", linear_search(numbers, 8))
16print("List:", numbers)17print("Search 8:", linear_search(numbers[10, 20, 30], 8))18print("Search 1:", linear_search(numbers[10, 20, 30], 1))19print("Search 7:", linear_search(numbers, 7))outputSearch 8: -1return -1 # not found
9 return i # found - return index10return -1 # not foundprint("Search 1:", linear_search(numbers, 1))
17print("Search 8:", linear_search(numbers, 8))18print("Search 1:", linear_search(numbers[10, 20, 30], 1))19print("Search 7:", linear_search(numbers[10, 20, 30], 7))outputSearch 1: -1return -1 # not found
9 return i # found - return index10return -1 # not foundprint("Search 7:", linear_search(numbers, 7))
18print("Search 1:", linear_search(numbers, 1))19print("Search 7:", linear_search(numbers[10, 20, 30], 7))outputSearch 7: -1
Searching Strings
strings.py
Replay: real traced execution (multi-file project)
# Search strings
def search_string(arr, target):
"""Search for exact string match"""
for i in range(len(arr)):
if arr[i] == target:
return i
return -1
def search_case_insensitive(arr, target):
"""Search ignoring case"""
# Case-insensitive search
target_lower = target.lower()
for i in range(len(arr)):
if arr[i].lower() == target_lower:
return i
return -1
# Test string searches
fruits = ["apple", "banana", "cherry", "date", "elderberry"]
print("Fruits:", fruits)
print("Search 'cherry':", search_string(fruits, "cherry"))
print("Search 'grape':", search_string(fruits, "grape"))
print("\nCase-insensitive:")
print("Search 'BANANA':", search_case_insensitive(fruits, "BANANA"))
print("Search 'Apple':", search_case_insensitive(fruits, "Apple"))
fruits ← ['apple', 'banana', 'cherry', 'date', 'elderberry']
22# Test string searches23fruits→ ['apple', 'banana', 'cherry', 'date', 'elderberry'] = ["apple", "banana", "cherry", "date", "elderberry"]2425print("Fruits:", fruits['apple', 'banana', 'cherry', 'date', 'elderberry'])26print("Search 'cherry':", search_string(fruits['apple', 'banana', 'cherry', 'date', 'elderberry'], "cherry"))27print("Search 'grape':", search_string(fruits, "grape"))outputFruits: ['apple', 'banana', 'cherry', 'date', 'elderberry']def search_string(arr, target):
pass 1 of 24def search_string(arr['apple', 'banana', 'cherry', 'date', 'elderberry'], targetcherry):5 """Search for exact string match"""6 for i in range(len(arr)):for i in range(len(arr)):
pass 1 of 85"""Search for exact string match"""6for i0 in range(len(arr['apple', 'banana', 'cherry', 'date', 'elderberry'])):7 if arr[i] == target:8 return iAll 8 passes — pass 1 is the card above pass iarr[i]target1 0 — — 2 1 — — 3 2 cherry cherry 4 0 — — 5 1 — — 6 2 — — 7 3 — — 8 4 — — if arr[i] == target:
6for i in range(len(arr)):7 if arr[i]cherry == targetcherry:8 return i29return -1print("Search 'cherry':", search_string(fruits, "cherry"))
25print("Fruits:", fruits)26print("Search 'cherry':", search_string(fruits['apple', 'banana', 'cherry', 'date', 'elderberry'], "cherry"))27print("Search 'grape':", search_string(fruits['apple', 'banana', 'cherry', 'date', 'elderberry'], "grape"))outputSearch 'cherry': 2def search_string(arr, target):
pass 2 of 24def search_string(arr['apple', 'banana', 'cherry', 'date', 'elderberry'], targetgrape):5 """Search for exact string match"""6 for i in range(len(arr)):return -1
8 return i9return -1print("Search 'grape':", search_string(fruits, "grape"))
26print("Search 'cherry':", search_string(fruits, "cherry"))27print("Search 'grape':", search_string(fruits['apple', 'banana', 'cherry', 'date', 'elderberry'], "grape"))2829print("\nCase-insensitive:")30print("Search 'BANANA':", search_case_insensitive(fruits['apple', 'banana', 'cherry', 'date', 'elderberry'], "BANANA"))31print("Search 'Apple':", search_case_insensitive(fruits, "Apple"))outputSearch 'grape': -1 Case-insensitive:target_lower ← banana
pass 1 of 212def search_case_insensitive(arr['apple', 'banana', 'cherry', 'date', 'elderberry'], targetBANANA):13 """Search ignoring case"""14 # Case-insensitive search15 target_lower→ banana = targetBANANA.lower()16 for i in range(len(arr)):for i in range(len(arr)):
pass 1 of 315target_lower = target.lower()16for i0 in range(len(arr['apple', 'banana', 'cherry', 'date', 'elderberry'])):17 if arr[i].lower() == target_lower:18 return iAll 3 passes — pass 1 is the card above pass iarr[i]target_lower1 0 — — 2 1 banana banana 3 0 apple apple if arr[i].lower() == target_lower:
pass 1 of 216for i in range(len(arr)):17 if arr[i]banana.lower() == target_lowerbanana:18 return i119return -1print("Search 'BANANA':", search_case_insensitive(fruits, "BANANA"))
29print("\nCase-insensitive:")30print("Search 'BANANA':", search_case_insensitive(fruits['apple', 'banana', 'cherry', 'date', 'elderberry'], "BANANA"))31print("Search 'Apple':", search_case_insensitive(fruits['apple', 'banana', 'cherry', 'date', 'elderberry'], "Apple"))outputSearch 'BANANA': 1target_lower ← apple
pass 2 of 212def search_case_insensitive(arr['apple', 'banana', 'cherry', 'date', 'elderberry'], targetApple):13 """Search ignoring case"""14 # Case-insensitive search15 target_lower→ apple = targetApple.lower()16 for i in range(len(arr)):if arr[i].lower() == target_lower:
pass 2 of 216for i in range(len(arr)):17 if arr[i]apple.lower() == target_lowerapple:18 return i019return -1print("Search 'Apple':", search_case_insensitive(fruits, "Apple"))
30print("Search 'BANANA':", search_case_insensitive(fruits, "BANANA"))31print("Search 'Apple':", search_case_insensitive(fruits['apple', 'banana', 'cherry', 'date', 'elderberry'], "Apple"))outputSearch 'Apple': 0
Find All Occurrences
find_all.py
Replay: real traced execution (multi-file project)
# Find all occurrences
def find_all(arr, target):
"""Return list of all indices where target appears"""
indices = []
# Collect all matching indices
for i in range(len(arr)):
if arr[i] == target:
indices.append(i)
return indices
# Test finding multiple occurrences
numbers = [3, 7, 3, 9, 3, 1, 3]
print("List:", numbers)
print("All 3's at:", find_all(numbers, 3))
print("All 9's at:", find_all(numbers, 9))
print("All 5's at:", find_all(numbers, 5))
numbers ← [3, 7, 3, 9, 3, 1, 3]
16# Test finding multiple occurrences17numbers→ [3, 7, 3, 9, 3, 1, 3] = [3, 7, 3, 9, 3, 1, 3]1819print("List:", numbers[3, 7, 3, 9, 3, 1, 3])20print("All 3's at:", find_all(numbers[3, 7, 3, 9, 3, 1, 3], 3))21print("All 9's at:", find_all(numbers, 9))outputList: [3, 7, 3, 9, 3, 1, 3]indices ← []
pass 1 of 34def find_all(arr[3, 7, 3, 9, 3, 1, 3], target3):5 """Return list of all indices where target appears"""6 indices→ [] = []All 3 passes — pass 1 is the card above pass targetindices1 3 [] 2 9 [] 3 5 [] for i in range(len(arr)):
pass 1 of 218# Collect all matching indices9for i0 in range(len(arr[3, 7, 3, 9, 3, 1, 3])):10 if arr[i] == target:11 indices.append(i)21 passes — pass 1 is the card above pass i1 0 2 1 3 2 4 3 5 4 6 5 7 6 8 0 9 1 ⋯ 10 more passes ⋯ 20 5 21 6 indices ← [0]
pass 1 of 59for i in range(len(arr)):10 if arr[i]3 == target3:11 indices→ [0].append(i0)All 5 passes — pass 1 is the card above pass arr[i]targetiindices1 3 3 0 [] → [0] 2 3 3 2 [0] → [0, 2] 3 3 3 4 [0, 2] → [0, 2, 4] 4 3 3 6 [0, 2, 4] → [0, 2, 4, 6] 5 9 9 3 [] → [3] return indices
13return indices[0, 2, 4, 6]print("All 3's at:", find_all(numbers, 3))
19print("List:", numbers)20print("All 3's at:", find_all(numbers[3, 7, 3, 9, 3, 1, 3], 3))21print("All 9's at:", find_all(numbers[3, 7, 3, 9, 3, 1, 3], 9))22print("All 5's at:", find_all(numbers, 5))outputAll 3's at: [0, 2, 4, 6]return indices
13return indices[3]print("All 9's at:", find_all(numbers, 9))
20print("All 3's at:", find_all(numbers, 3))21print("All 9's at:", find_all(numbers[3, 7, 3, 9, 3, 1, 3], 9))22print("All 5's at:", find_all(numbers[3, 7, 3, 9, 3, 1, 3], 5))outputAll 9's at: [3]return indices
13return indices[]print("All 5's at:", find_all(numbers, 5))
21print("All 9's at:", find_all(numbers, 9))22print("All 5's at:", find_all(numbers[3, 7, 3, 9, 3, 1, 3], 5))outputAll 5's at: []
find_all
Return all indices where the target appears, not just the first
Count Occurrences
count.py
Replay: real traced execution (multi-file project)
# Search with count
def linear_search_counted(arr, target):
"""Return (index, comparisons) tuple"""
comparisons = 0
# Count each comparison
for i in range(len(arr)):
comparisons += 1
if arr[i] == target:
return (i, comparisons)
return (-1, comparisons)
# Test with different positions
numbers = [10, 20, 30, 40, 50]
idx1, comp1 = linear_search_counted(numbers, 10) # first element
print(f"Search 10: index={idx1}, comparisons={comp1}")
idx2, comp2 = linear_search_counted(numbers, 50) # last element
print(f"Search 50: index={idx2}, comparisons={comp2}")
idx3, comp3 = linear_search_counted(numbers, 99) # not found
print(f"Search 99: index={idx3}, comparisons={comp3}")
numbers ← [10, 20, 30, 40, 50]
17# Test with different positions18numbers→ [10, 20, 30, 40, 50] = [10, 20, 30, 40, 50]1920idx1, comp1 = linear_search_counted(numbers[10, 20, 30, 40, 50], 10) # first element21print(f"Search 10: index={idx1}, comparisons={comp1}")comparisons ← 0
pass 1 of 34def linear_search_counted(arr[10, 20, 30, 40, 50], target10):5 """Return (index, comparisons) tuple"""6 comparisons→ 0 = 0All 3 passes — pass 1 is the card above pass targetarr[i]icomparisons1 10 10 0 0 2 50 50 4 0 3 99 — — 0 comparisons ← 1
pass 1 of 118# Count each comparison9for i0 in range(len(arr[10, 20, 30, 40, 50])):10 comparisons→ 1 += 111 if arr[i] == target:All 11 passes — pass 1 is the card above pass iarr[i]targetcomparisons1 0 10 10 0 → 1 2 0 — — 0 → 1 3 1 — — 1 → 2 4 2 — — 2 → 3 5 3 — — 3 → 4 6 4 50 50 4 → 5 7 0 — — 0 → 1 8 1 — — 1 → 2 9 2 — — 2 → 3 10 3 — — 3 → 4 11 4 — — 4 → 5 if arr[i] == target:
pass 1 of 210comparisons += 111if arr[i]10 == target10:12 return (i0, comparisons1)idx1 ← 0, comp1 ← 1
20idx1→ 0, comp1→ 1 = linear_search_counted(numbers[10, 20, 30, 40, 50], 10) # first element21print(f"Search 10: index={idx10}, comparisons={comp11}")2223idx2, comp2 = linear_search_counted(numbers[10, 20, 30, 40, 50], 50) # last element24print(f"Search 50: index={idx2}, comparisons={comp2}")outputSearch 10: index=0, comparisons=1if arr[i] == target:
pass 2 of 210comparisons += 111if arr[i]50 == target50:12 return (i4, comparisons5)idx2 ← 4, comp2 ← 5
23idx2→ 4, comp2→ 5 = linear_search_counted(numbers[10, 20, 30, 40, 50], 50) # last element24print(f"Search 50: index={idx24}, comparisons={comp25}")2526idx3, comp3 = linear_search_counted(numbers[10, 20, 30, 40, 50], 99) # not found27print(f"Search 99: index={idx3}, comparisons={comp3}")outputSearch 50: index=4, comparisons=5return (-1, comparisons)
14return (-1, comparisons5)idx3 ← -1, comp3 ← 5
26idx3→ -1, comp3→ 5 = linear_search_counted(numbers[10, 20, 30, 40, 50], 99) # not found27print(f"Search 99: index={idx3-1}, comparisons={comp35}")outputSearch 99: index=-1, comparisons=5
Predicate Search
predicate.py
Replay: real traced execution (multi-file project)
# Search with condition
def find_first(arr, condition):
"""Find first element matching condition function"""
# Search for first element matching condition
for i in range(len(arr)):
if condition(arr[i]):
return i
return -1
# Test with different conditions
numbers = [3, 7, 12, 5, 18, 9]
print("List:", numbers)
# Find first even number
even_idx = find_first(numbers, lambda x: x % 2 == 0)
print("First even at index:", even_idx)
# Find first number > 10
large_idx = find_first(numbers, lambda x: x > 10)
print("First > 10 at index:", large_idx)
# Find first negative (not found)
neg_idx = find_first(numbers, lambda x: x < 0)
print("First negative at index:", neg_idx)
numbers ← [3, 7, 12, 5, 18, 9]
13# Test with different conditions14numbers→ [3, 7, 12, 5, 18, 9] = [3, 7, 12, 5, 18, 9]1516print("List:", numbers[3, 7, 12, 5, 18, 9])1718# Find first even number19even_idx = find_first(numbers[3, 7, 12, 5, 18, 9], lambda x: x % 2 == 0)20print("First even at index:", even_idx)outputList: [3, 7, 12, 5, 18, 9]def find_first(arr, condition):
pass 1 of 34def find_first(arr[3, 7, 12, 5, 18, 9], condition<function <lambda> at ⟨addr A⟩>):5 """Find first element matching condition function"""6 # Search for first element matching conditionAll 3 passes — pass 1 is the card above pass arr[i]i1 12 2 2 12 2 3 — — for i in range(len(arr)):
pass 1 of 126# Search for first element matching condition7for i0 in range(len(arr[3, 7, 12, 5, 18, 9])):8 if condition(arr[i]):9 return iAll 12 passes — pass 1 is the card above pass iarr[i]1 0 — 2 1 — 3 2 12 4 0 — 5 1 — 6 2 12 7 0 — 8 1 — 9 2 — 10 3 — 11 4 — 12 5 — if condition(arr[i]):
pass 1 of 27for i in range(len(arr)):8 if condition(arr[i]12):9 return i210return -1even_idx ← 2
18# Find first even number19even_idx→ 2 = find_first(numbers[3, 7, 12, 5, 18, 9], lambda x: x % 2 == 0)20print("First even at index:", even_idx2)2122# Find first number > 1023large_idx = find_first(numbers[3, 7, 12, 5, 18, 9], lambda x: x > 10)24print("First > 10 at index:", large_idx)outputFirst even at index: 2if condition(arr[i]):
pass 2 of 27for i in range(len(arr)):8 if condition(arr[i]12):9 return i210return -1large_idx ← 2
22# Find first number > 1023large_idx→ 2 = find_first(numbers[3, 7, 12, 5, 18, 9], lambda x: x > 10)24print("First > 10 at index:", large_idx2)2526# Find first negative (not found)27neg_idx = find_first(numbers[3, 7, 12, 5, 18, 9], lambda x: x < 0)28print("First negative at index:", neg_idx)outputFirst > 10 at index: 2return -1
9 return i10return -1neg_idx ← -1
26# Find first negative (not found)27neg_idx→ -1 = find_first(numbers[3, 7, 12, 5, 18, 9], lambda x: x < 0)28print("First negative at index:", neg_idx-1)outputFirst negative at index: -1
predicate_search
Search using a condition function instead of a specific value
Characteristics
- Time complexity: O(n) - must potentially check every element
- Space complexity: O(1) - only uses a few variables
- Works on: unsorted or sorted lists
- Best case: O(1) - target is first element
- Worst case: O(n) - target is last element or not present
time_complexity
O(n) - must potentially check every element in the worst case
When to Use
- Small lists
- Unsorted data
- Single search operation
- When simplicity matters more than speed
Exercise: practical.py
Implement a search function that finds products by name in a shopping cart and returns their total price