LeetCode 315. Count of Smaller Numbers After Self

本文介绍了解决LeetCode第315题Count of Smaller Numbers After Self的一种方法,该方法使用了分治思想与归并排序相结合的方式。通过递归地将数组分为两个子数组,并在合并过程中计算每个元素右侧比其小的元素数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分治算法练习

本周选择LeetCode第315题Count of Smaller Numbers After Self,难度为hard。

题目描述如下:

You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example:

Given nums = [5, 2, 6, 1]
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.

Return the array [2, 1, 1, 0].


使用分治算法进行解题。
1. 考虑对于第i个元素,所求的counts[i]的值仅与nums[i]右侧的值有关,若nums[i]右侧的值已排序,则求大于nums[i]的值的数目将变得十分容易。因此可以从排序的的角度思考解题方法。
2. 归并排序是将一个大的数组,不断拆分成小数组,对小数组进行排序,并将有序的小数组合并成大的数组。对于此题,将nums[]拆分成不断左右两子数组,最终使得子数组有序,将有序子数组合并时,将右边数组中小于左边数组的元素数目加到counts[]中,最终得到counts[]。
3. 上述解法近似于对数组进行归并排序,时间复杂度为O(nlogn),需要记录counts,nums,和排序后的index,空间复杂度为O(n)。


具体代码如下:
Solution类有3个私有变量vector分别储存传入的nums,结果的counts,和排序后元素的index。
公有函数countSmaller传入nums并返回结果counts。
divide将数组其中一段分成左右两部分,并调用sort进行合并及排序。

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums);

    void divide(int head, int length);

    void sort(int head1, int head2, int length);

private:
    vector<int> counts;
    vector<int> index;
    vector<int> nums;
};

各个函数的实现:

vector<int> countSmaller(vector<int>& nums) {
    this->nums = nums;
    for (int i = 0; i < nums.size(); i++) {
        index.push_back(i);
        counts.push_back(0);
    }
    if (nums.size() != 0) 
        divide(0, nums.size());
    return counts;
}
void divide(int head, int length) {
    if (length == 1) return;
    divide(head, length/2);
    divide(head + length/2, length-length/2);
    sort(head, head+length/2, length);
    return;
}

void sort(int head1, int head2, int length) {
    int length1 = head2-head1;
    int length2 = length-length1;
    int i = 0,
        j = 0;

    // copies of indexes
    int* left = new int[length1];
    int* right = new int[length2];
    for (int k = 0 ; k < length1; k++) {
        left[k] = index[head1+k];
    }
    for (int k = 0 ; k < length2; k++) {
        right[k] = index[head2+k];
    }

    // record the count of current max num of right[]
    int maxCount = 0;

    // merge sort
    while (i != length1 && j != length2) {
        if (nums[left[i]] <= nums[right[j]]) {
            index[i+j+head1] = left[i];
            counts[left[i]] += maxCount;
            i++;
        } else {
            index[i+j+head1] = right[j];
            maxCount += 1;
            j++;
        }
    }
    for (i; i < length1; i++) {
        index[i+j+head1] = left[i];
        counts[left[i]] += maxCount;
    }
    for (j; j < length2; j++) {
        index[i+j+head1] = right[j];
    }

    delete [] left;
    delete [] right;

}

运行结果:
16 / 16 test cases passed.
Status: Accepted
Runtime: 33 ms

Your runtime beats 61.10 % of cpp submissions.

这说明还有更优的方法解决此问题,有待进一步探究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值