Skip to content

315. Count of Smaller Numbers After Self

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

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]

Solution:

class Solution {
    public List<Integer> countSmaller(int[] nums) {
        /*
        [ 5 , 2 , 6, 1]
                     i
        */
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < nums.length; i++){
            int cur = nums[i];
            nums[i] = 0;
            for (int j = i + 1; j < nums.length; j++){
                if (cur > nums[j]){
                    nums[i]++;
                }
            }
        }

        for (int i = 0; i < nums.length; i++){
            result.add(nums[i]);
        }

        return result;
    }
}
// TC: O(n^2)
// SC: O(n)

// TLE
class Solution {
    public List<Integer> countSmaller(int[] nums) {
        List<Integer> result = new ArrayList<Integer>();
        int[] counts = new int[nums.length]; // [0, 0, 0, 0]

        int[] indices = new int[nums.length]; 
        for (int i = 0; i < nums.length; i++){
            indices[i] = i;
        }

        // indices [0,  1 , 2, 3]

        int left = 0;
        int right = nums.length - 1;
        // 5  2     6 1
        //.l          r
        mergeSort(nums, counts, indices, left, right);

        for (int i = 0; i < counts.length; i++){
            result.add(counts[i]);
        }

        return result;

    }

    /*
                            5   2      6     1
                            /             \
                        5 ,2               6, 1
                        /\               / \
                    5     2            6    1
                    \     /             \ /
 // indices[1, 0]     2, 5               1 ,6    // indices[1, 0, 4, 3]
                            \        /
                            1, 2, 5, 6
                    // indices[4, 2, 1, 3]
    */ 

    private void mergeSort(int[] nums, int[] counts, int[] indices, int left, int right){
        if (left >= right){
            return;
        }

        int mid = left + (right - left)/2;
        mergeSort(nums, counts, indices, left, mid);
        mergeSort(nums, counts, indices, mid + 1, right);

        merge(nums, counts, indices, left, right);
    }

    //         // indices [0,  1 , 2, 3]

        /*
                            5   2      6     1
                            /             \
                        5 ,2               6, 1
                        /\               / \
                    5     2            6    1
                    \     /             \ /
                     2, 5               1 ,6
                            \        /
                            1, 2, 5, 6

    */ 
    private void merge(int[] nums, int[] counts, int[] indices, int left, int right){
        int mid = left + (right - left)/2;// 0 1-0/2 = 0

        int leftIndex = left;  // 0 
        int rightIndex = mid + 1; // 1

        int rightCount = 0;

        int[] sorted = new int[right - left + 1];
        int sortedIndex = 0;


        while(leftIndex <= mid && rightIndex <= right){
            // 0 <= 0 && 1 <=1
            if (nums[indices[rightIndex]] < nums[indices[leftIndex]]){
                // nums[indices[1]] = nums[1] = 2 < nums[indices[0]] = nums[0] = 5 v 
                rightCount = rightCount + 1; // we find right part has a number large than left
                sorted[sortedIndex] = indices[rightIndex]; 
                // sorted[0] = indices[1]  // [1, 0, 0, 0]
                sortedIndex++; // 1
                rightIndex++; // 1
            }else{// left 2 , right = 3  mid = 2
                counts[indices[leftIndex]] = counts[indices[leftIndex]] + rightCount;
                // counts[indices[2]] = counts[3] = counts[indices[2]]  = counts[3] + 0 =counts[3]= 0;
                sorted[sortedIndex] = indices[leftIndex];
                // sorted[0] = indices[2] = 2 [2,0] 

                sortedIndex++;
                leftIndex++;
            }
        }

        // sorted [1, 0]

        while(leftIndex <= mid){ // 0, 0
            counts[indices[leftIndex]] = counts[indices[leftIndex]] + rightCount;
            // count[indices[0]] = count[0] = 1
            sorted[sortedIndex] = indices[leftIndex];
            // [1,0]
            sortedIndex++; // 
            leftIndex++;
        }

        while(rightIndex <= right){
            sorted[sortedIndex] = indices[rightIndex];
            sortedIndex++;
            rightIndex++;
        }


        for (int i = left; i <= right; i++){
            // 0 1 
            indices[i] = sorted[i-left];

            // [0, i] = [0-0] =  
            // indices[0] = sorted[0 - 0] = sorted[0] =1 
            // indices[1, 0]
        }

    }
}

