View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All

Data Structures & Algorithm in Python: Everything You Need to Know

By Rohit Sharma

Updated on Apr 25, 2025 | 12 min read | 45.6k views

Share:

Imagine being in an unfamiliar location without maps or GPS to guide you—you would likely feel disoriented. Similarly, a program tasked with solving a problem lacks direction without a well-defined plan. An algorithm serves as this essential guide, providing clear, step-by-step instructions to help navigate the process and reach the desired outcome.

Did you know entrepreneurs using data-driven decision-making have seen a 5-6% increase in company productivity? The heart of this productivity boost? Well-crafted algorithms that power everything from data analysis to artificial intelligence. This blog provides an overview of various algorithms and data structure in Python, allowing you to excel in Python programming.

Take your Python skills to the next level with our Artificial Intelligence & Machine Learning Courses and build cutting-edge projects!

 What Are the Types of Data Structure Algorithms in Python?

You can categorize Python algorithms on the basis of their purpose and design principles. Understanding the types can help you effectively choose and implement appropriate algorithms to solve various computational problems like searching and sorting in Python.

Here’s an overview of the common types of algorithms related to data structure in Python.

Sorting Algorithms in Python

The purpose of a sorting algorithm is to organize data in a specific order — ascending or descending. 

Some common examples of sorting algorithms include the following.

  • Bubble Sort: Compares adjacent elements and swaps them if out of order 
  • Merge Sort: Divides the data into smaller parts, sorts them, and merges them.
  • Quick Sort: Uses a "pivot" element to partition data and recursively sort.
  • Insertion Sort: Build a sorted list one element at a time.

Code:

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr

Enhance your skills with our industry-relevant AI and analytics courses:

 Searching Algorithms in Python

The purpose of a searching algorithm is to find a specific element in a dataset.

Common type of searching algorithms in Python include the following.

  • Linear Search: Iterates through each element until the desired one is found.
  • Binary Search: Divides a sorted list in half repeatedly to find the target.

Code

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

Graph Algorithms in Python

Graph algorithms process data structure in Python programs in the form of graphs.
Here are some common examples of graph algorithms.

  • Depth-First Search (DFS): Explores as far down one branch as possible before going back.
  • Breadth-First Search (BFS): Explores all nodes at the present depth before moving deeper.
  • Dijkstra’s Algorithm: Locates the shortest path in a weighted graph.

Code

from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    while queue:
        node = queue.popleft()
        if node not in visited:
            print(node, end=" ")
            visited.add(node)
            queue.extend(graph[node] - visited)

Dynamic Programming

Dynamic programming solves complex problems by breaking them into smaller subproblems and solving each separately.

Common examples of dynamic programming include the following.

  • Fibonacci sequence: Tabulation is used to store previously calculated values, avoiding redundant calculations and improving efficiency.
  • Knapsack problem: Optimizes resource allocation under constraints.

Code

def fibonacci(n):
    dp = [0, 1]
    for i in range(2, n+1):
        dp.append(dp[i-1] + dp[i-2])
    return dp[n]

Greedy Algorithms in Python

Greedy algorithms make the optimal choice at each step, looking for a global solution.

Common examples of the greedy algorithms in Python include the following.

  • Activity selection problem: Chooses the maximum number of activities that don't overlap.
  • Huffman encoding: Builds an optimal binary tree for data compression.

Code: 

def activity_selection(activities):
    activities.sort(key=lambda x: x[1])
    last_end_time = 0
    selected = []
    for start, end in activities:
        if start >= last_end_time:
            selected.append((start, end))
            last_end_time = end
    return selected

Backtracking Algorithms in Python

Backtracking explores possible solutions and "backs up" when a particular solution path fails.

Some common examples include the following.

  • N-Queens Problem: Place N-queens on a chessboard so no two threaten each other.
  • Sudoku Solver: Solves the puzzle by filling cells and backtracking when needed.

Code snippet: 

def solve_n_queens(board, col):
    if col >= len(board):
        print(board)
        return True
    for i in range(len(board)):
        if is_safe(board, i, col):
            board[i][col] = 1
            if solve_n_queens(board, col + 1):
                return True
            board[i][col] = 0
    return False

Now that you have an idea of the types of algorithms related to data structure in Python, let's discuss them in complete detail.

 What Are the Common Sorting Algorithms in Python?

Sorting algorithms are fundamental tools in computer science that can arrange data in a specific order, typically ascending or descending. These algorithms can arrange data efficiently, enabling quicker and more reliable access to information.

