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,問題得解。

##後記 這是我在作這道題時的思考過程。超時後想了好久怎麼優化,最終解決了問題真的很開心。在這個過程當中,本身對分治思想和備忘錄法也有了更深的理解,還學到了線段樹、樹狀數組等新的數據結構,以爲有必要寫下來提醒本身,但願對你們也有所幫助。

相關文章
相關標籤/搜索