【问题标题】:O notation and merging two already sorted arraysO 表示法并合并两个已排序的数组
【发布时间】:2021-08-12 04:33:41
【问题描述】:

我被告知要在线性时间内将两个已排序的不同大小的数组 A 和 B 合并为一个数组 C。

通过线性时间,我知道这个算法的时间复杂度必须是 O(n)。后来,我还被告知在合并数组时执行排序。

我的一个想法是创建一个算法,其中两个数组中都有两个指针,指向最小的元素。指向的最小元素将进入新数组。当一个数组耗尽时,将另一个数组的剩余元素复制到新数组中。

自从我几个月前刚开始编程以来,我发现这很难实现,因此我决定执行合并排序 (MS),因为它与上述算法最相似。但是,我担心 MS 本身的时间复杂度 - O(nlogn)

但是,鉴于这两个数组已经排序,MS 的时间复杂度会降低还是保持不变?

提前致谢。

【问题讨论】:

  • 合并排序多次执行两个排序数组的合并。您只需要执行一次,就可以使用来自 mergesort 的单个合并步骤。
  • “merge”这个词的正常用法是合并两个已经排序好的数组,而“sorting while merging”会与“merge”的含义发生冲突。应该是“合并前排序”,在这种情况下,可以使用任何排序算法对 2 个数组进行排序。如果理想是实现合并排序,那么原始方法是自下而上的方法,将单个数组的 n 个元素处理为每个大小为 1 个元素的排序运行,并且每次通过合并偶数和奇数运行以使其大小加倍并将运行次数减半,直到产生单个排序运行。
  • 归并排序的平均和最大时间复杂度为O(n*log n)。但是,由于您只需要执行单个合并步骤而不是递归合并,因此时间复杂度仅为O(n)
  • @gann:这两个数组的大小是否相同并不重要。如果数组已经排序,那么您可以同时合并和排序,使用时间复杂度为O(n) 的算法。该算法与单个合并排序步骤相同。但是,如果这两个数组没有排序,则必须先进行简单的未排序合并,然后再进行完全合并排序,其时间复杂度为O(n*log n)
  • @gann:感谢您的提议,但我认为我无法改进已经存在的答案。随意接受它。

标签: c algorithm sorting time-complexity mergesort


【解决方案1】:

将两个已排序的大小不同的数组 A 和 B 合并到一个 数组,C 线性时间。

int *mergeArrays(const int *array1, const int *array2, size_t size1, size_t size2, int asc)
{
    int *result = malloc((size1 + size2) * sizeof(*array1));
    size_t index1 = 0, index2 = 0;
    size_t i;


    for(i = 0; i < size1 + size2 && index1 < size1 && index2 < size2; i++)
    {   
        if(asc)
        {
            if(array1[index1] > array2[index2]) result[i] = array2[index2++];
            else result[i] = array1[index1++];
        }
        else
        {
            if(array1[index1] > array2[index2]) result[i] = array2[index2++];
            else result[i] = array1[index1++];
        }
    }
    if(index1 == size1) memcpy(result + i, array2 + index2, sizeof(result) * (size2 - index2));
    if(index2 == size2) memcpy(result + i, array1 + index1, sizeof(result) * (size1 - index1));
    return result;
}

https://godbolt.org/z/PY8Ysv319

int cmpfunc (const void * a, const void * b) {
   return ( *(int*)a - *(int*)b );
}

void sort(int *array, size_t size)
{
    qsort(array, size, sizeof(array[1]), cmpfunc);
}

int *mergeArrays(const int *array1, const int *array2, size_t size1, size_t size2, int asc)
{
    int *result = malloc((size1 + size2) * sizeof(*array1));
    size_t index1 = 0, index2 = 0;
    size_t i;


    for(i = 0; i < size1 + size2 && index1 < size1 && index2 < size2; i++)
    {   
        if(asc)
        {
            if(array1[index1] > array2[index2]) result[i] = array2[index2++];
            else result[i] = array1[index1++];
        }
        else
        {
            if(array1[index1] > array2[index2]) result[i] = array2[index2++];
            else result[i] = array1[index1++];
        }
    }
    if(index1 == size1) memcpy(result + i, array2 + index2, sizeof(result) * (size2 - index2));
    if(index2 == size2) memcpy(result + i, array1 + index1, sizeof(result) * (size1 - index1));
    return result;
}

int main(void)
{
    srand(time(NULL));
    size_t size1 = rand() % 20, size2 = rand() % 20;
    int *mergedArray;

    if(size1 < 5) size1 += 5;
    if(size2 < 5) size2 += 5;

    int array1[size1], array2[size2];

    for(size_t i = 0; i < size1; i++)
        array1[i] = rand();
    for(size_t i = 0; i < size2; i++) 
        array2[i] = rand();
    sort(array1, size1);
    sort(array2, size2);

    for(size_t i = 0; i < size1; i++)
        printf("array1[%2zu] = %d\n", i, array1[i]);
    for(size_t i = 0; i < size2; i++)
        printf("array2[%2zu] = %d\n", i, array2[i]);

    mergedArray = mergeArrays(array1, array2, size1, size2, 1);
    for(size_t i = 0; i < size2 + size1; i++)
        printf("result[%2zu] = %d\n", i, mergedArray[i]);
}

您需要添加内存分配检查、NULL 指针检查等。

【讨论】:

    【解决方案2】:

    您的任务是实现归并排序算法的归并阶段。 mergesort 对数据集进行排序的复杂度为 O(N.log(N)),但每个合并阶段所花费的线性时间与合并集的长度成正比。

    这里是这个的伪代码:

    merge(array a, array b into array c)
        int i = 0, j = 0, k = 0;
        while (i < len(a) and j < len(b)) {
            if (a[i] <= b[j]) {
                c[k++] = a[i++];
            } else {
                c[k++] = b[j++];
            }
        }
        while (i < len(a)) {
            c[k++] = a[i++];
        }
        while (j < len(b)) {
            c[k++] = b[j++];
        }
    }
    

    复杂性是线性的,因为每个循环中的每个步骤都会将一个元素复制到c 数组中,总共有len(a) + len(b) 个步骤。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-08
      • 2015-08-26
      • 2011-04-05
      • 2016-05-18
      • 2014-08-02
      相关资源
      最近更新 更多