剑指offer面试题之求数组中逆序对的个数

1,问题:

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

2,想法:

(1),暴力枚举法o(n2)

(2),利用归并的思想,先计算左半边的逆序对数,再计算右半边的逆序对数,再计算合并时的逆序对数,合并的过程中排序。方便上层递归计算。

3,在牛客网上的编码为:

class Solution {
public:
    int merge(int* p, int low, int mid, int high)
    {
        int m = mid - low + 1;
        int n = high - mid;
        int* temp1 = new int[m];
        int* temp2 = new int[n];
        int i,j;
        int count = 0;
        int tempflag = 0;//临时标记值,用来记录当前以i为首的逆序对个数
        for (i = 0; i < m; i++)
        {
            temp1[i] = p[i + low];
        }
        for (j = 0; j < n; j++)
        {
            temp2[j] = p[j + mid + 1];
        }
        int k = low;
        //整个算法的难点就在合并这里,看怎么计算count
        for (i = 0, j = 0; i < m && j < n;)
        {
            if (temp1[i] > temp2[j])
            {
                count++;
                p[k] = temp2[j];
                tempflag = count;//时刻标记着,记录以当前i为首的逆序对的个数
                j++;
                k++;
            }
            else
            {
                p[k] = temp1[i];
                i++;
                k++;
                //count = count + count;//这里不能是翻倍的增加,会放大
                //例子就是6,5,4,3,2,1,应该增加的是以i-1为首的逆序对个数
                //在决定是否加tempflag前,应该判断i值是否已经超出
                //超出就不再加了,因为这时的i不存在,若加就多算了一次
                //例子就是1,2,1,2,1
                if (i < m)
                {
                    count = count + tempflag;                    
                }
            }
        }
        if (i < m)
        {
            p[k] = temp1[i];//以这个i为首的逆序对已经求了,不能再算一次
            k++;
            for (i = i + 1; i < m; i++)//现在以i为首的逆序对数就是tempflag个
            {
                p[k] = temp1[i];
                //count = count + count;
                count = count + tempflag;
                k++;
            }
        }
        else if (j < n)
        {
            for (j; j < n; j++)
            {
                p[k] = temp2[j];
                k++;
            }
        }
        delete temp1;
        delete temp2;
        return count;
    }
    int Count(int *p, int low, int high)
    {
        if (p == NULL || low == high)
        {
            return 0;
        }
        int count = 0;
        if (low < high)
        {
            int mid = (low + high)/2;
            int leftcount = Count(p, low, mid);
            int rightcount = Count(p, mid + 1, high);
            int mergecount = merge(p, low, mid, high);
            count = leftcount + rightcount + mergecount;
        }
        return count;
    }
    int InversePairs(vector<int> data) {
        //O(n2),最笨方法
        /*if (data.empty())
        {
            return 0;
        }
        unsigned int i,j;
        int count = 0;
        for (i = 0; i < data.size(); i++)
        {
            for (j = i + 1; j < data.size(); j++)
            {
                if (data[i] > data[j])
                {
                    count++;
                }
            }
        }
        return count;*/
        if (data.empty())
        {
            return 0;
        }
        unsigned int i;
        int m = data.size();
        int *temp = new int[m];
        for (i = 0; i < m; i++)
        {
            temp[i] = data[i];
        }
        return Count(temp, 0, m - 1);
    }
};
在利用合并的思想要注意的几点:

(1),怎么尽量少使用辅助空间

(2),在合并的过程中,计算逆序对,需要考虑全面,即将两个有序数组合A(在前)和B(在后)并为一个有序数组的过程中,每次A数组的指针i增加1时,先要考虑这个指针i是否已经超出限制条件,若没有,那以这个指针i所在位置值为首的逆序对,至少都有以i-1所在位置为首的逆序对个数那么多个。而我们需要做的就是记录以当前i所在位置值为首的逆序对的个数。以方便当i增加1时,把握count的变化。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值