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]