Introduction to Data Structures & Algorithms

Data structures are ways of organizing and storing data to enable efficient access and modification. Algorithms are step-by-step procedures for solving problems.

Why Learn Data Structures & Algorithms?

  • Problem Solving: Essential for solving complex programming problems
  • Efficiency: Write more efficient and optimized code
  • Interviews: Core topic in technical interviews
  • Foundation: Building blocks for advanced computer science concepts

Prerequisites

  • Basic programming knowledge in any language (Python, Java, C++)
  • Understanding of variables, loops, and functions
  • Basic mathematical concepts

Arrays

Arrays are collections of elements stored in contiguous memory locations. They provide O(1) access time to elements using indices.

Array Operations

# Python Array Examples
# Creating an array
arr = [1, 2, 3, 4, 5]

# Accessing elements
first_element = arr[0]  # O(1)
last_element = arr[-1]  # O(1)

# Insertion
arr.append(6)           # O(1) at end
arr.insert(0, 0)        # O(n) at beginning

# Deletion
arr.pop()               # O(1) from end
arr.pop(0)              # O(n) from beginning

# Searching
index = arr.index(3)    # O(n) linear search

Common Array Problems

Problem: Two Sum

Given an array of integers and a target sum, find two numbers that add up to the target.

def two_sum(nums, target):
    seen = {}
    for i, num in enumerate(nums):
        complement = target - num
        if complement in seen:
            return [seen[complement], i]
        seen[num] = i
    return []

# Example usage
nums = [2, 7, 11, 15]
target = 9
result = two_sum(nums, target)  # Returns [0, 1]

Linked Lists

Linked lists are linear data structures where elements are stored in nodes, and each node contains data and a reference to the next node.

Types of Linked Lists

  • Singly Linked List: Each node points to the next node
  • Doubly Linked List: Each node has pointers to both next and previous nodes
  • Circular Linked List: Last node points back to the first node

Implementation

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class LinkedList:
    def __init__(self):
        self.head = None
    
    def insert_at_beginning(self, val):
        new_node = ListNode(val)
        new_node.next = self.head
        self.head = new_node
    
    def delete_node(self, val):
        if not self.head:
            return
        
        if self.head.val == val:
            self.head = self.head.next
            return
        
        current = self.head
        while current.next:
            if current.next.val == val:
                current.next = current.next.next
                return
            current = current.next

Stacks & Queues

Stack (LIFO - Last In, First Out)

Elements are added and removed from the same end (top).

class Stack:
    def __init__(self):
        self.items = []
    
    def push(self, item):
        self.items.append(item)
    
    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        return None
    
    def peek(self):
        if not self.is_empty():
            return self.items[-1]
        return None
    
    def is_empty(self):
        return len(self.items) == 0

Queue (FIFO - First In, First Out)

Elements are added at rear and removed from front.

from collections import deque

class Queue:
    def __init__(self):
        self.items = deque()
    
    def enqueue(self, item):
        self.items.append(item)
    
    def dequeue(self):
        if not self.is_empty():
            return self.items.popleft()
        return None
    
    def front(self):
        if not self.is_empty():
            return self.items[0]
        return None
    
    def is_empty(self):
        return len(self.items) == 0

Trees

Trees are hierarchical data structures with a root node and child nodes. Each node can have multiple children but only one parent.

Binary Tree Implementation

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class BinaryTree:
    def __init__(self):
        self.root = None
    
    def inorder_traversal(self, node):
        """Left -> Root -> Right"""
        if node:
            self.inorder_traversal(node.left)
            print(node.val, end=" ")
            self.inorder_traversal(node.right)
    
    def preorder_traversal(self, node):
        """Root -> Left -> Right"""
        if node:
            print(node.val, end=" ")
            self.preorder_traversal(node.left)
            self.preorder_traversal(node.right)
    
    def postorder_traversal(self, node):
        """Left -> Right -> Root"""
        if node:
            self.postorder_traversal(node.left)
            self.postorder_traversal(node.right)
            print(node.val, end=" ")

Binary Search Tree (BST)

A special type of binary tree where left subtree contains values less than root, and right subtree contains values greater than root.

Sorting Algorithms

Bubble Sort - O(n²)

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

Quick Sort - O(n log n)

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)

Time & Space Complexity

Big O notation describes the upper bound of algorithm performance in terms of time and space.

Common Time Complexities

Complexity Name Example Description
O(1) Constant Array access Same time regardless of input size
O(log n) Logarithmic Binary search Time increases logarithmically
O(n) Linear Linear search Time increases linearly
O(n log n) Linearithmic Merge sort Efficient sorting algorithms
O(n²) Quadratic Bubble sort Time increases quadratically

Practice Problems

Beginner Problems
  • Reverse an array
  • Find maximum element
  • Check for palindrome
  • Implement stack using array
Advanced Problems
  • Longest Common Subsequence
  • Graph traversal (BFS/DFS)
  • Dynamic programming problems
  • Tree balancing algorithms

Ready to Master DSA?

Take your learning to the next level with our structured Data Structures & Algorithms course featuring mentorship, coding practice, and interview preparation.

Enroll in Course Back to Tutorials