Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Divide and Conquer

What is Divide and Conquer?

Divide and Conquer is an algorithm design paradigm that works by recursively breaking a problem into smaller subproblems of the same type, solving each independently, then combining their solutions.

  • Divide — split the problem into 2 or more smaller subproblems
  • Conquer — solve each subproblem recursively (base case: solve directly)
  • Combine — merge the subproblem solutions into the final answer
AlgorithmDivideConquerCombineComplexity
Binary SearchSplit array in halfSearch one halfNoneO(log n)
Merge SortSplit into halvesSort each halfMerge sorted halvesO(n log n)
Quick SortPartition around pivotSort each partitionNone (in-place)O(n log n) avg
Strassen's MatrixSplit matrices7 recursive multiplicationsAdd/subtractO(n^2.81)
Merge Sort — Classic Divide and Conquer
import java.util.Arrays;

public class MergeSort {

    // DIVIDE: split array into two halves
    // CONQUER: recursively sort each half
    // COMBINE: merge the two sorted halves
    static void mergeSort(int[] arr, int left, int right) {
        if (left >= right) return;  // base case: 1 element

        int mid = left + (right - left) / 2;

        mergeSort(arr, left, mid);       // sort left half
        mergeSort(arr, mid + 1, right);  // sort right half
        merge(arr, left, mid, right);    // combine
    }

    static void merge(int[] arr, int left, int mid, int right) {
        int n1 = mid - left + 1;
        int n2 = right - mid;

        int[] L = Arrays.copyOfRange(arr, left, mid + 1);
        int[] R = Arrays.copyOfRange(arr, mid + 1, right + 1);

        int i = 0, j = 0, k = left;
        while (i < n1 && j < n2) {
            if (L[i] <= R[j]) arr[k++] = L[i++];
            else               arr[k++] = R[j++];
        }
        while (i < n1) arr[k++] = L[i++];
        while (j < n2) arr[k++] = R[j++];
    }

    public static void main(String[] args) {
        int[] arr = {38, 27, 43, 3, 9, 82, 10};
        System.out.println("Before: " + Arrays.toString(arr));
        mergeSort(arr, 0, arr.length - 1);
        System.out.println("After:  " + Arrays.toString(arr));
        // After: [3, 9, 10, 27, 38, 43, 82]
    }
}

/*
 * Recurrence: T(n) = 2T(n/2) + O(n)
 * By Master Theorem (Case 2): T(n) = O(n log n)
 *
 * Space: O(n) auxiliary for temporary arrays
 */
Maximum Subarray — Kadane's vs Divide and Conquer
public class MaxSubarray {

    // Divide and Conquer approach — O(n log n)
    static int maxCrossing(int[] arr, int l, int mid, int r) {
        int leftSum = Integer.MIN_VALUE, sum = 0;
        for (int i = mid; i >= l; i--) {
            sum += arr[i];
            if (sum > leftSum) leftSum = sum;
        }
        int rightSum = Integer.MIN_VALUE;
        sum = 0;
        for (int i = mid + 1; i <= r; i++) {
            sum += arr[i];
            if (sum > rightSum) rightSum = sum;
        }
        return leftSum + rightSum;
    }

    static int maxSubarrayDC(int[] arr, int l, int r) {
        if (l == r) return arr[l];
        int mid = (l + r) / 2;
        return Math.max(
            Math.max(maxSubarrayDC(arr, l, mid),
                     maxSubarrayDC(arr, mid + 1, r)),
            maxCrossing(arr, l, mid, r)
        );
    }

    // Kadane's Algorithm — O(n) — simpler and faster
    static int kadane(int[] arr) {
        int maxSoFar = arr[0], maxEndingHere = arr[0];
        for (int i = 1; i < arr.length; i++) {
            maxEndingHere = Math.max(arr[i], maxEndingHere + arr[i]);
            maxSoFar = Math.max(maxSoFar, maxEndingHere);
        }
        return maxSoFar;
    }

    public static void main(String[] args) {
        int[] arr = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
        System.out.println("D&C:    " + maxSubarrayDC(arr, 0, arr.length - 1)); // 6
        System.out.println("Kadane: " + kadane(arr));  // 6
    }
}

Advantages and Disadvantages

AdvantagesDisadvantages
Naturally parallelizable — subproblems are independentRecursion overhead (function calls, stack frames)
Often achieves optimal time complexityExtra memory for recursive call stack
Clean, elegant code structureCombine step can be complex (e.g., merge in merge sort)
Cache-friendly for many problemsNot suitable when subproblems overlap (use DP instead)

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.