归并排序+逆序对

第二次接触MergeSort与Inversions(逆序对),这次弄懂了每一个细节,很好的理解了两者之间关系。下面总结一下相关技巧。

1.MergeSort和quickSort类似,主要采用了Divide-and-Conquer的思想,也就是将原本规模为1的问题,二分为规模相近的两个独立子问题,同时求解。因此这里递归通常是常用技巧。这里难点应该在边界条件的判断上。(当然这也是算法通有难点)。

   既然提到了quickSort,这里不得不对比一下两种排序算法的区别。时间复杂度上,MergeSort经历了一个先分后合的过程,而quickSort在划分成子问题时就已经在排序了,因此分好即排序完毕。因此,虽然两者时间复杂度理论上都小于O(nlogn),但是快排好很多,尤其是快排的轴点选取法采用“任意三点取中法”时。空间复杂度上,前者在每次合并时至少需要开辟(lo+hi)>>1大小的数组备份前半部分数据,空间复杂度较大(可以结合后面的代码理解);而后者,只需在每次划分为子问题时,另开辟一个相关类型的空间存储轴点pivot。综上,可以看出,快排好很多。

2.Inversions。逆序对指序列中与理想次序不一致的点对数。其可以定量衡量InsertionSort的效率。比如升序序列中,如果秩i<j,但是i对应的值却大于j,则i和j对应的值构成一对逆序对。降序,相反。注意,这里采用前向统计方法。比如序列(5,6,2,2,3),元素3对应的逆序对为2,分别为(5,3),(6,3).这里是关键

3.在MergeSort中如何统计Inversions?在相邻序列归并的过程过程中,如果一旦存在后序元素大于前序,则存在逆序对,且逆序对数恰好为(lb-i)。其中,i为前序元素的秩,lb为前序元素的大小。请自行画图并结合一下代码理解。

完整代码如下:

#include <iostream>
#include <vector>

using namespace std;

vector<int*> sequence;
int cnt = 0;                    //统计逆序对数

void merge(int lo, int mid, int hi){
    int lb = mid - lo;
    int s = lo;

    vector<int*> B(lb);
    for (int i = 0; i < lb; i++){    //深拷贝
        int* a = new int;
        *a = *sequence[s++];
        B[i] = a;
    }


    //开始比较操作
    int i = 0;

    //一共有四种边界情况
    while (hi>lo)                                     
    {
        if ((i < lb) && (mid < hi)){
            if (*B[i] <= *sequence[mid])
                *sequence[lo++] = *B[i++];
            else{
                cnt += (lb - i);                         //统计逆序对
                *sequence[lo++] = *sequence[mid++];
            }

        }
        if ((i < lb) && !(mid < hi)){
            *sequence[lo++] = *B[i++];
            //cnt += (lb - i);                           //想一下这里为什么要去掉
        }
        if (!(i<lb) && (mid < hi))
            *sequence[lo++] = *sequence[mid++];
    }
}

void mergeSort(int lo, int hi){
    if (hi - lo < 2)
        return;    //相邻元素自然有序
    int mid = (lo + hi) >> 1;
    mergeSort(lo, mid);
    mergeSort(mid, hi);
    merge(lo, mid, hi);
}

int main(){
    int n;
    scanf("%d", &n);
    sequence.resize(n);

    for (int i = 0; i < n; i++)
    {
        int* a = new int;
        scanf("%d", a);
        sequence[i] = a;
    }

    mergeSort(0, sequence.size());
    for (int i = 0; i < n; i++)
    {
        cout << *sequence[i] << " ";
    }
    cout << endl << cnt << endl;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/aishuirenjia/article/details/100066548