如何找到两个数组的交集(最优解)

时间:2015-11-06 06:28:11

标签: c# arrays algorithm data-structures

我正在为两个阵列A和B的交集编写算法,我想要一个空间复杂度和时间复杂度方面的优化解决方案。 我已经编写了算法并且它工作正常,但我想知道是否有更优化的解决方案然后存在或者如果有人可以提供给我。

我的工作是:

(1)找到两个中最小的数组。

(2)新数组的大小分配大小等于较小的数组

(3)从较小的数组我去和比较大数组中的每个元素如果它存在一个,我得到它在第三个数组“C”并在那里打破(因为我们需要找到交集,即使它之后重复100次     我们不关心我们只有一个存在足以放入第三个数组)。同时,我们还必须检查较小数组中与较大数组中的所有元素进行比较的元素是否已存在于第三个数组中,例如A=[0,1,1], B[0,1,2,3]。  现在我们从A的第一个元素开始,它存在于数组B中,我们将其保存在C[0]中,然后转到第二个,现在C是[0,1],并且在下一步中我们再次进行1比较,其中我们已经比较过了。因此,我们必须检查数组C中是否已存在要比较的元素,然后我们不再检查它。

(4)我们将找到的元素存储在C(第三个数组)中并打印出来。

我的完整工作代码是:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] aar1 = { 0, 1, 1, 7, 2, 6, 3, 9, 11, 2, 2,3,3,3,3,3,1 };
            int[] aar2 = { 0, 1, 2, 3, 4, 5, 6, 11, 11, 1, 1, 1, 1 };
            int[] arr3 = findIntersection(aar1, aar2);
            Console.WriteLine("the array is : " + arr3);
            Console.ReadKey();
        }

        private static int[] findIntersection(int[] aar1, int[] aar2)
        {
            int[] arr3 = { 0 };
            if (aar1.Count() < aar2.Count())
            {
                int counter = 0;
                arr3 = new int[aar1.Count()];
                foreach (int var1 in aar1)
                {
                    if (!checkifInThirdArray(var1, arr3))
                    {
                        foreach (int var2 in aar2)
                        {
                            if (var1 == var2)
                            {
                                arr3[counter++] = var1;
                                break;
                            }
                        }
                    }
                }
            }
            else
            {
                int counter = 0;
                arr3 = new int[aar1.Count()];
                foreach (int var2 in aar2)
                {
                    if (!checkifInThirdArray(var2, arr3))
                    {
                        foreach (int var1 in aar1)
                        {
                            if (var2 == var1)
                            {
                                arr3[counter++] = var2;
                                break;
                            }
                        }
                    }
                }
            }
            return arr3;
        }

        private static bool checkifInThirdArray(int var1, int[] arr3)
        {
            bool flag = false;
            if (arr3 != null)
            {
                foreach (int arr in arr3)
                {
                    if (arr == var1) { flag = true; break; }
                }
            }
            return flag;
        }
    }
}

我发现的一个空间复杂问题是(如果您发现任何问题,如果您通过解决方案告诉我,我会非常感激):

(1)当我将大小分配给第三个数组时,我分配要比较的两个数组的Min,如果交集元素太少,那么我们  不必要地分配了额外的内存。如何解决这个问题?

请注意,我不必使用任何内置函数,如intersection()或任何其他函数。

3 个答案:

答案 0 :(得分:2)

听起来你的解决方案是O(n2),对于一个数组中的每个元素,你可能需要处理另一个元素中的每个元素(在交集是空集的情况下) 。您应该知道C#实际上具有设施来查找数组的交集,但是,如果您希望实现自己的数组,请继续阅读。

您可能更好地对两个数组进行排序(如果允许,则为原地进行单独的集合),然后对两者进行合并检查以构建另一个数组。排序可以是O(n log n),合并检查可以是O(n)

如果您想知道合并检查的含义,它只是并排处理两个(已排序的)数组。

如果两者中的第一个元素匹配,则您有一个交叉点,您应该存储该值并前进两个列表,直到下一个值不同。

如果它们不同,则没有交叉点,您可以使用最低值推进数组,直到它发生变化。

举例来说,这是Python中的一些代码(理想的伪代码语言),它实现了这样的解决方案。数组a包含0到18之间的所有三的倍数(以任意顺序并包括重复),而数组b包含该范围内的所有偶数(同样,有一些重复和有序& #34;随机&#34;。)

a = [0,3,15,3,9,6,12,15,18,6]
b = [10,0,2,12,4,6,18,8,16,10,12,6,14,16]

# Copy and sort.

a2 = a; a2.sort()
b2 = b; b2.sort()

# Initial pointers and results for merge check.

ap = 0
bp = 0
c = []

# Continue until either array is exhausted.

while ap < len(a2) and bp < len(b2):
    # Check for intersect or which list has lowest value.

    if a2[ap] == b2[bp]:
        # Intersect, save, advance both lists to next number.

        val = a2[ap]
        c.append(val)
        while ap < len(a2) and a2[ap] == val:
            ap += 1
        while bp < len(b2) and b2[bp] == val:
            bp += 1
    elif a2[ap] < b2[bp]:
        # A has smallest, advance it to next number.

        val = a2[ap]
        while ap < len(a2) and a2[ap] == val:
            ap += 1
    else:
        # B has smallest, advance it to next number.

        val = b2[bp]
        while bp < len(b2) and b2[bp] == val:
            bp += 1

print(c)

如果你运行它,你会看到两个数组之间形成的交叉列表:

[0, 6, 12, 18]

答案 1 :(得分:0)

也许我不理解你,但你为什么不使用以下内容;

int[] aar1 = { 0, 1, 1, 7, 2, 6, 3, 9, 11, 2, 2,3,3,3,3,3,1 };
int[] aar2 = { 0, 1, 2, 3, 4, 5, 6, 11, 11, 1, 1, 1, 1 };

aarResult = aar1.Intersect(aar2).ToArray();

这将导致数组只需要所需的空间并与数组相交。您还可以按如下方式初始化aarResult以获得最小数组大小:

int[] aarResult = new int[Math.Min(aar1.Count(), aar2.Count())];

答案 2 :(得分:0)

您可以使用LINQ Intersect方法。它使用散列并适用于线性O(N+M),它比您的算法更快:

int[] aar1 = { 0, 1, 1, 7, 2, 6, 3, 9, 11, 2, 2, 3, 3, 3, 3, 3, 1 };
int[] aar2 = { 0, 1, 2, 3, 4, 5, 6, 11, 11, 1, 1, 1, 1 };

int[] result = aar1.Intersect(aar2).ToArray();

它还将解决您不必要的分配项问题,因为它将创建一个确切答案大小的数组。