1. 程式人生 > >2019秋招筆試題——(數組合並)n個有序集合的並集,時間複雜度O(n^2)

2019秋招筆試題——(數組合並)n個有序集合的並集,時間複雜度O(n^2)

這是一道下午剛剛筆試的題目,百詞斬的秋招演算法工程師題目中的一個。
題目:
這裡寫圖片描述
n個有序集合的合併,我最低的時間複雜度只能降到O(n^2),水平不夠,不能再優化了。
先說說我的思想:
輸入要求已經說明了,我必須要先儲存這n個集合,包括集合的長度以及元素,顯然是一個二維陣列,第一維存放長度並控制集合的選擇,第二維存放對應的集合元素。輸入輸出不再贅述。
首先我摒棄了O(n^3)時間複雜度的3個for迴圈巢狀的常規套路,而是用三個平行的while執行兩個集合的合併操作,然後將合併後集合元素拷貝給while迴圈中的第一個集合,外圍用一個for迴圈來控制另一個集合,這樣就可以把不斷的進行兩兩合併。我並沒有進行排序,而是比較插入一氣呵成。
我覺得這個演算法最重要的有兩點:
1.集合兩兩合併問題
2.迴圈控制——迴圈進行兩兩合併問題
對於第一個問題,嚴蔚敏的資料結構一書中兩個非遞減的線性表合併的虛擬碼給出,三個非巢狀while迴圈時間複雜度可以降為O(n^2),而不是很多博文中三個for迴圈巢狀的那種做法。非常有代表性。
簡而言之就是:
類似雙指標跳動,雙下標進行跳動比較。
如下程式碼所示

while ( (i <= LA.length()) && (j <= LB.length()) )
{
     ai = LA.GetElem(i);
     bi = LB.GetElem(j);
     if(ai <= bi)
     { LC.ListInsert(++k, ai);++i;  }
     else{ LC.ListInsert(++k, bi);++j;}
}
while ( i <= LA.length())
{
         ai = LA.GetElem(i++);
         LC.ListInsert(++k, ai);
}
while (i <= LB.length()) { bi = LB.GetElem(i++); LC.ListInsert(++k, bi); }

第二個問題怎麼控制迴圈
我先把第一個集合拷貝給一個迴圈利用的集合result,在合併操作外圍用一個for迴圈來控制依次取出剩下的每一個集合,每次合併操作的結果再次拷貝覆蓋result集合。這樣通過外圍的迴圈,就不斷的完成兩兩合併。值得注意的是,兩兩合併時,需要用result集合元素的個數來控制合併,因此每次合併後集合元素的個數要不斷返回給while中的變數,引數傳遞確實比較繞。
以下是完整C++程式碼

#include <stdio.h>
#include <stdlib.h>
#define max_size 1000
int main()
{
    int n, i, j;
    int **sets;
    int *set_size;
    int *result, *result1,sizeResult = 0;
    scanf_s("%d", &n);
    sets = (int **)malloc(sizeof(int *) * n);
    set_size = (int *)malloc(sizeof(int) * n);
    for (i = 0; i < n; ++i) {
        scanf_s("%d", &set_size[i]);
        /*if (set_size[i] > max_size) {
            max_size = set_size[i];
        }*/
        sets[i] = (int *)malloc(sizeof(int) * set_size[i]);
        for (j = 0; j < set_size[i]; ++j) {
            scanf_s("%d", &sets[i][j]);
        }
    }
    result = (int *)malloc(sizeof(int) * max_size);
    result1= (int *)malloc(sizeof(int) * max_size);
    for (int i = 0; i < set_size[0]; i++)
    {
        result[i] = sets[0][i];
    }

    for (int i = 1; i <=n; i++)
    {
        int t = 0;
        int ai = 0;
        int bi = 0;
        int k = 0; int q = 0;
        while ((k < set_size[0]) && (q < set_size[i]))
        {
            ai = result[k];
            bi = sets[i][q];
            if (ai <= bi) 
            { 
                result1[t] = ai; 
                ++k;
                ++t;
            }
            else
            { result1[t] = bi; ++q; ++t; }
        }
        while (k < set_size[0])
        {
            ai = result[k++];
            result1[t]= ai;
            t++;
        }
        while (q <set_size[i])
        {
            bi = sets[i][q++];
            result1[t] = bi;
            t++;
        }
        set_size[0] = t;
        for (int j = 0; j < t; j++)
        {
            result[j] = result1[j];
        }
    }
    for (i = 0; i < set_size[0]; ++i) 
    {
        printf("%d ", result[i]);
    }
    system("pause");
    return 0;
}


/*
測試用例
輸入:
4
3 1 2 3
4 1 1 1 3
2 2 3
2 3 4
*/

執行結果:
這裡寫圖片描述
這裡寫圖片描述