Back to Data Structures & Algorithms
2026-02-105 min read

Greedy Algorithm (Data Structures & Algorithms)

Learn Greedy Algorithm (Data Structures & Algorithms) step by step with clear examples and exercises.

Why This Matters

Greedy algorithms are essential tools in computer science for solving optimization problems, especially those with locally optimal choices at each step. They are widely used in various fields such as computer science, operations research, economics, and bioinformatics. Understanding Greedy Algorithms is crucial for acing technical interviews, solving real-world programming challenges, and debugging complex problems.

Prerequisites

To follow this guide, you should have a good understanding of the following:

  1. Basic Python syntax and data structures (lists, dictionaries)
  2. Control flow statements (if-else, for loops, while loops)
  3. Concepts of algorithms and complexity analysis
  4. Understanding of sorting algorithms (e.g., bubble sort, quicksort, mergesort)
  5. Basic knowledge of heap data structures

Core Concept

A greedy algorithm makes the locally optimal choice at each stage with the hope that the solution will be optimal globally. In other words, it always selects the best immediate option without looking ahead. The key property of a problem for which a greedy algorithm works is the _Optimality Property_: if a solution is partially constructed and some choice must be made to continue construction, then making the locally optimal choice at that moment will result in a globally optimal or near-optimal solution when the entire process is complete.

Example: Huffman Coding

Huffman coding is a popular lossless data compression algorithm that uses a greedy approach. The idea is to assign shorter codes (bits) to more frequent characters, resulting in an overall reduced file size. Here's a simplified example of how it works:

  1. Create a priority queue with nodes representing each character and their frequencies. Each node has two attributes: character and frequency.
  2. Remove the two nodes with the lowest frequencies from the queue and combine them into a new internal node, making this new node the parent of the two original nodes. The new node's frequency is the sum of the frequencies of its children.
  3. Add the new internal node back to the priority queue.
  4. Repeat steps 2 and 3 until only one node remains (the root).
  5. Assign codes to characters by traversing the tree from the root, choosing the left child for zeroes and the right child for ones.

Worked Example

Let's implement a simple Greedy Algorithm example: finding the maximum sum of contiguous subarray in an array. This problem is also known as Maximum Subarray Problem or Kadane's Algorithm.

def max_subarray(arr):
max_so_far = arr[0]
current_max = arr[0]

for i in range(1, len(arr)):
current_max = max(arr[i], current_max + arr[i])
max_so_far = max(current_max, max_so_far)

return max_so_far

In this example, we maintain two variables: max_so_far, which keeps track of the maximum sum found so far, and current_max, which represents the current maximum sum ending at the current index. At each step, we compare the current maximum sum with the sum obtained by adding the current element to the previous sum (current_max + arr[i]). We update current_max and max_so_far accordingly.

Sorting Algorithm Example: Activity Selection

Greedy algorithms can also be used in sorting problems, such as the Activity Selection Problem. The problem is to select a maximum number of activities from a list of activities without any overlaps. Here's a Python implementation using a greedy approach:

def max_activities(activities):
activities.sort(key=lambda x: x[1])
selected_activities = []
current_time = -float('inf')

for activity in activities:
start, end = activity
if start >= current_time:
selected_activities.append(activity)
current_time = end

return len(selected_activities)

In this example, we first sort the list of activities by their ending times. We then iterate through the sorted list and select each activity if its start time is greater than or equal to the current time. The current time is updated to the end time of the selected activity at each step.

Common Mistakes

  1. Not initializing max_so_far correctly: If not initialized properly, the algorithm may not find the correct maximum sum. Always initialize max_so_far to the first element of the array or a small negative value (like -float('inf')).
  2. Not considering the empty subarray: The algorithm should handle the case when the input array is empty or contains only zeros, in which case the maximum sum is considered as zero.
  3. Not handling negative numbers correctly: When dealing with negative numbers, the greedy approach may not always yield the correct result. In such cases, consider modifying the algorithm to handle both positive and negative numbers.
  4. Ignoring the Optimality Property: If a problem does not have the Optimality Property, a greedy algorithm may not provide an optimal solution or may even fail to produce any solution at all. Always verify whether a problem has the Optimality Property before applying a greedy approach.
  5. Not considering all possible choices: In some cases, a greedy algorithm may overlook better solutions by only considering locally optimal choices. It is essential to ensure that all possible choices are considered during each step of the algorithm.

Practice Questions

  1. Implement a Greedy Algorithm for the Knapsack problem (0/1 Knapsack).
  2. Write a Python function to find the minimum number of coins required to make change for an amount using a greedy approach.
  3. Implement a Greedy Algorithm for the Activity Selection Problem with overlapping activities.
  4. Implement a Greedy Algorithm for the Job Scheduling problem (non-preemptive scheduling).
  5. Implement a Greedy Algorithm for the Minimum Spanning Tree problem using Prim's algorithm.

FAQ

What is the time complexity of Kadane's Algorithm?

Kadane's Algorithm has a linear time complexity, O(n), where n is the length of the input array.

Can Greedy Algorithms always find optimal solutions?

No, not all optimization problems can be solved using greedy algorithms. Some problems require dynamic programming or other approaches to guarantee an optimal solution. However, many real-world problems can be efficiently solved using a greedy approach.

What is the difference between Greedy Algorithms and Dynamic Programming?

Greedy algorithms make locally optimal choices at each step with the hope that the solution will be globally optimal or near-optimal. In contrast, dynamic programming breaks down a problem into smaller subproblems and solves them optimally using memoization or tabulation techniques. While some problems can be solved using either approach, there are also problems that require one method over the other.

What is the difference between Greedy Algorithms and Brute Force?

Greedy algorithms make locally optimal choices at each step to find an approximate solution, while brute force tries every possible combination of solutions until the optimal or best solution is found. Greedy algorithms are generally more efficient than brute force for large input sizes but may not always provide the optimal solution.

Greedy Algorithm (Data Structures & Algorithms) | Data Structures & Algorithms | XQA Learn