1. Topic Description
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. Algorithmic Analysis
In this paper, two methods are used to solve this problem, but the time complexity of both methods is not very good. The method of binary tree implementation will be supplemented later.
(1) The first algorithm:
Direct two-tier for loop implementation is the first idea of most people. Time complexity is O(n^2), but we need to pay attention to the following two implementations, one of which can pass the leetcode test, the other can not.
Because the push_back of vectors will continue to expand new memory space, and at the same time need to copy the original data, and given the length of vectors at the beginning can reduce these operations.
Implementations not adopted:
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;
}
Achievements successfully adopted:
vector<int> countSmaller(vector<int>& nums) {
int n = nums.size();
int count = 0;
// Direct use of ans[i] = count can pass, but push_back can't, but note that when declaring ans, give its length, which is equivalent to an array to use.
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++;
}
}
// Notice the changes here.
ans[i] = count;
}
return ans;
}
Here is the main function for testing:
#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) The second algorithm
An inverse array is used to achieve:
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;
}
Traversing the whole array from right to left, and continually sorting the numbers on the right. When we reach position i, because the numbers on the right have been sorted, we only need to know the position of the number in the sorted array, then the number before that position in the sorted array is smaller than nums[i], so its position is exactly the number of numbers smaller than it. And knowing the position of the number, inserting it directly into the ordered array can ensure that the ordered array expands continuously and always keeps in order.
Example:
For example, the previous [5, 2, 6, 1]
From right to left, nums[3] = 1 is the first one. At this time, we put 1 directly into the sorted array, and because the sorted array is empty at the beginning, the number of numbers smaller than it is 0, so ans[i] = 0.
Next is nums[2] = 6, because 6 is to be inserted on the right side of [1], so its insertion position is 1, and the number in front of it is (1) smaller than it.
Next is nums[1] = 2. In the sorted array [1,6] (1 and 6 are both numbers on the right of 2 and have been sorted), 2 is inserted in the position of 1, so the number of numbers smaller than 2 on the right of 2 is 1.
The sequential method can get 5 positions to be inserted in 2.
So we end up with [2,1,1,0]