315-Count of Smaller Numbers After Self

1.题目描述

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]
return the array [2, 1, 1, 0].

2.算法分析

在这里使用两种方法来解决这道题目,但是两者的时间复杂度都没有很好,二叉树实现的方法后期会补上。

(1)第一种算法:

直接两层for循环实现,这是大多数人的第一想法,时间复杂度是O(n^2),但是需要注意下面这两种实现,其中一种能够通过leetcode的测试,一种则不能。
因为vector的push_back会不断地拓展新的内存空间,并且同时需要拷贝原有的数据,而一开始就给定vector的长度可以减少这些操作。
未通过的实现:

vector<int> countSmaller(vector<int>& nums) {
    int n = nums.size();
    int count = 0;
    vector<int> ans;
    for (int i = 0; i < n; ++i) {
        count = 0;
        for (int j = i+1; j < n; ++j) {
            if (nums[j] < nums[i]) {
                count++;
            } 
        }
        ans.push_back(count); 
    }
    return ans;
}

成功通过的实现:

vector<int> countSmaller(vector<int>& nums) {
    int n = nums.size();
    int count = 0;
    //  直接使用ans[i] = count可以通过,使用push_back则不能,但是要注意在声明ans的时候要给出其长度,相当于数组来使用。 
    vector<int> ans(n);
    for (int i = 0; i < n; ++i) {
        count = 0;
        for (int j = i+1; j < n; ++j) {
            if (nums[j] < nums[i]) {
                count++;
            } 
        }
        //  注意这里的变化
        ans[i] = count; 
    }
    return ans;
}

下面是main函数,用于测试:

#include <iostream>
#include <vector>
using namespace std;

vector<int> countSmaller(vector<int>& nums);

int main() {
    int n, value;
    vector<int> nums;
    cout << "the size of the array: " << endl;
    cin >> n;
    for (int i = 0; i <n; ++i) {
        cin >> value;
        nums.push_back(value);
    }   
    vector<int> result = countSmaller(nums);
    for (int i = 0; i < n; ++i) {
        cout << result[i] << " ";
    }
    cout << endl;
    return 0;
} 

(2)第二种算法

采用逆序数组来实现:

vector<int> countSmaller(vector<int>& nums) {
    int n = nums.size();
    int left, right, mid;
    vector<int> sorted, ans(n);
    for (int i = n - 1; i >= 0; --i) {
        left = 0;
        right = sorted.size();
        while (left < right) {
            mid = left + (right - left)/2;
            if (sorted[mid] >= nums[i]) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        ans[i] = right;
        sorted.insert(sorted.begin()+right, nums[i]);
    }
    return ans;
}

从右往左遍历整个数组,并且不断地对右边的数字进行排序,当到达i位置的时候,因为右边的数字都已经进行排序,所以只需要知道该数在排序数组中的位置,则排序数组中该位置之前的数字都是比nums[i]小的,所以其位置也恰好就是比它小的数字的数目。并且知道该数的位置以后,直接将其插入到排好序的数组中,可以保证排序数组不断扩大,并且总是保持有序。

Example:
比如前面的[5,2,6,1]
从右往左,首先是nums[3] = 1, 此时直接将1放入到排序数组中,并且因为排序数组开始的时候是空的,所以比它小的数字的数目是0,所以ans[i] = 0
接着是nums[2] = 6, 因为6要插在[1]的右边,所以其插入的位置是1,其前面的数字也就是(1)就是比它小的数。
接着是nums[1] = 2, 用二分查找的方法得到在排序数组[1,6](1和6都是2右边的数字,并且已经排好序)中,2要插在1的位置,所以在2右边比2小的数字的数目是1。
依次方法可以得到5要插在2的位置。
所以最后得到[2,1,1,0]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值