๐Ÿ” Search Algorithms in Computer Science#

๐Ÿง  What Is a Search Algorithm?#

A search algorithm is a method for finding a specific item or value within a data structure such as an array, list, tree, or graph. It answers the question:

โ€œWhere is the item Iโ€™m looking for?โ€

Search algorithms are fundamental to computing, powering everything from database queries to AI pathfinding.


๐Ÿ“‚ Types of Search Algorithms#

5. Graph Search (DFS/BFS)#

  • Description: Explores nodes in a graph using depth-first or breadth-first strategies.

  • Time Complexity: O(V + E)

  • Use Case: Pathfinding, network traversal, AI.


๐Ÿ”„ Searching vs Sorting#

Feature

Searching

Sorting

Goal

Find a specific item

Arrange items in a specific order

Input Requirement

May require sorted data (e.g., binary search)

Works on unsorted data

Output

Index or presence of item

Entire dataset reordered

Examples

Linear, Binary, Hash Search

Bubble, Merge, Quick Sort


๐Ÿง  Conceptual Questions#

  1. Why does binary search require sorted data?

  2. How does hashing achieve constant-time search?

  3. What trade-offs exist between tree-based and hash-based search?

  4. When would you prefer BFS over DFS in graph search?

  5. How does the structure of data affect search performance?


๐Ÿ“ Mini Quiz#

Question 1:
Which search algorithm is most efficient for large sorted arrays?

  • A) Linear Search

  • B) Binary Search

  • C) Hash Search

  • D) DFS
    โœ… Correct Answer: B

Question 2:
Which search algorithm uses a hash function to locate items?

  • A) Binary Search

  • B) Tree Search

  • C) Hash Search

  • D) BFS
    โœ… Correct Answer: C

Question 3:
Which search algorithm explores all neighbors before going deeper?

  • A) DFS

  • B) BFS

  • C) Binary Search

  • D) Hash Search
    โœ… Correct Answer: B


๐ŸŽ‰ Fun Facts About Searching#

  • ๐Ÿง  Binary search is so efficient it can search a billion items in ~30 steps.

  • ๐Ÿงฉ Hash tables are the backbone of Pythonโ€™s dict and set types.

  • ๐Ÿ•ธ๏ธ Graph search algorithms are used in Google Maps, social networks, and AI.

  • ๐Ÿงช Search algorithms are often combined with sorting for optimal performance (e.g., sort first, then binary search).


๐Ÿ” Reflection Prompt#

Think of a real-world scenario (e.g., searching a contact, finding a route, locating a file).
Which search algorithm would be most appropriate and why? Consider data structure, speed, and memory usage.

import numpy as np
import ipywidgets as widgets
from IPython.display import display, Markdown

### ๐Ÿ” Search Algorithms
def linear_search(arr, target):
    for i, val in enumerate(arr):
        if val == target:
            return i
    return -1

def binary_search(arr, target):
    arr = sorted(arr)
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

def hash_search(arr, target):
    lookup = {val: i for i, val in enumerate(arr)}
    return lookup.get(target, -1)

# ๐Ÿง  Algorithm dictionary
algorithms = {
    "Linear Search": linear_search,
    "Binary Search": binary_search,
    "Hash Search": hash_search
}

### ๐ŸŽ›๏ธ Widgets
size_slider = widgets.IntSlider(value=20, min=10, max=100, step=10, description="Data Size:")
target_input = widgets.IntText(value=42, description="Target Value:")
algo_dropdown = widgets.Dropdown(options=list(algorithms.keys()), value="Linear Search", description="Algorithm:")
run_button = widgets.Button(description="Run Search", button_style='success')
output = widgets.Output()

### ๐Ÿงช Run search
def on_run_click(b):
    output.clear_output()
    np.random.seed(0)
    data = np.random.randint(0, 100, size_slider.value).tolist()
    target = target_input.value
    algo_name = algo_dropdown.value
    func = algorithms[algo_name]
    result = func(data, target)
    with output:
        display(Markdown(f"### ๐Ÿ” {algo_name} Result"))
        print(f"Data: {data}")
        print(f"Target: {target}")
        if result != -1:
            print(f"โœ… Found at index {result}")
        else:
            print("โŒ Not found")

run_button.on_click(on_run_click)

### ๐Ÿš€ Display UI
display(size_slider, target_input, algo_dropdown, run_button, output)
import numpy as np
import time
import matplotlib.pyplot as plt
import ipywidgets as widgets
from IPython.display import display, Markdown

# ๐Ÿ” Search Algorithms
def linear_search(arr, target):
    for i, val in enumerate(arr):
        if val == target:
            return i
    return -1

def binary_search(arr, target):
    arr = sorted(arr)
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

def hash_search(arr, target):
    lookup = {val: i for i, val in enumerate(arr)}
    return lookup.get(target, -1)

# ๐Ÿง  Algorithm dictionary
algorithms = {
    "Linear Search": linear_search,
    "Binary Search": binary_search,
    "Hash Search": hash_search
}

# ๐ŸŽ›๏ธ Widgets
size_slider = widgets.IntSlider(value=1000, min=100, max=10000, step=100, description="Data Size:")
target_input = widgets.IntText(value=42, description="Target Value:")
compare_button = widgets.Button(description="Compare Performance", button_style='info')
output = widgets.Output()

# ๐Ÿ“Š Compare performance
def on_compare_click(b):
    output.clear_output()
    np.random.seed(0)
    data = np.random.randint(0, 10000, size_slider.value).tolist()
    target = target_input.value
    times = {}
    results = {}

    for name, func in algorithms.items():
        start = time.time()
        index = func(data, target)
        duration = time.time() - start
        times[name] = duration
        results[name] = index

    with output:
        display(Markdown("### ๐Ÿ“Š Search Algorithm Performance Summary"))
        print(f"๐Ÿ”ข Data Sample (first 20 of {len(data)}): {data[:20]}")
        print(f"๐ŸŽฏ Target Value: {target}\n")
        for name in algorithms:
            found_msg = f"โœ… Found at index {results[name]}" if results[name] != -1 else "โŒ Not found"
            print(f"{name:<15} โžค Time: {times[name]:.6f} sec | {found_msg}")

        # Plotting
        plt.figure(figsize=(8, 5))
        plt.bar(times.keys(), times.values(), color='lightgreen')
        plt.ylabel("Execution Time (seconds)")
        plt.title("Search Algorithm Comparison")
        plt.grid(True, linestyle='--', alpha=0.5)
        plt.tight_layout()
        plt.show()

compare_button.on_click(on_compare_click)

# ๐Ÿš€ Display UI
display(size_slider, target_input, compare_button, output)