Get Data For Me
Courses

Introduction to the Essential Data Structures & Algorithms

Marcell Ziemann
#tutorial#python#data-structures

Introduction to Essential Data Structures & Algorithms

Key Data Structures

  1. Arrays

    • Description: Fixed-size, sequential collection of elements of the same type.
    • Usage: Suitable for scenarios where quick access to elements is needed.
    • Operations: Access (O(1)), Insertion (O(n)), Deletion (O(n)).
    • Example:
      array = [1, 2, 3, 4, 5]
      print(array[2])  # Access element at index 2
      
  2. Linked Lists

    • Description: A collection of nodes where each node contains a value and a reference to the next node.
    • Usage: Efficient for insertion and deletion, less efficient for access.
    • Operations: Access (O(n)), Insertion (O(1)), Deletion (O(1)).
    • Example:
      class Node:
          def __init__(self, value):
              self.value = value
              self.next = None
      
      head = Node(1)
      head.next = Node(2)
      
  3. Stacks

    • Description: Last-In-First-Out (LIFO) structure.
    • Usage: Used for problems involving recursion, function calls.
    • Operations: Push (O(1)), Pop (O(1)).
    • Example:
      stack = []
      stack.append(1)  # Push
      print(stack.pop())  # Pop
      
  4. Queues

    • Description: First-In-First-Out (FIFO) structure.
    • Usage: Useful in scenarios like scheduling tasks, managing resources.
    • Operations: Enqueue (O(1)), Dequeue (O(1)).
    • Example:
      from collections import deque
      queue = deque()
      queue.append(1)  # Enqueue
      print(queue.popleft())  # Dequeue
      
  5. Hash Tables

    • Description: Key-value pair structure, offering fast access.
    • Usage: Ideal for scenarios needing quick lookups.
    • Operations: Access (O(1)), Insertion (O(1)), Deletion (O(1)).
    • Example:
      hash_table = {}
      hash_table['key'] = 'value'
      print(hash_table['key'])  # Access by key
      
  6. Trees

    • Description: Hierarchical structure with a root node and children.
    • Usage: Used in scenarios like hierarchical data representation, searching, and sorting.
    • Operations: Depends on tree type (e.g., BST: Search O(log n)).
    • Example:
      class TreeNode:
          def __init__(self, value):
              self.value = value
              self.left = None
              self.right = None
      
      root = TreeNode(1)
      root.left = TreeNode(2)
      root.right = TreeNode(3)
      

Key Algorithms

  1. Sorting Algorithms

    • Bubble Sort:

      • Description: Repeatedly swaps adjacent elements if they are in the wrong order.
      • Complexity: O(n^2)
      • Example:
        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]
        
        array = [64, 34, 25, 12, 22, 11, 90]
        bubble_sort(array)
        print(array)
        
    • Quick Sort:

      • Description: Divides and conquers by partitioning the array.
      • Complexity: O(n log n)
      • Example:
        def quick_sort(arr):
            if len(arr) <= 1:
                return arr
            pivot = arr[len(arr) // 2]
            left = [x for x in arr if x < pivot]
            middle = [x for x in arr if x == pivot]
            right = [x for x in arr if x > pivot]
            return quick_sort(left) + middle + quick_sort(right)
        
        array = [3, 6, 8, 10, 1, 2, 1]
        print(quick_sort(array))
        
  2. Searching Algorithms

    • Linear Search:

      • Description: Checks each element sequentially.
      • Complexity: O(n)
      • Example:
        def linear_search(arr, target):
            for i, value in enumerate(arr):
                if value == target:
                    return i
            return -1
        
        array = [1, 3, 5, 7, 9]
        print(linear_search(array, 5))  # Output: 2
        
    • Binary Search:

      • Description: Efficiently finds an item in a sorted list by repeatedly dividing the search interval.
      • Complexity: O(log n)
      • Example:
        def binary_search(arr, target):
            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
        
        array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        print(binary_search(array, 4))  # Output: 3
        
  3. Graph Algorithms

    • Depth-First Search (DFS):

      • Description: Explores as far as possible along each branch before backtracking.
      • Complexity: O(V + E)
      • Example:
        def dfs(graph, start, visited=None):
            if visited is None:
                visited = set()
            visited.add(start)
            for next_node in graph[start] - visited:
                dfs(graph, next_node, visited)
            return visited
        
        graph = {'A': {'B', 'C'},
                 'B': {'A', 'D', 'E'},
                 'C': {'A', 'F'},
                 'D': {'B'},
                 'E': {'B', 'F'},
                 'F': {'C', 'E'}}
        print(dfs(graph, 'A'))
        
    • Breadth-First Search (BFS):

      • Description: Explores all the nodes at the present depth level before moving on to nodes at the next depth level.
      • Complexity: O(V + E)
      • Example:
        def bfs(graph, start):
            visited, queue = set(), [start]
            while queue:
                vertex = queue.pop(0)
                if vertex not in visited:
                    visited.add(vertex)
                    queue.extend(graph[vertex] - visited)
            return visited
        
        graph = {'A': {'B', 'C'},
                 'B': {'A', 'D', 'E'},
                 'C': {'A', 'F'},
                 'D': {'B'},
                 'E': {'B', 'F'},
                 'F': {'C', 'E'}}
        print(bfs(graph, 'A'))
        

Conclusion

Understanding and implementing these basic data structures and algorithms are essential steps towards solving complex computational problems. By mastering these fundamentals, one can efficiently manage data, optimize performance, and develop robust applications.

← Back to Blog