Count of Smaller Numbers After Self

来源:互联网 发布:易语言源码怎么用 编辑:程序博客网 时间:2024/05/30 21:58
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].

tags:Divide and Conquer / Binary Indexed Tree / Segment Tree / Binary Search Tree

1)最开始想到二分搜索(binary search)。思想是,维持一个vector<int> arr,初始为空。从nums的尾部向前遍历,对于每个元素,都在arr中进行binary search,返回该元素应该插入的位置i,i就是比该元素小的元素的个数。然后将该元素插入arr中。

开始以为这样的复杂度为O(lgn!)。但是我忘了vector的insert操作不是O(1),因为每次插入都要向后移动一部分元素,所以很费时间。虽然vector在binary search 中 的访问时间为O(1)。代码如下:

<span style="font-size:18px;">class Solution { //二分搜索O(lg(n!)) 1700ms左右public:    vector<int> countSmaller(vector<int>& nums) {         vector<int> arr, res;        int size = nums.size(), pos;        for(int i = size-1; i >= 0; --i){            pos = biSearch(arr, nums[i]);            cout<<pos<<" ";            arr.insert(arr.begin()+pos, nums[i]);            res.insert(res.begin(), pos);        }        return res;    }    int biSearch(vector<int>& arr, int n){        int s = 0, e = arr.size()-1, mid;        float tmp = n - 0.5;        while(s<=e){            mid = (s+e)/2;            if(arr[mid]<tmp) s++;//因为是整数,不会出现相等的情况。            else e--;        }        return s;    }};</span>
2)由1)中遇到的问题,可以想到用binary search tree,因为BST的搜索和插入时间都为lgN。这样时间复杂度就变成了N*O(lgN)。下面的代码是discuss中的,写的很好,40ms,值得学习。

<span style="font-size:18px;">class Solution {//BSTpublic:    struct Node {        int val, smaller; //smaller统计该节点左子树中节点的个数。        Node *left, *right;        Node(int value, int small) {             left=right=NULL, val=value, smaller=small;         }    };        int insert(Node *&root, int value) {//将一个元素插入到bst中,并返回树中比该元素小的元素的个数。        if (root==NULL)            return (root=new Node(value, 0)), 0;        if (value < root->val){            root->smaller++;            return insert(root->left, value);        }         else            return insert(root->right, value) + root->smaller + (value>root->val ? 1 : 0);    }        vector<int> countSmaller(vector<int>& nums) {        Node *root = NULL;        deque<int> ans; //deque可以从头开始插入元素,只需O(1),而vector需要O(n)        for (int i=nums.size()-1; i>=0; i--)            ans.push_front(insert(root, nums[i]));                    return vector<int> (ans.begin(), ans.end());    }};</span>
3)然后就是分治方法了。这个方法大体也想出来了。但是在merge时候,要维持一个index数组,用来保存某个数最原始的位置。代码如下,时间100ms,可能因为常数项大了点。

<span style="font-size:18px;">class Solution { //分治O(nlgn) public:vector<int> countSmaller(vector<int>& nums) {int size = nums.size();vector<int> res(size, 0);vector<int> ind(size, 0);for (int i = 0; i < size; ++i) ind[i] = i;mergeSort(nums, 0, size - 1, res, ind);return res;}void mergeSort(vector<int>& nums, int s, int e, vector<int>& res, vector<int>& ind){if (s < e){int mid = (s + e) / 2;mergeSort(nums, s, mid, res, ind);mergeSort(nums, mid + 1, e, res, ind);merge(nums, s, mid, e, res, ind);}}void merge(vector<int>& nums, int s, int mid, int e, vector<int>& res, vector<int>& ind){vector<int> l, r, ind_l, ind_r;l.push_back(INT_MAX);r.push_back(INT_MAX);for (int k = s; k <= mid; ++k){l.push_back(nums[k]);ind_l.push_back(ind[k]);r.push_back(nums[k - s + mid + 1]);ind_r.push_back(ind[k - s + mid + 1]);}int i = mid - s + 1, j = e - mid, cnt = 0;for (int k = e; k >= s; --k){if (l[i] > r[j]){cnt++;nums[k] = r[j];ind[k] = ind_r[j - 1];j--;}else{nums[k] = l[i];ind[k] = ind_l[i - 1];res[ind_l[i - 1]] += cnt;i--;}}}};</span>
4)至于binary indexed tree/segment tree。有空再补上。


0 0
原创粉丝点击