2019秋招筆試題——(數組合並)n個有序集合的並集,時間複雜度O(n^2)
阿新 • • 發佈:2018-11-19
這是一道下午剛剛筆試的題目,百詞斬的秋招演算法工程師題目中的一個。
題目:
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
*/
執行結果: