题目链接: https://leetcode.com/problems/count-of-smaller-numbers-after-self/
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. 当要插入的元素>根元素, 则递归的插入到右子树, 要插入元素的计数为 += (1 + 根结点左子树个数)
2. 当要插入的元素=根元素, 则递归的插入到右子树,要插入元素的计数为 += (根结点左子树的个数)
3. 当要插入的元素<根元素, 则递归的插入到左子树,要插入元素的计数不变并且根结点左子树计数+1
然后在插入的时候我们将每一个结点都保存到一个数组, 最后遍历一遍数组取出每个结点的计数即可.
其中插入元素的时间复杂度是O(log n), 因此总的时间复杂度是O(n log n).
代码如下:
class Solution { public: struct Node { Node(int Id, int Val):id(Id),cnt(0),val(Val),leftCnt(0),left(NULL),right(NULL){} int id, cnt, val, leftCnt; struct Node *left, *right; }; void insert(Node* root, Node* p) { if(root == NULL) return; if(p->val >= root->val) { p->cnt += root->leftCnt; if(p->val > root->val) p->cnt++; if(root->right) insert(root->right, p); else root->right = p; } else { root->leftCnt++; if(!root->left) root->left = p; else insert(root->left, p); } } vector<int> countSmaller(vector<int>& nums) { int len = nums.size(); if(len == 0) return result; result.resize(len, 0); vector<Node*> vec; Node* root = new Node(len-1, nums[len-1]); vec.push_back(root); for(int i = len-2; i >= 0; i--) { Node* tem = new Node(i, nums[i]); vec.push_back(tem); insert(root, tem); } for(auto val: vec) result[val->id] = val->cnt; return result; } private: vector<int> result; };