hackerrank: Insertion Sort Advanced Analysis

Insertion Sort Advanced Analysis算法

Insertion Sort is a simple sorting technique which was covered in previous challenges. Sometimes, arrays may be too large for us to wait around for insertion sort to finish. Is there some other way we can calculate the number of times Insertion Sort shifts each elements when sorting an array?数组

If ki is the number of elements over which ith element of the array has to shift then total number of shift will be k1 + k2 + … + kN.数据结构

Input: The first line contains the number of test cases T. T test cases follow. The first line for each case contains N, the number of elements to be sorted. The next line contains N integers a[1],a[2]…,a[N].优化

Output: Output T lines, containing the required answer for each test case.ui

Constraints: 1 <= T <= 5 1 <= N <= 100000 1 <= a[i] <= 1000000code

Sample Input:排序

2 5 1 1 1 2 2 5 2 1 3 1 2element

Sample Output:it

0 4io

Explanation First test case is already sorted therefore there’s no need to shift any element. In second case it will proceed in following way.

Array: 2 1 3 1 2 -> 1 2 3 1 2 -> 1 1 2 3 2 -> 1 1 2 2 3 Moves: ---
1 --- 2 --- 1 = 4

##通俗易懂的 O(N^2) 算法 这道题其实就是求一个数组中的逆序对的数量(作题时还不知道这叫逆序对呢,-_-!!!)。

最直白的解法就包含在题目标题里——插入排序 (O(N^2)):

int solve(vector<int> &v) {
    int c = 0;
    // 插入排序
    for (int i = 1; i < v.size(); ++i) {
        int tmp = v[i];
        int j = i - 1;
        for (; j >= 0 && v[j] > tmp; --j) {
            v[j + 1] = v[j];
            // 在这里加一个计数变量就好了
            ++c;
        }
        v[j + 1] = tmp;
    }

    return c;
}

这种方法的好处是简单明了,直接模拟人脑数数过程。但缺点也很明显——效率过低。 在本题里,N 的上限是 100000,而插入排序的时间复杂度是 O(N^2),显然会超时。

##那如何提升“数数”效率呢? 咱们先在脑海里模拟一下插入排序的数数过程:

假若有这样一个数组,

8 7 2 1 3 1 2

先来计算 8 的逆序对数,简单,数一下它前面有几个大于它的数就好了:

嗯,它前面没有数,也就没有逆序对数了

下面计算 7 的:

8,有一个

而后是 2:

八、7,有两个

1 的:

八、七、2,三个

一直继续……

发现没有,八、7 组成的区间(以及其余以前计算时出现过的区间)被反复数了好几遍,这太浪费时间了吧?!

若是能把以前区间的计算结果保存下来,用到时直接查询,消除重复“劳动”,“数数”效率天然会大大提升。

##O(NlogN) 算法隆重登场 经过前面的分析,咱们知道,若是可以保存以前工做的状态,就能比较高效地解决这个问题了。从划分区间入手,很容易想到归并排序。它的时间复杂度是 O(NlogN),解决本题绰绰有余。

归并解法:

long long mergeSort(vector<int> &v) {
    // 归并程序
    if (v.size() <= 1) {
        return 0;
    }

    long long ans = 0;
    int m = v.size() / 2;
    vector<int> v1(m);
    copy(v.begin(), v.begin() + m, v1.begin());
    vector<int> v2(v.size() - m);
    copy(v.begin() + m, v.end(), v2.begin());
    // 将分两段处理的结果加起来
    ans += mergeSort(v1);
    ans += mergeSort(v2);

    auto it = v.begin();
    auto it1 = v1.begin();
    auto it2 = v2.begin();
    while (it1 != v1.end() && it2 != v2.end()) {
        if (*it1 <= *it2) {
            *it++ = *it1++;
        } else {
            // *it1 及至分段结束的值都大于 *it2,它们均可以跟 *it2 组成逆序对
            ans += v1.end() - it1;
            *it++ = *it2++;
        }
    }
    while (it1 != v1.end()) {
        *it++ = *it1++;
    }
    while (it2 != v2.end()) {
        *it++ = *it2++;
    }

    return ans;
}

提交,OK,问题得解。

##后记 这是我在作这道题时的思考过程。超时后想了好久怎么优化,最终解决了问题真的很开心。在这个过程当中,本身对分治思想和备忘录法也有了更深的理解,还学到了线段树、树状数组等新的数据结构,以为有必要写下来提醒本身,但愿对你们也有所帮助。

相关文章
相关标签/搜索