Here’s a breakdown of the common sorting algorithms in Python.

How Does Merge Sort Work?

Merge Sort is a popular divide-and-conquer sorting algorithm that breaks down a problem into smaller sub-problems, solves them, and then combines the results. It is efficient, stable, and works well for large datasets.

The merge sort follows the divide-and-conquer approach to solve the problem.

  • Divide: Split the unsorted array into two halves until each subarray contains a single element.
  • Conquer: Recursively sort each half.
  • Combine: Merging the two sorted halves into a single sorted array.

Code snippet:

def merge_sort(arr):
    # Base case: A single-element list is already sorted
    if len(arr) <= 1:
        return arr

    # Divide: Find the middle point and split the array
    mid = len(arr) // 2
    left_half = merge_sort(arr[:mid])
    right_half = merge_sort(arr[mid:])

    # Conquer: Merge the sorted halves
    return merge(left_half, right_half)

def merge(left, right):
    sorted_array = []
    i = j = 0

    # Compare elements from both halves and merge
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            sorted_array.append(left[i])
            i += 1
        else:
            sorted_array.append(right[j])
            j += 1

    # Append any remaining elements from the left or right half
    sorted_array.extend(left[i:])
    sorted_array.extend(right[j:])
    
    return sorted_array

# Example usage
unsorted_array = [38, 27, 43, 3, 9, 82, 10]
sorted_array = merge_sort(unsorted_array)
print("Sorted Array:", sorted_array)

What Is Heap Sort, and How Is It Implemented?

Heap Sort is a comparison-based sorting algorithm that organizes and sorts data by using the properties of a binary heap data structure. It sorts elements by repeatedly removing the largest (or smallest) element from the heap and placing it at the end of the array.

Here’s the binary heap data structure in Python.

  • Max-Heap: Each parent node is greater than or equal to its children.
  • Min-Heap: Each parent node is less than or equal to its children.

Code snippet:

def heapify(arr, n, i):
    """
    Function to maintain the max-heap property.
    :param arr: Array to heapify
    :param n: Size of the heap
    :param i: Index of the current node
    """
    largest = i  # Assume the current node is the largest
    left = 2 * i + 1  # Left child index
    right = 2 * i + 2  # Right child index

    # Check if the left child exists and is greater than the current largest
    if left < n and arr[left] > arr[largest]:
        largest = left

    # Check if the right child exists and is greater than the current largest
    if right < n and arr[right] > arr[largest]:
        largest = right

    # If the largest node is not the current node, swap and continue heapifying
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]  # Swap
        heapify(arr, n, largest)

def heap_sort(arr):
    """
    Main function to perform Heap Sort.
    :param arr: Array to be sorted
    """
    n = len(arr)

    # Step 1: Build a max-heap
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)

    # Step 2: Extract elements from the heap one by one
    for i in range(n - 1, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]  # Move the root to the end
        heapify(arr, i, 0)  # Restore the max-heap property for the reduced heap

# Example usage
unsorted_array = [4, 10, 3, 5, 1]
heap_sort(unsorted_array)
print("Sorted Array:", unsorted_array)

What Is Radix Sort, and When Should You Use It?

Radix Sort is a non-comparative integer sorting algorithm that sorts numbers (or strings) by processing individual digits or characters. It sorts numbers based on their individual digits, starting from the least significant digit (LSD) or the most significant digit (MSD) and repeatedly grouping numbers with the same digit.

Here are the steps followed in Radix Sort.

  • Find the largest number: The algorithm locates the largest number in the dataset to determine the number of digits (or place values) to process.
  • Sort by each digit: Start from the least significant digit (LSD) and sort the numbers into buckets. Then, reassemble the numbers.
  • Repeat for each digit: Move to the next digit, repeat the process, and reassemble the numbers until the most significant digit is processed.

Code snippet: 

def counting_sort(arr, exp):
    """
    A helper function to perform counting sort based on a digit represented by exp (10^i).
    :param arr: The array to be sorted
    :param exp: The digit place (1s, 10s, 100s, etc.)
    """
    n = len(arr)
    output = [0] * n  # Output array that will store sorted numbers
    count = [0] * 10  # Count array to store the frequency of digits (0-9)

    # Count occurrences of each digit in the current place
    for i in range(n):
        index = arr[i] // exp
        count[index % 10] += 1

    # Change count[i] to be the actual position of this digit in the output
    for i in range(1, 10):
        count[i] += count[i - 1]

    # Build the output array, ensuring stable sorting
    for i in range(n - 1, -1, -1):
        index = arr[i] // exp
        output[count[index % 10] - 1] = arr[i]
        count[index % 10] -= 1

    # Copy the sorted elements back into the original array
    for i in range(n):
        arr[i] = output[i]

