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.
Predicate Search
basic.py
# 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 =
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 =
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 =
print("List:", numbers)
print("Search 8:", linear_search(numbers, 8))
print("Search 1:", linear_search(numbers, 1))
print("Search 7:", linear_search(numbers, 7))
strings.py
# 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"))
find_all.py
# 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))
count.py
# 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}")
predicate.py
# 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)
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
When to Use
- Small lists
- Unsorted data
- Single search operation
- When simplicity matters more than speed
sequential_search
Examine each element in order from first to last until finding the target
find_all
Return all indices where the target appears, not just the first
predicate_search
Search using a condition function instead of a specific value
time_complexity
O(n) - must potentially check every element in the worst case
Exercise: practical.py
Implement a search function that finds products by name in a shopping cart and returns their total price