ScholarQuill logoScholarQuillUniversity Notes
My Semester
Explore Notes
Past Papers12 New
Video CoursesNew
Search
Settings
Study Tools
GPA/CGPA Calculator
Schedule & To-Do
Student Portal
Contact
Contact
ScholarQuill logo
My Semester
Explore Notes
Past Papers12 New
Video CoursesNew
Search
Settings
GPA/CGPA Calculator
Schedule & To-Do
Student Portal
Contact
ScholarQuill logoScholarQuillUniversity Notes
Study PortalDC-321Divide And Conquer App…
Ctrl + K
Login
Explore
Search
Semester
Papers
CGPA
    DC-321

    Design & Analysis of Algorithms

    Progress0 / 28 done
    Topics28
    1Introduction2Role of Algorithms in Computing3Analysis on Nature of Input and Size of Input4Asymptotic Notations5Big-O Notation6Big-Ω Notation7Big-Θ Notation8Little-o Notation9Little-ω Notation10Sorting Algorithm Analysis11Loop Invariants12Recursion and Recurrence Relations13Algorithm Design Techniques14Brute Force Approach15Divide-and-Conquer Approach16Merge Sort17Quick Sort18Greedy Approach19Dynamic Programming20Elements of Dynamic Programming21Search Trees22Heaps23Hashing24Graph Algorithms25Shortest Paths26Sparse Graphs27String Matching28Introduction to Complexity Classes
    8 min read
    Topic 15 of 28
    Topic Study Note Illustration
    15

    Divide-and-Conquer Approach

    The Divide-and-Conquer (D&C) approach is a fundamental algorithm design paradigm where a problem is broken down into smaller subproblems, solved independently, and then combined to produce the...

    Divide-and-Conquer Approach

    The Divide-and-Conquer (D&C) approach is a fundamental algorithm design paradigm where a problem is broken down into smaller subproblems, solved independently, and then combined to produce the solution to the original problem. This technique is especially useful for problems that can be recursively divided into smaller subproblems, which can be solved more easily and then combined.

    Divide-and-conquer is commonly used in many well-known algorithms like Merge Sort, Quick Sort, and Binary Search.


    Key Steps of the Divide-and-Conquer Approach:

    1. Divide: Split the problem into smaller subproblems. The subproblems should be smaller instances of the same problem (recursively defined).
    2. Conquer: Solve each of the subproblems independently. If the subproblem is small enough, solve it directly. Otherwise, recursively apply the divide-and-conquer technique to break it down further.
    3. Combine: Merge or combine the solutions of the subproblems to form the final solution to the original problem.

    General Structure of Divide-and-Conquer Algorithms:

    1. Recursion: Divide the problem into smaller instances and solve them recursively.
    2. Combination of Results: After solving the subproblems, combine the results in an efficient manner to obtain the final solution.

    Example 1: Merge Sort

    Merge Sort is a classic example of the divide-and-conquer paradigm. The algorithm divides an array into two halves, recursively sorts each half, and then merges the sorted halves to get the final sorted array.

    Steps:

    1. Divide: Split the array into two halves.
    2. Conquer: Recursively sort each half.
    3. Combine: Merge the two sorted halves to produce a fully sorted array.

    Algorithm (Merge Sort):

    def merge_sort(arr):
        if len(arr) <= 1:
            return arr
        
        # Divide: Find the middle point and divide the array into two halves
        mid = len(arr) // 2
        left = merge_sort(arr[:mid])  # Recursively sort the left half
        right = merge_sort(arr[mid:])  # Recursively sort the right half
        
        # Combine: Merge the sorted halves
        return merge(left, right)
    
    def merge(left, right):
        sorted_arr = []
        i = j = 0
        
        # Merge two sorted arrays
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                sorted_arr.append(left[i])
                i += 1
            else:
                sorted_arr.append(right[j])
                j += 1
        
        # Append remaining elements (if any)
        sorted_arr.extend(left[i:])
        sorted_arr.extend(right[j:])
        
        return sorted_arr
    

    Time Complexity:

    • Divide step takes O(log⁡n)O(\log n)O(logn) as we divide the array in half repeatedly.
    • Conquer step involves recursively sorting each subarray, and this contributes to a total time complexity of O(nlog⁡n)O(n \log n)O(nlogn).
    • Combine step takes O(n)O(n)O(n) to merge two sorted arrays.

    So, the overall time complexity of Merge Sort is O(n log n).


    Example 2: Quick Sort

    Quick Sort is another well-known algorithm that uses the divide-and-conquer strategy. It picks a pivot element from the array, partitions the array into two subarrays (those smaller and greater than the pivot), and then recursively sorts the subarrays.

    Steps:

    1. Divide: Choose a pivot element and partition the array such that elements smaller than the pivot are on one side, and elements greater than the pivot are on the other side.
    2. Conquer: Recursively sort the subarrays on either side of the pivot.
    3. Combine: Since the array is being sorted in place, no explicit combination is needed. The subarrays will be sorted in place.

    Algorithm (Quick Sort):

    def quick_sort(arr):
        if len(arr) <= 1:
            return arr
        
        pivot = arr[len(arr) // 2]  # Choose the middle element as pivot
        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]
        
        # Recursively sort the left and right subarrays
        return quick_sort(left) + middle + quick_sort(right)
    

    Time Complexity:

    • The time complexity depends on how well the pivot divides the array. In the best and average case, the pivot splits the array into two roughly equal halves, leading to a time complexity of O(n log n).
    • In the worst case, when the pivot does not divide the array evenly (e.g., when the array is already sorted), the time complexity becomes O(n²).

    Example 3: Binary Search

    Binary Search is a simple and efficient divide-and-conquer algorithm used to search for an element in a sorted array. The algorithm works by repeatedly dividing the search interval in half.

    Steps:

    1. Divide: Calculate the middle index of the current search interval.
    2. Conquer: If the middle element is equal to the target, return the index. If the target is smaller than the middle element, search the left half; otherwise, search the right half.
    3. Combine: Since the result is found immediately, no combining is necessary.

    Algorithm (Binary Search):

    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  # Target not found
    

    Time Complexity:

    • Divide step reduces the problem size by half in each iteration.
    • The search is complete when the size of the search interval is reduced to 1.
    • Time complexity is O(log⁡n)O(\log n)O(logn) for both the best, average, and worst case, as the array is halved at each step.

    Example 4: Strassen's Matrix Multiplication

    Strassen's Matrix Multiplication is a divide-and-conquer algorithm for matrix multiplication that reduces the number of multiplications required.

    Instead of performing standard matrix multiplication, Strassen divides each matrix into smaller submatrices, recursively multiplies them, and combines the results.

    Steps:

    1. Divide: Divide the matrices into smaller submatrices.
    2. Conquer: Perform matrix multiplications on these smaller submatrices.
    3. Combine: Combine the results to obtain the final product.

    Time Complexity:

    • The standard matrix multiplication takes O(n3)O(n^3)O(n3), but Strassen's algorithm reduces it to O(nlog⁡27)≈O(n2.81)O(n^{\log_2 7}) \approx O(n^{2.81})O(nlog2​7)≈O(n2.81), which is more efficient for large matrices.

    Advantages of Divide-and-Conquer:

    1. Efficiency: It often leads to algorithms that have better time complexities compared to simpler techniques like brute force.
    2. Parallelism: Since subproblems are independent, divide-and-conquer algorithms can be parallelized, making them suitable for distributed computing.
    3. Optimal Substructure: Problems that can be divided into smaller subproblems often have an optimal substructure, making divide-and-conquer a natural fit for these kinds of problems.

    Disadvantages of Divide-and-Conquer:

    1. Recursive Overhead: Recursive calls introduce overhead, such as additional memory usage for the call stack.
    2. Complexity: Some divide-and-conquer algorithms, like Strassen’s matrix multiplication, can be difficult to implement and understand.
    3. Suboptimal for Certain Problems: Not all problems can be divided into smaller subproblems efficiently. Some problems may not benefit from divide-and-conquer and may require other approaches.

    Summary

    The Divide-and-Conquer approach is a powerful technique used to solve problems by recursively breaking them into smaller, more manageable subproblems, solving each subproblem, and then combining the results. This technique is widely used in algorithms like Merge Sort, Quick Sort, and Binary Search. While it often results in efficient solutions, it can also introduce recursive overhead and complexity in some cases. Divide-and-conquer is particularly useful when the problem can be broken down into independent subproblems, and the subproblems are easier to solve than the original problem.

    ← Previous topic 14
    Brute Force Approach
    Next topic 16 →
    Merge Sort

    Past Papers

    Open this section to view and download past papers

    Click on Show Past Papers to load available exam papers.

    Reading Progress
    0%
    8mremaining
    1.3kwords
    Intermediatelevel
    On This Page

    No headings found

    Study Toolkit

    Subject Portal

    Design & Analysis of Algorithms