def radix_sort(arr):
    """
    The main function to implement Radix Sort.
    :param arr: The array to be sorted
    """
    # Find the maximum number to determine the number of digits
    max_num = max(arr)

    # Perform counting sort for every digit (1s, 10s, 100s, etc.)
    exp = 1  # Start from the least significant digit (1s)
    while max_num // exp > 0:
        counting_sort(arr, exp)
        exp *= 10  # Move to the next digit

# Example usage
unsorted_array = [170, 45, 75, 90, 802, 24, 2, 66]
radix_sort(unsorted_array)
print("Sorted Array:", unsorted_array)

After discovering the working of sorting algorithms in Python, let’s explore searching algorithms.

Also Read: Selection Sort Algorithm in Data Structure 

 

background

Liverpool John Moores University

MS in Data Science

Dual Credentials

Master's Degree17 Months

Placement Assistance

Certification6 Months

How Do Searching Algorithms Work in Python?

Searching algorithms can locate specific elements within a collection of data. Python provides several efficient searching methods to retrieve information from various data structures. Here’s a quick look at the different searching algorithms in Python.

What Is Linear Search, and How Does It Work?

Linear Search checks every element of a list (or array) in sequence until the desired element is found or the list is completely searched. 

Here’s how Linear Search works.

  • Begin with the first element in the list.
  • Compare the current element with the target element.
  • If the element is found, return its position; otherwise, move to the next element.
  • Repeat until the element is found or the end of the list is reached.

Code snippet:

def linear_search(arr, target):
    """
    Function to perform linear search on a list.
    :param arr: List to be searched
    :param target: The element to search for
    :return: Index of the target element if found, otherwise -1
    """
    for i in range(len(arr)):
        if arr[i] == target:  # Check if the current element matches the target
            return i  # Return the index if element is found
    return -1  # Return -1 if target is not found

# Example usage
arr = [10, 20, 30, 40, 50]
target = 30
result = linear_search(arr, target)

if result != -1:
    print(f"Element {target} found at index {result}")
else:
    print(f"Element {target} not found")

How Does Binary Search Optimize Searching?

Binary Search finds the position of a target value within a sorted array or list by dividing the search interval in half. It is a classic example of the divide-and-conquer strategy.

Here’s how Binary Search works.

  • Split the array into two halves by finding the middle element.
  • If the target element is smaller than the middle element, search the left half; otherwise, search the right half.
  • The array is divided into smaller subproblems until the target is located or the search space is exhausted.

Code snippet:

def binary_search(arr, target):
    """
    Function to perform binary search on a sorted array.
    :param arr: Sorted list to be searched
    :param target: The element to search for
    :return: Index of the target element if found, otherwise -1
    """
    low = 0
    high = len(arr) - 1

    while low <= high:
        mid = (low + high) // 2  # Find the middle index
        
        # Check if the target is at the middle
        if arr[mid] == target:
            return mid
        
        # If the target is smaller, ignore the right half
        elif arr[mid] > target:
            high = mid - 1
        
        # If the target is larger, ignore the left half
        else:
            low = mid + 1
    
    # If the target is not found
    return -1

# Example usage
arr = [1, 3, 5, 7, 9, 11, 13]
target = 7
result = binary_search(arr, target)

if result != -1:
    print(f"Element {target} found at index {result}")
else:
    print(f"Element {target} not found")

You can check the following section for the graph algorithms in Python.

Also Read: Linear Search vs. Binary Search

What Are the Key Graph Algorithms in Python?

Graph algorithms can solve network-related problems in Python. Networks, represented as graphs, consist of nodes (vertices) and edges (connections between nodes). You can apply various graph algorithms to solve a wide range of network-related tasks efficiently.

Here’s a quick look at the different graph algorithms in Python.

How Does Depth-First Search (DFS) Traverse Graphs?

Depth-First Search (DFS) traverses as far as possible along a branch of the graph before backtracking. It starts at a given node and moves as far as possible down each branch before moving to the next one. The recursive approach is the most popular for DFS implementations.

Here’s how recursive DFS works.

  • Begin the traversal at the starting node (could be any node in the graph).
  • Mark the current node as visited so as not to visit again.
  • Recursively visit all the unvisited neighbors of the current node.
  • Once all neighbors are visited, backtrack to the previous node.

