寻找在C中实现的快速排序的整数数组交集/联合算法

时间:2012-01-17 05:34:14

标签: c algorithm intersection

我正在寻找实现快速排序整数数组交集/联合操作的C算法(或代码)。越快越好。

换句话说,在C中实现两个整数数组之间的并集和交集操作的有效方法是什么?

2 个答案:

答案 0 :(得分:2)

这不是最快的,但它展示了正确的算法。

//List is an output variable, it could be a 
//linked list or an array list or really anything you can add at end to.  

void intersection(int A[], int B[], int aLen, int bLen, List output)
{
    int i = 0;
    int j = 0;
    while (i < aLen && j < bLen)
    {
        a = A[i];
        b = B[j];
        if (a == b)
        {
            add(output, a);
            i++;
            j++;
        }
        else if (a < b)
        {
            i++;
        }
        else
        {
            j++;
        }
    }
}

以上算法为O(aLen + bLen)

你可以做得更好,特别是当遇到交叉超过2个列表的问题时。

对于交集,基本算法是迭代遍历所有已排序的列表,同时进行交叉。如果所有列表的头部匹配,则移动到所有列表中的下一个元素,并将头部添加到交叉点。如果没有,找到可见的最大元素,并尝试在所有其他列表中找到该元素。

在我的代码示例中,我只是继续迭代,但由于这些是排序列表,如果您希望A是数字1到10000而B是集合{7777},您还可以二进制搜索到正确的索引。使用多个列表查找最大元素意味着如果要正确执行,请使用堆。

如果你进行二进制搜索更改,最坏的情况将会达到O((aLen + bLen)*(lg(aLen + bLen)),但根据数据,你的平均情况可能会大幅改善。

当将多个集合交叉时,堆更改将是必要的,因为上述算法变为O(numLists *(所有列表中的元素总数))并且可以减少为O(lg(numLists)*(总数)所有列表中的元素))

void union(int A[], int B[], int aLen, int bLen, List output)
    {
        int i = 0;
        int j = 0;
        while (i < aLen && j < bLen)
        {
            a = A[i];
            b = B[j];
            if (a == b)
            {
                add(output, a);
                i++;
                j++;
            }
            else if (a < b)
            {
                add(output, a);
                i++;
            }
            else
            {
                add(output, b);
                j++;
            }
        }
        //Add any leftovers.  
        for (;i < aLen; i++)
        {
            add(output, A[i]);
        }
        for (;j < bLen; j++)
        {
            add(output, B[j]);
        }
    }

Union基本上是相同的算法,除了你总是添加每个元素,因此,在二进制搜索中没有任何意义。将其扩展到多个列表可以使用实现查看的堆来完成,基本规则是始终添加最小元素,并在前面具有该元素的每个列表中前进。

答案 1 :(得分:2)

假设这些是实际的(因为具有重复项的数组上的交集最多是有问题的),以下代码可能有所帮助。

首先,一些必要的标题:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

和一些强制性文件:

// Description:
//     Will give back the union or intersection of two sorted integer
//         sets (only one copy of each element).
//     Caller responsible for freeing memory.
// Input:
//     Union ('u') or intersection (anything else).
//     arr1 and arr2 are pointers to the arrays.
//     sz1 and sz2 are the number of integers in each array.
//     psz3 as the pointer to the variable to receive the
//         size of the returned array.
// Returns:
//     A pointer to the array, or NULL if malloc failed.
//     Memory allocated even if result set is empty, so
//        NULL return indicates ONLY malloc failure.
//     psz3 receives the size of that array, which is
//        zero for an empty set.

然后功能正确:

int *arrUnionIntersect ( int type,
    int *arr1, size_t sz1,
    int *arr2, size_t sz2,
    size_t *psz3
) {
    int *arr3, *ptr1, *ptr2;

    *psz3 = 0;

    // If result will be empty, just return dummy block.

    if ((sz1 == 0) && (sz2 == 0))
        return malloc (1);

    // Otherwise allocate space for real result.

    if (type == 'u')
        arr3 = malloc ((sz1 + sz2) * sizeof (*arr1));
    else
        if (sz1 > sz2)
            arr3 = malloc (sz1 * sizeof (*arr1));
        else
            arr3 = malloc (sz2 * sizeof (*arr1));
    if (arr3 == NULL)
        return NULL;

直到那里,它主要是初始化功能。此跟随位遍历两个输入集,选择添加到结果中的内容。这是最好的(在我看来)作为三个阶段,第一个是当两个输入集仍然有一些元素时选择一个元素。请注意这里对于联合和交叉点的不同行为,特别是交叉点只有在两个输入集中添加了元素:

    // Set up pointers for input processing.

    ptr1 = arr1;
    ptr2 = arr2;

    // Phase A: select appropriate number from either, when both
    //    have remaining elements.

    while ((sz1 > 0) && (sz2 > 0)) {
        if (*ptr1 == *ptr2) {
            arr3[(*psz3)++] = *ptr1++;
            sz1--;
            ptr2++;
            sz2--;
            continue;
        }

        // We don't copy for intersect where elements are different.

        if (*ptr1 < *ptr2) {
            if (type == 'u')
                arr3[(*psz3)++] = *ptr1;
            ptr1++;
            sz1--;
            continue;
        }

        if (type == 'u')
            arr3[(*psz3)++] = *ptr2;
        ptr2++;
        sz2--;
    }

其他两个阶段(其中只有一个阶段将针对工会运行,而没有针对交叉点运行),只是从非空输入集中获取剩余的项目:

    // Phase B and C are only for unions.

    if (type == 'u') {
        // Phase B: process rest of arr1 if arr2 ran out.

        while (sz1 > 0) {
            arr3[*psz3++] = *ptr1++;
            sz1--;
        }

        // Phase C: process rest of arr2 if arr1 ran out.

        while (sz2 > 0) {
            arr3[*psz3++] = *ptr2++;
            sz2--;
        }
    }

    // Return the union.

    return arr3;
}

测试程序:

int main (void) {
    int x1[] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};
    int x2[] = {2, 3, 5, 7, 11, 13, 17, 19};
    size_t i, sz3;
    int *x3;

    x3 = arrUnionIntersect ('u', x1, sizeof(x1)/sizeof(*x1),
        x2, sizeof(x2)/sizeof(*x2), &sz3);
    printf ("union =");
    for (i = 0; i < sz3; i++)
        printf (" %d", x3[i]);
    free (x3);
    printf ("\n");

    x3 = arrUnionIntersect ('i', x1, sizeof(x1)/sizeof(*x1),
        x2, sizeof(x2)/sizeof(*x2), &sz3);
    printf ("intersection =");
    for (i = 0; i < sz3; i++)
        printf (" %d", x3[i]);
    free (x3);
    printf ("\n");

    return 0;
}

及其输出,如预期:

union = 1 2 3 5 7 9 11 13 15 17 19
intersection = 3 5 7 11 13 17 19