Divide and Conquer Technique in Algorithms

Shraddha Rao
5 min readJul 22, 2024

--

Proceed only if you are here because:

  • You have started learning coding patterns.
  • You possess a fundamental knowledge of a programming language.
  • You believe in consistency and are committed to problem-solving.
  • You remember to practice pseudocoding while tackling problems by:
    Reading the problem statement thoroughly.
    — Writing and comprehending the coding technique.
    — Identifying the data types involved.
    — Determining the expected output data types.
    — Writing the initial solution.
    — Optimizing the solution.
    — Testing the solution against different test cases.

Getting Back to the Concept:

Divide and Conquer is a powerful algorithmic paradigm that works by breaking down a problem into smaller sub-problems, solving each sub-problem independently, and then combining their solutions to solve the original problem. This approach is efficient and often leads to recursive algorithms that are easier to understand and implement.

What is Divide and Conquer?

  1. Divide: Break the problem into smaller sub-problems of the same type.
  2. Conquer: Solve the sub-problems recursively. If they are small enough, solve them directly.
  3. Combine: Combine the solutions of the sub-problems to get the solution to the original problem.

Applications of Divide and Conquer:

  1. Sorting: Algorithms like Merge Sort and Quick Sort use divide and conquer.
  2. Searching: Binary Search is a classic example of this technique.
  3. Multiplication: Algorithms like Karatsuba for fast multiplication.
  4. Dynamic Programming: Many DP problems use divide and conquer to build solutions.

Example Problems and Walkthroughs — Variations of Divide and Conquer Technique

1. Merge Sort

Merge Sort is a stable, comparison-based sorting algorithm that uses the divide-and-conquer approach. It divides the array into halves, recursively sorts them, and then merges the sorted halves.

Merge Sort Approach:

  1. Divide the array into two halves.
  2. Recursively sort each half.
  3. Merge the two sorted halves to produce the sorted array.

Walkthrough:

  1. Divide the array into two halves until each sub-array contains a single element.
  2. Merge each pair of sub-arrays to form a sorted array.
  3. Repeat the merging process until a single sorted array is obtained.
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]

merge_sort(left_half)
merge_sort(right_half)

i = j = k = 0

while i < len(left_half) and j < len(right_half):
if left_half[i] < right_half[j]:
arr[k] = left_half[i]
i += 1
else:
arr[k] = right_half[j]
j += 1
k += 1

while i < len(left_half):
arr[k] = left_half[i]
i += 1
k += 1

while j < len(right_half):
arr[k] = right_half[j]
j += 1
k += 1
return arr

arr = [38, 27, 43, 3, 9, 82, 10]
print(merge_sort(arr)) # Output: [3, 9, 10, 27, 38, 43, 82]

2. Binary Search

Binary Search is an efficient algorithm for finding an item from a sorted list of items. It works by repeatedly dividing the search interval in half.

Binary Search Approach:

  1. Compare the target value to the middle element of the array.
  2. If the target value is equal to the middle element, return the index.
  3. If the target value is less than the middle element, repeat the search on the left half of the array.
  4. If the target value is greater than the middle element, repeat the search on the right half of the array.

Walkthrough:
1. Initialize the left and right pointers to the start and end of the array.
2. Calculate the mid-point and compare it with the target value.
3. If the target is found, return the index.
4. If the target is less than the mid-point, adjust the right pointer.
5. If the target is greater than the mid-point, adjust the left pointer.
6. Repeat until the target is found or the search interval is empty.

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

arr = [2, 3, 4, 10, 40]
target = 10
print(binary_search(arr, target)) # Output: 3

Maximum Subarray Sum (Kadane’s Algorithm)

Given an array of integers, find the contiguous subarray with the maximum sum. This problem can be solved efficiently using a divide-and-conquer approach.

Divide and Conquer Approach:
1. Divide the array into two halves.
2. Recursively find the maximum subarray sum for each half.
3. Calculate the maximum subarray sum that crosses the midpoint.
4. Combine the results from the left half, right half, and the cross sum.
5. Return the maximum of these three values.

def max_crossing_sum(arr, left, mid, right):
left_sum = float('-inf')
right_sum = float('-inf')
sum = 0

for i in range(mid, left - 1, -1):
sum += arr[i]
if sum > left_sum:
left_sum = sum

sum = 0
for i in range(mid + 1, right + 1):
sum += arr[i]
if sum > right_sum:
right_sum = sum

return left_sum + right_sum

def max_subarray_sum(arr, left, right):
if left == right:
return arr[left]

mid = (left + right) // 2

left_sum = max_subarray_sum(arr, left, mid)
right_sum = max_subarray_sum(arr, mid + 1, right)
cross_sum = max_crossing_sum(arr, left, mid, right)

return max(left_sum, right_sum, cross_sum)

arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
print(max_subarray_sum(arr, 0, len(arr) - 1)) # Output: 6 (subarray: [4, -1, 2, 1])

Why Use Divide and Conquer?

  1. Efficiency: Breaks down problems into manageable parts, often leading to more efficient solutions.
  2. Simplicity: Easier to conceptualize and implement recursive solutions.
  3. Scalability: Can handle large datasets by solving smaller sub-problems independently.

When to Avoid Divide and Conquer?

  1. Overhead: Recursive function calls can add overhead, impacting performance for small datasets.
  2. Complexity: May introduce additional complexity in terms of understanding and implementation.
  3. Memory Usage: Recursive approaches can lead to increased memory usage due to the call stack.

POV —

Stay consistent, practice regularly, and you’ll see improvement in your problem-solving skills. Remember, the key to mastering coding patterns is practice and understanding. Keep coding!

Had fun? For more coding pattern concepts, follow the link — [Follow this trick to learn Data Structures & Algorithms](https://medium.com/@shraddharao_/follow-this-trick-to-learn-data-structures-algorithms-5dc3ded0933f)

--

--