Code snippet:

def dfs(graph, node, visited=None):
    """
    Perform Depth-First Search (DFS) on the graph using recursion.
    :param graph: The graph represented as an adjacency list (dict of lists)
    :param node: The current node being explored
    :param visited: Set of visited nodes to avoid revisiting
    """
    if visited is None:
        visited = set()  # Initialize the visited set if it's not provided

    # Mark the node as visited
    visited.add(node)
    print(node, end=' ')  # Print the current node

    # Recursively visit all unvisited neighbors of the current node
    for neighbor in graph[node]:
        if neighbor not in visited:
            dfs(graph, neighbor, visited)

# Example usage:
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

# Perform DFS starting from node 'A'
dfs(graph, 'A')

What Is Dijkstra’s Algorithm and Its Applications?

Dijkstra's Algorithm locates the shortest path between nodes in a graph, particularly for weighted graphs where the edges have non-negative weights. It has applications in GPS navigation systems, routing algorithms, and network optimization.

Here’s how Dijikstra’s algorithm works.

  1. Set the distance to the source node as 0 and to all other nodes as infinity. Mark all nodes as unvisited.
  2. Pick the unvisited node with the smallest distance.
  3. Calculate the tentative distance through the selected node. If this tentative distance is shorter than the current shortest distance to the neighbor, update the neighbor's distance.
  4. Mark the selected node as visited.
  5. Repeat steps 2-4 until all nodes are visited or the destination node is reached.

Code snippet:

import heapq

def dijkstra(graph, start):
    """
    Implements Dijkstra's algorithm to find the shortest paths from the start node.
    : param graph: A dictionary where keys are nodes and values are dictionaries of neighbors and edge weights.
    :param start: The starting node for the shortest path calculation.
    :return: A dictionary of the shortest distance from the start node to every other node.
    """
    # Priority queue (min-heap) to select the node with the smallest tentative distance
    pq = [(0, start)]  # (distance, node)
    distances = {node: float('inf') for node in graph}  # Set all distances to infinity initially
    distances[start] = 0
    visited = set()  # To track visited nodes

    while pq:
        current_distance, current_node = heapq.heappop(pq)  # Get the node with the smallest distance

        if current_node in visited:
            continue  # Skip already visited nodes

        visited.add(current_node)

        # Explore the neighbors of the current node
        for neighbor, weight in graph[current_node].items():
            if neighbor in visited:
                continue  # Skip visited neighbors

            distance = current_distance + weight
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                heapq.heappush(pq, (distance, neighbor))

    return distances

# Example graph (Adjacency List representation)
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'A': 1, 'C': 2, 'D': 5},
    'C': {'A': 4, 'B': 2, 'D': 1},
    'D': {'B': 5, 'C': 1}
}

# Find the shortest paths from node 'A'
distances = dijkstra(graph, 'A')
print(distances)

Now that you have an idea about the different algorithms in Python, check out how dynamic programming can simplify problem-solving.

 

Also Read: Dijikstra’s Shortest Path Algorithm

How Does Dynamic Programming Simplify Complex Problems?

Dynamic programming solves simpler subproblems and stores the results to avoid redundant computations. It is suitable for optimization problems. 

Here are some examples of dynamic programming in Python.

What Are Examples of Dynamic Programming Problems?

Check the following examples of dynamic programming.

1. Fibonacci Sequence

The Fibonacci sequence calculates the nth Fibonacci number. Each term in a Fibonacci sequence is the sum of the two preceding ones, starting from 0 and 1. Dynamic programming can optimize the calculation of the Fibonacci sequence by storing already computed Fibonacci numbers and turning them into a linear time solution.

Code snippet:

def fibonacci(n):
    # Base case
    if n == 0: return 0
    if n == 1: return 1
    
    # Create a table to store results of subproblems
    fib = [0] * (n + 1)
    fib[0], fib[1] = 0, 1
    
    # Fill the table from 2 to n
    for i in range(2, n + 1):
        fib[i] = fib[i-1] + fib[i-2]
    
    return fib[n]

# Example usage:
print(fibonacci(10))  # Output: 55

2. Knapsack Problem

Given items with weights and values, the Knapsack problem involves selecting the maximum value that can be obtained by selecting items within a weight limit. Using dynamic programming, you can maximize the total value while considering the constraint.

Code snippet:

def knapsack(weights, values, capacity):
    n = len(weights)
    # Create a DP table to store maximum value at each weight
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    
    for i in range(1, n + 1):
        for w in range(capacity + 1):
            if weights[i-1] <= w:
                dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1])
            else:
                dp[i][w] = dp[i-1][w]
    
    return dp[n][capacity]

