315-Count of Smaller Numbers After Self

来源:互联网 发布:山下智久长泽雅美 知乎 编辑:程序博客网 时间:2024/06/05 17:57

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]