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

来源:互联网 发布:青岛seo排名软件 编辑:程序博客网 时间:2024/06/05 06:49

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
原创粉丝点击