# Example usage:
weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
capacity = 5
print(knapsack(weights, values, capacity))  # Output: 7

3. Longest Common Subsequence (LCS)

The longest common Sequence (LCS) problem finds the longest character sequence that appears in the same order in two strings. In this case, dynamic programming avoids recalculating solutions for overlapping subproblems.

Code snippet:

def lcs(X, Y):
    m, n = len(X), len(Y)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if X[i-1] == Y[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    
    return dp[m][n]

# Example usage:
X = "ABCBDAB"
Y = "BDCAB"
print(lcs(X, Y))  # Output: 4

4. Matrix Chain Multiplication

The Matrix Chain Multiplication problem asks for the most efficient way to multiply a chain of matrices together. Dynamic programming breaks the problem into subproblems of multiplying smaller chains of matrices.

Code snippet:

def matrix_chain_order(p):
    n = len(p) - 1
    dp = [[0] * n for _ in range(n)]
    
    # l is the chain length
    for length in range(2, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            dp[i][j] = float('inf')
            for k in range(i, j):
                q = dp[i][k] + dp[k+1][j] + p[i]*p[k+1]*p[j+1]
                dp[i][j] = min(dp[i][j], q)
    
    return dp[0][n-1]

# Example usage:
p = [30, 35, 15, 5, 10, 20, 25]
print(matrix_chain_order(p))  # Output: 15125

5. Coin Change Problem

The Coin Change problem asks for the minimum number of coins to make a certain amount from a given set of denominations.

Code snippet:

def coin_change(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0  # Base case: 0 coins needed to make 0 amount
    
    for i in range(1, amount + 1):
        for coin in coins:
            if i - coin >= 0:
                dp[i] = min(dp[i], dp[i-coin] + 1)
    
    return dp[amount] if dp[amount] != float('inf') else -1

# Example usage:
coins = [1, 2, 5]
amount = 11
print(coin_change(coins, amount))  # Output: 3 (5 + 5 + 1)

6. Edit Distance (Levenshtein Distance)

The Edit Distance problem calculates the minimum number of operations (insertions, deletions, or substitutions) to convert one string into another. Dynamic programming ensures that overlapping subproblems are solved once and reused.

Code snippet:

def edit_distance(str1, str2):
    m, n = len(str1), len(str2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    for i in range(m + 1):
        for j in range(n + 1):
            if i == 0:
                dp[i][j] = j  # Insert all characters of str2
            elif j == 0:
                dp[i][j] = i  # Delete all characters of str1
            elif str1[i-1] == str2[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = 1 + min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])
    
    return dp[m][n]

# Example usage:
print(edit_distance("kitten", "sitting"))  # Output: 3

Are you curious to know about the greedy algorithms in Python? Explore the subsequent section.

What Are Greedy Algorithms, and Where Are They Used?

A greedy algorithms in Pythonmakes the optimal choice at each step, hoping to find an optimum solution. It's like making decisions without looking too far ahead, always choosing the immediate best option.

Here’s how the greedy algorithms in Python works.

  • Select the best immediate option.
  • Remove the chosen option and update the remaining problem.
  • Continue until a solution is found.

Greedy algorithms have applications in the following fields.

  • Network and Graph Algorithms: Finding the shortest path between two nodes in a weighted graph.  
  • Data Compression: Assigning variable-length codes to characters based on their frequency.  
  • Scheduling: Selecting the maximum number of non-overlapping activities from a set of activities.  
  • Resource Allocation: Determining the optimal selection of items to maximize value.  

Here’s an example of Huffman coding, which is a greedy algorithm.

How Does Huffman Coding Work?

Huffman coding is used for lossless data compression. It identifies the frequency of characters (or symbols) in a dataset and assigns shorter codes to more frequent characters and longer codes to less frequent ones. 

Here’s the working of Huffman coding.

  • Count the frequency of each character (or symbol) in the input data.
  • Create a min-heap where each node contains a symbol and its frequency.  
  • Build a Huffman tree using the frequency of characters. 
  • Traverse the Huffman Tree to assign binary codes to the symbols
  • Replace each symbol in the input data with its corresponding Huffman code.
  • Traverse the Huffman Tree following the bits in the encoded data for decoding.

Curious about how backtracking algorithms can crack constraint problems in Python? Scroll down to discover.

How Do Backtracking Algorithms Solve Constraint Problems?

 Backtracking is a recursive approach to solving problems involving constraint satisfaction. The core idea is to explore all possible solutions to a problem, systematically eliminating the infeasible ones and eventually selecting the optimal or valid solution. 

Here are the key characteristics of backtracking algorithms.

  • Recursive Exploration: The algorithm breaks down the problem into smaller subproblems.
  • Pruning: If a partial solution does not meet the problem's constraints, it will not be reconsidered. This removes unnecessary steps.
  • Decision Tree Traversal: Backtracking can be visualized as a decision tree. The algorithm explores each branch systematically before backtracking.
  • Feasible Solution: The algorithm generates potential solutions and checks whether they satisfy the given constraints.

Here’s a brief idea of how backtracking algorithms work.

  • Start from an initial state and try to extend it step by step.
  • Decide at each step, and check if the decision leads to a valid or feasible solution.
  • If the current solution is valid, proceed to the next step.
  • If the current solution violates constraints, backtrack to the previous step and try a different option.
  • Repeat this process until either a solution is found or all possibilities are exhausted.

Here are a few examples of the backtracking algorithms in Python.

What Are Examples of Backtracking Algorithms?

Check the following examples of the backtracking algorithms in Python.

1. N-Queens Problem

The N-Queens problem is to place N queens on an N x N chessboard such that no two queens threaten each other. A queen can attack another queen in the same row, column, or diagonal.

Code snippet:

def is_safe(board, row, col, n):
    # Check column
    for i in range(row):
        if board[i][col] == 1:
            return False
    
    # Check upper-left diagonal
    for i, j in zip(range(row-1, -1, -1), range(col-1, -1, -1)):
        if board[i][j] == 1:
            return False
    
    # Check upper-right diagonal
    for i, j in zip(range(row-1, -1, -1), range(col+1, n)):
        if board[i][j] == 1:
            return False
    
    return True

def solve_n_queens(board, row, n):
    if row >= n:  # All queens are placed
        return True
    
    for col in range(n):
        if is_safe(board, row, col, n):
            board[row][col] = 1  # Place queen
            
            if solve_n_queens(board, row + 1, n):
                return True
            
            board[row][col] = 0  # Backtrack (remove queen)
    
    return False  # No valid position found

def print_board(board):
    for row in board:
        print(" ".join(["Q" if x == 1 else "." for x in row]))

def n_queens(n):
    board = [[0 for _ in range(n)] for _ in range(n)]
    
    if solve_n_queens(board, 0, n):
        print_board(board)
    else:
        print("No solution exists.")

# Example usage:
n_queens(4)  # Solve 4-Queens problem

Output:

Q . . .
. . Q .
. Q . .
. . . Q

2. Sudoku Solver

The Sudoku problem requires you to fill a 9x9 grid with numbers from 1 to 9 so that every row, column, and 3x3 subgrid contains each number exactly once.

Code snippet:

def is_safe(board, row, col, num):
    # Check the row
    for x in range(9):
        if board[row][x] == num:
            return False
    
    # Check the column
    for x in range(9):
        if board[x][col] == num:
            return False
    
    # Check the 3x3 subgrid
    start_row, start_col = 3 * (row // 3), 3 * (col // 3)
    for i in range(3):
        for j in range(3):
            if board[i + start_row][j + start_col] == num:
                return False
    
    return True

def solve_sudoku(board):
    empty = find_empty_location(board)
    if not empty:
        return True  # Solution found
    
    row, col = empty
    
    for num in range(1, 10):
        if is_safe(board, row, col, num):
            board[row][col] = num
            
            if solve_sudoku(board):
                return True
            
            board[row][col] = 0  # Backtrack
    
    return False  # Trigger backtracking if no valid number

def find_empty_location(board):
    for i in range(9):
        for j in range(9):
            if board[i][j] == 0:
                return i, j
    return None  # No empty location found

def print_board(board):
    for row in board:
        print(" ".join(str(num) if num != 0 else '.' for num in row))

# Example usage with a partially filled Sudoku board
sudoku_board = [
    [5, 3, 0, 0, 7, 0, 0, 0, 0],
    [6, 0, 0, 1, 9, 5, 0, 0, 0],
    [0, 9, 8, 0, 0, 0, 0, 6, 0],
    [8, 0, 0, 0, 6, 0, 0, 0, 3],
    [4, 0, 0, 8, 0, 3, 0, 0, 1],
    [7, 0, 0, 0, 2, 0, 0, 0, 6],
    [0, 6, 0, 0, 0, 0, 2, 8, 0],
    [0, 0, 0, 4, 1, 9, 0, 0, 5],
    [0, 0, 0, 0, 8, 0, 0, 7, 9]
]

if solve_sudoku(sudoku_board):
    print_board(sudoku_board)
else:
    print("No solution exists.")

Output:

5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9

3. Subset Sum Problem

The subset sum problem involves finding a subset of numbers from a given set that adds up to a specific target sum. For instance, from an array of [10,5,6,9,3,4], you have to achieve the target digit 9. A subset [4,5] gives you the target digit.

Code snippet:

def subset_sum(nums, target, current=[], index=0):
    # If the current sum equals target, print the current subset
    if sum(current) == target:
        print(current)
        return True
    
    # If the current sum exceeds the target, return False
    if sum(current) > target:
        return False
    
    for i in range(index, len(nums)):
        current.append(nums[i])  # Include the number in the subset
        
        if subset_sum(nums, target, current, i + 1):  # Recur with the next number
            return True
        
        current.pop()  # Backtrack, exclude the number
    
    return False  # No valid subset found

# Example usage:
nums = [3, 34, 4, 12, 5, 2]
target = 9
if not subset_sum(nums, target):
    print("No subset found")

Output:

[4, 5]

Check out the essential libraries used for algorithm development.

 What Are the Key Libraries for Algorithm Development in Python?

Python offers several libraries that simplify the implementation of algorithms, making it easier to work with complex data structures, algorithms, and optimization techniques.

Here are the essential Python libraries that can help in algorithm development.

1. NumPy

NumPy provides high-performance arrays and matrices, making it suitable for algorithms related to mathematical computations, optimization, and data analysis. For instance, NumPy is useful for algorithms like Gaussian elimination.

Code snippet:

import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.dot(a, b)  # Dot product
print(result)

2. SciPy

SciPy is suitable for scientific and technical computing. It is used for optimization, signal processing, graph theory, and solving differential equations. For example, SciPy is suitable for graph algorithms.

Code snippet:

from scipy.optimize import minimize

def func(x):
    return x**2 + 5

result = minimize(func, 0)
print(result.x)  # Minimum value of the function

3. NetworkX

NetworkX is suitable for complex graphs and networks. It has easy-to-use tools for graph creation, manipulation, and various graph algorithms. NetworkX is perfect for graph algorithms like DFS, BFS, and Dijkstra.

Code snippet:

import networkx as nx
G = nx.Graph()
G.add_edges_from([(1, 2), (2, 3), (3, 4)])
path = nx.shortest_path(G, source=1, target=4)
print(path)  # Output: [1, 2, 3, 4]

4. SymPy

SymPy can simplify algebraic expressions, solve equations, perform symbolic differentiation and integration, and more. It can also implement algorithms in symbolic form.

Code snippet:

from sympy import symbols, Eq, solve

x = symbols('x')
equation = Eq(x**2 - 5, 0)
solution = solve(equation)
print(solution)  # Solve x^2 = 5

5. Pandas

Pandas is a powerful library for data manipulation and analysis. It is suitable for handling algorithms for data processing, machine learning, or optimization. For instance, the Pandas library can pre-process data before applying machine learning.

Code snippet:

import pandas as pd

data = {'A': [1, 2, 3], 'B': [4, 5, 6]}
df = pd.DataFrame(data)
print(df.mean())  # Compute the mean of each column

Now that you have gained valuable insights into different algorithms, let’s examine how Python can be suitable for algorithm development and implementation.

Discover the power of Python’s top libraries! Enroll in upGrad’s course to master NumPy, Pandas, and Matplotlib and take your skills to the next level.

Is Python Suitable for Algorithm Development and Implementation?

Python’s simple syntax, extensive standard library, and rich ecosystem provide a wide range of tools for data structures, algorithms, and numerical computations. However, Python's interpreted nature can lead to slower execution speeds compared to compiled languages like C++ or Java.

Here’s a brief idea about the strengths and weaknesses of Python for algorithm development.

Python’s Strengths for Algorithm Development

Python’s strengths in algorithm development can be broken down into the following points.

  • Simple syntax and readability

Python's syntax is straightforward, intuitive, and closely resembles English, which makes it easy to learn and write code. 

  • Large library support

Python offers a massive collection of standard libraries like Pandas and NumPy for different tasks, such as data manipulation, machine learning, and web development. 

  • Strong community support

Python has one of the largest and most active communities of developers, contributors, and learners. Examples include Python.org and Stack Overflow.

  • Cross-platform compatibility

Python is platform-independent. The same Python code can run without modification on different operating systems (Windows, macOS, Linux).

  • Built-in garbage collection

Python’s automatic memory management and garbage collection free up memory space when objects are no longer needed, reducing the risk of memory leaks. 

  • Rich ecosystem for AI and ML

Python is most appropriate for new technologies like Artificial Intelligence (AI) and Machine Learning (ML) due to powerful libraries like TensorFlow, Keras, PyTorch, Scikit-learn, and OpenCV.

  • Readable debugging tools

Python’s built-in debugging tools, such as pdb and the Python Debugger, make it easy to troubleshoot and fix issues in algorithm implementation.

  • Dynamic typing

Python’s variables need not be declared with a specific data type, which allows for more flexibility and concise code. 

Python’s Limitations for Algorithm Development

Here are some limitations of using Python for algorithm development.

  • Slower execution speed

Python is an interpreted language, which means it executes slower than compiled languages like C. For example, sorting large datasets can be slower in Python. 

  • Higher memory usage

Python’s dynamic data types, high-level abstractions, and garbage collection can increase memory usage. For example, handling large graphs is less memory-efficient in Python compared to C++.

  • Limited for low-level programming

Python is unsuitable for low-level system programming tasks, such as writing operating systems or real-time embedded systems. For example, developing a real-time control system for hardware. 

  • Global Interpreter Lock (GIL)

The Global Interpreter Lock (GIL) mechanism ensures that only one thread executes Python bytecode at a time, limiting the parallel execution of multi-threaded Python programs. 

  • Not ideal for real-time systems

Python's slower execution time makes it unsuitable for real-time systems. For example, Python-based controlling machinery in an industrial setting is not feasible.

  • Dependency on external libraries

Python relies on external libraries, such as NumPy or SciPy, to achieve performance. This raises issues like version compatibility. 

  • Lack of native optimization

Python’s absence of built-in low-level optimizations can limit the performance of certain algorithms. For example, Python cannot handle algorithms for ray tracing in graphics programming.

Also Read: Top 10 Reasons Why Python is Popular With Developers 2025

Conclusion

Python's popularity and widespread adoption in algorithm development have grown significantly in recent years. The TIOBE index ranks Python as the most popular programming language in the world.

Python’s specialized applications in fields such as Machine Learning, Artificial Intelligence, Data Science, and Web development make it a go-to programming language for developing algorithms.

To pursue a career in Python programming, you need to acquire specialized skills. upGrad's free Python courses equip you with industry-specific skills, preparing you for a successful career in Python programming.

Here are some popular courses offered by upGrad in Python programming.

Do you need help deciding which course to take to become a Python programmer? Contact upGrad for personalized counseling and valuable insights.

Unlock the power of data with our popular Data Science courses, designed to make you proficient in analytics, machine learning, and big data!

Elevate your career by learning essential Data Science skills such as statistical modeling, big data processing, predictive analytics, and SQL!

Stay informed and inspired with our popular Data Science articles, offering expert insights, trends, and practical tips for aspiring data professionals!

References

  1. https://www.techrepublic.com/article/tiobe-index-language-rankings/
  2. https://psico-smart.com/en/blogs/blog-how-can-organizations-leverage-data-analytics-to-enhance-decisionmaking-and-improve-performance-140055#:~:text=The%20Role%20of%20Big%20Data%20in%20Organizational%20Performance,-In%20the%20digital&text=For%20instance%2C%20a%20study%20by,profitability%20compared%20to%20their%20competitors.

Frequently Asked Question (FAQs)

What is an algorithm?

What is the fastest way to learn algorithm in Python?

Which type of algorithm in Python is the easiest?

What is the slowest algorithm in Python?

Which algorithm in Python is best for sorting?

Which algorithm in Python is best for searching?

Which sorting algorithm is best in Python?

What are the different search algorithms?

What is the difference between searching and sorting?

Which search algorithm is used in Google?

Which algorithm is the most used by a computer?

Rohit Sharma

759 articles published

Get Free Consultation

+91

By submitting, I accept the T&C and
Privacy Policy

Start Your Career in Data Science Today

Top Resources

Recommended Programs

upGrad Logo

Certification

3 Months

Liverpool John Moores University Logo
bestseller

Liverpool John Moores University

MS in Data Science

Dual Credentials

Master's Degree

17 Months

IIIT Bangalore logo
bestseller

The International Institute of Information Technology, Bangalore

Executive Diploma in Data Science & AI

Placement Assistance

Executive PG Program

12 Months