// TC: O(nlogn)
// SC: O(n)
class Solution {
    public List<Integer> countSmaller(int[] nums) {
        // nums:  [5, 2, 6, 1]
        int n = nums.length;
        int[] counts = new int[n]; // 存储最终的计数结果 

        int[] indices = new int[n]; // 存储每个元素的索引 


        // 这个循环初始化 indices 数组, 使其元素的值从0到n-1
        for (int i = 0; i < n; i++){
            indices[i] = i;            
        }

        // indices: [0, 1, 2, 3]
        // 开始对数组进行排序, 并在此过程中计算每个右侧小于它的元素数量
        // 这个方法采用分而治之的策略, 通过递归分解数组, 然后在合并过程中进行计数
        mergeSort(nums, indices, counts, 0, n - 1);   
        List<Integer> result = new ArrayList<Integer>();
        for (int count : counts){
            result.add(count);
        }

        return result;

    }

    //  nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 0, 3
    private void mergeSort(int[] nums, int[] indices, int[] counts, int left, int right){
        if (left >= right){
            return;
        }

        int mid = left + (right - left)/2; // 0 + (3 -0)/2 = 1
        mergeSort(nums, indices, counts, left, mid); // 
        // nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 0, 1
        /*     mid = 0 + (1-0)/2 = 0 
                nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 0, 0

                nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 1,1
         */
        mergeSort(nums, indices, counts, mid + 1, right);
        //  nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 2, 3
        /* 
              mid = 2 + (3-2)/2 = 2 + 0=2
            nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 2, 2
            nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 3, 3
        */
        merge(nums, indices, counts, left, right);
        //  nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 0, 1
    }

    //    nums: [5,2,6,1], indices: [0, 1, 2, 3], counts: [0, 0, 0, 0], 2, 3
    private void merge(int[] nums, int[] indices, int[] counts, int left, int right){
        int mid = left + (right - left)/2; // 计算中间点,用于分割数组为左右两部分
        // 2+(3-2)/2 = 2 + 1 /2 = 2
        int leftIndex = left; // 2 
        int rightIndex = mid + 1; // 3
        // 初始化两个指针,leftIndex指向左半部分的起始位置,rightIndex指向右半部分的起始位置
        int rightCount = 0;
        // 初始化rightCount变量,用于记录当前右侧已经合并到结果数组中的元素数量,这对于更新计数非常关键
        int[] sorted = new int[right - left + 1]; // 3-2+1 = 2        sorted[3, 2]
        int sortedIndex = 0;
        // 创建一个临时数组sorted用于存放当前范围内排序后的元素,
        // sortedIndex用于跟踪当前填充到sorted数组中的位置。

        while(leftIndex <= mid && rightIndex <= right){
            // 2 <= 2  && 3 <= 3
            if (nums[indices[rightIndex]] < nums[indices[leftIndex]]){
                // nums[indices[3]] = nums[3] = 1 < nums[indics[2]] = nums[2] = 6
                rightCount++; // 1
                sorted[sortedIndex] = indices[rightIndex]; // sorted[0] = indices[3]=3
                sortedIndex++;// 1
                rightIndex++;// 3+1
            } else {
                counts[indices[leftIndex]] = counts[indices[leftIndex]] + rightCount;
                sorted[sortedIndex] = indices[leftIndex];
                sortedIndex++;
                leftIndex++;
            }
        }

        while(leftIndex <= mid){
            // 2 <= 2
            counts[indices[leftIndex]] = counts[indices[leftIndex]] + rightCount;
            // counts[indices[2]]= counts[2] = 0 + 1;
            sorted[sortedIndex] = indices[leftIndex];
            // sorted[1] = indices[2] = 2
            sortedIndex++;// 3
            leftIndex++; // 3
        }


        while(rightIndex <= right){
            sorted[sortedIndex] = indices[rightIndex];
            sortedIndex++;
            rightIndex++;
        }

        for (int i = left; i <= right; i++){
            indices[i] = sorted[i - left];
        }
        // 2 <=3
        // indices[2] = sorted[2-2] = 3 
        // 2
    }
}

// Time Complexity: O(Nlog⁡(N)). 

// Space Complexity: O(N)

Hard!!!!

https://www.youtube.com/watch?v=bX36G_P83W4 看着半年前的视频 陷入了沉思 咋这么牛逼

...

现在不会了!!!

mergeSort的话会涉及到交换的部分, 每次sort 就是count++

indices 记录的是swap前原来的index顺序

然后利用count[] 再将结果返回到原来的index