ArraysCount of Smaller Numbers After Self

Count of Smaller Numbers After Self

16 mins read The Jat Hard Updated 11 months ago
Array

Problem Statement

Given an integer array nums, return an integer array counts where counts[i] is the number of smaller elements to the right of num[i].

LeetCode:

Examples

Example 1:

Input: nums = [5,2,6,1]
Output: [2,1,1,0]

Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.
Example 2:

Input: nums = [-1]
Output: [0]
Example 3:

Input: nums = [-1,-1]
Output: [0,0]

Constraints:

1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
  • 1 ≤ nums.length ≤ 10^5:
    • The size of the array can be very larger (up to 100,000).
    • Brute Force (O(n^2)), where you loop through all elements after i, will TLE (Time Limit Exceeded).
    • You need an efficient solution: O(n log n) is ideal.
  • -10^4 ≤ nums[i] ≤ 10^4:
    • Elements are bounded integers.

Different Approaches

1️⃣ Brute Force Approach

🧠 Intuition:

For each element, scan all elements to its right and count how many are smaller.

💡 Approach:

  • Loop through each element.
  • For each, run a nested loop to its right.
  • Count how many are smaller.

Code:

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        vector<int> result(n, 0);

        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (nums[j] < nums[i])
                    result[i]++;
            }
        }
        return result;
    }
};

Complexity Analysis:

  • Time Complexity:O(n^2)
  • Space Complexity:O(1)

2️⃣ Modified Binary Search

Code:

class Solution {
public:

    // This function merges two sorted halves and counts the number of smaller elements after each element
    void merge(vector<pair<int, int>>& nums, vector<int>& count, int left, int mid, int right) {
        int i = left;
        int j = mid + 1;
        vector<pair<int, int>> temp;

        // Merge the two sorted halves
        while (i <= mid && j <= right) {
            if (nums[i].first <= nums[j].first){
                // Element at j is not smaller, just add to temp
                temp.push_back(nums[j++]);
            } else {
                // Element at i is greater than nums[j]
                // So nums[i] has (right - j + 1) smaller elements to its right
                count[nums[i].second] += (right - j + 1);
                temp.push_back(nums[i++]);
            }
        }

        // Append remaining elements from the left half
        while (i <= mid) {
            temp.push_back(nums[i++]);
        }

        // Append remaining elements from the right half
        while (j <= right) {
            temp.push_back(nums[j++]);
        }

        // Copy merged elements back to the original `nums` vector
        for (int k = 0; k < temp.size(); k++) {
            nums[left + k] = temp[k];
        }
    }

    // Recursive merge sort with counting
    void sort(vector<pair<int, int>>& nums, vector<int>& count, int left, int right) {
        if (left < right) {
            int mid = (left + (right - left) / 2);

            // Sort left half
            sort(nums, count, left , mid);

            // Sort right half
            sort(nums, count, mid + 1, right);

            // Merge both halves and count inversions
            merge(nums, count, left, mid, right);
        }
    }

    // Main function
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();

        // Pair each element with its original index
        vector<pair<int, int>> arr(n);
        for (int i = 0; i < n; i++) {
            arr[i] = {nums[i], i};
        }

        // Result array to store counts
        vector<int> count(n, 0);

        // Perform modified merge sort
        sort(arr, count, 0, n - 1);

        return count;
    }
};

Complexity Analysis:

  • Time Complexity:O(n log n)
    • The array is divided into two halves recursively, O(log n)
    • At each level, merging takes O(n) time (since every element is visited once per level).
    • During the merge step, we also count the number of smaller elements, which takes constant time per element.
  • Space Complexity:O(n)
    • count vector stores the count of smaller elements of size n.
    • temp vector is used during merging up to size n.
    • arr vector of pairs of size n.
Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *

Your experience on this site will be improved by allowing cookies Cookie Policy