数组的逆序对

题目描述来自力扣https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

除了暴力法以外,还有两种更优的方法可以解决这个问题。

  1. 利用归并排序,递归的求解逆序对的个数。
  2. 利用元素计数数组。构造这个计数数组时,需要在原数组上从后往前遍历。此外,使用树状数组可以减少内存使用。

1)第一种方法:

7 12 15 16 | 4 6 9 10 
^ ^ ^
lp rp rp

进行归并排序时,假设已经回溯到最后一次合并,如上图所示。

1、两个子数组进行一轮比较后,右边数组指针指向红色标记处。此时,对于lp指向的元素来说,存在两个逆序对(<7, 4>和<7, 6>)。

2、进行第二轮比较后,lp此时指向12,rp指向右边数组的尾后位置。这是,对于元素12来说,逆序对个数为4。

依次类推,可以得到最终答案。当然,在递归最深处往前回溯时,会自动记录各个不同消息子数组的逆序对个数。具体代码如下:

int reversePairs(vector<int>& nums) {
    int n = nums.size();
    vector<int> temp(n);
    return mergeAndCount(nums, temp, 0, n - 1);
}

int mergeAndCount(vector<int>& nums, vector<int>& temp, int lo, int hi)
{
    if(lo >= hi) return 0;
    int mid = lo + (hi - lo) / 2;
    int ans = 0;
    ans += mergeAndCount(nums, temp, lo, mid) + mergeAndCount(nums,temp, mid + 1, hi);
    if(nums[mid] <= nums[mid + 1])
    {
        return ans;
    }
    int pos = lo;
    int i = lo;
    int j = mid + 1;
    while(i <= mid && j <= hi)
    {
        if(nums[i] <= nums[j])
        {
            temp[pos++] = nums[i++];
            ans += j - mid - 1;
        }
        else
        {
            temp[pos++] = nums[j++];
        }
    }
    for(int k = i; k <= mid; ++k)
    {
        temp[pos++] = nums[k];
        ans += j - mid - 1;
    }
    for(int k = j; k <= hi; ++k)
    {
        temp[pos++] = nums[k];
    }
    std::copy(temp.begin() + lo, temp.begin() + hi + 1, nums.begin() + lo);
    return ans;
}

时间复杂度:O(nlgn), 空间复杂度:O(n)

2)第二种方法:

 基本原理比较好理解。现在来说一下如何利用树状数组减少内存的使用。树状数组用来存在原始数组的前缀和。其更新和查询的时间复杂度均为O(lgn),n为数组大小。

实际上,树状数组只需要存储原始数组每个元素大小的排名即可。这样一来,树状数组的尺寸可以设为原始数组的大小加1(设原始数组有n个元素,则数状数组大小为n+1。因为数组数组第0位不存储元素)。

代码如下:

struct TreeArray
{
    TreeArray(int cap) : data(cap + 1), n(cap) {}

  //从整数的二进制表达来看,此函数用来计算整数x的最低`1`比特位到x的最低比特位组成的整数
static int lowbit(int x) { return x & (-x); }
  //查询并返回原始数组a[1...i]的和
int query(int i) { int res = 0; while(i > 0) { res += data[i]; i -= lowbit(i); } return res; }
  //更新树状数组
void update(int i, int val) { while(i <= n) { data[i] += val; i += lowbit(i); } } private: int n; vector<int> data; }; class Solution { public: int reversePairs(vector<int>& nums) { int n = nums.size(); vector<int> temp = nums; std::sort(temp.begin(), temp.end()); for(auto& num : nums) { num = lower_bound(temp.begin(), temp.end(), num) - temp.begin() + 1; } TreeArray ta(n); int ans = 0; for(int i = n - 1; i >= 0; --i) { ans += ta.query(nums[i] - 1); ta.update(nums[i], 1); } return ans; } };

PS: 树状数组的基本原理和实现可以参考以下链接

https://blog.csdn.net/flushhip/article/details/79165701

https://www.cnblogs.com/xenny/p/9739600.html

猜你喜欢

转载自www.cnblogs.com/chenqn/p/13192423.html