2019秋招笔试题——(数组合并)n个有序集合的并集,时间复杂度O(n^2)

这是一道下午刚刚笔试的题目,百词斩的秋招算法工程师题目中的一个。
题目:
2019秋招笔试题——(数组合并)n个有序集合的并集,时间复杂度O(n^2)_第1张图片
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 
#include 
#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 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
*/

运行结果:
2019秋招笔试题——(数组合并)n个有序集合的并集,时间复杂度O(n^2)_第2张图片
2019秋招笔试题——(数组合并)n个有序集合的并集,时间复杂度O(n^2)_第3张图片

你可能感兴趣的:(冲刺2020秋招季,刷题不停歇)