【问题标题】:How to find intersection of two arrays (Optimal solution)如何找到两个数组的交集(最佳解决方案)
【发布时间】:2015-11-06 06:28:11
【问题描述】:

我正在为两个数组 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) 当我为第三个数组分配大小时,我分配两个数组中要比较的最小值,如果交集元素太小,那么我们 不必要地分配了额外的内存。如何解决这个问题?

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

【问题讨论】:

  • 使用使用 hashset 的 linq Intersect。您可以查看其实现并编写自己的。
  • 与当前解决方案相比,它将如何改进。请写在下面作为答案详细解释,这样如果你对我有用,我什至可以标记你。
  • @struggling 它不会改进这个算法。但是,它不会使情况恶化。那么,为什么不使用单行就绪的解决方案,而不是重新发明轮子并实施自己的解决方案。
  • @struggling HashSet 是优化的集合。访问、添加和删除项目的时间复杂度为O(1)。意味着您的收藏有多大并不重要,性能几乎相同。 Hashset 也不包含重复项。因为它使用 hashkeys 来访问它的元素。因此它不能包含重复的键,并且每个键必须是唯一的。 Intersect 使用这个哈希集来执行交集。时间复杂度为O(m+n)
  • @M.kazemAkhgary ,我也知道它的复杂度为 O(1) 并且是最优的。但是你能给我算法(通过下面的ansering)吗?真的很有帮助

标签: c# arrays algorithm data-structures


【解决方案1】:

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

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

如果您想知道我所说的合并检查是什么意思,它只是并排处理两个(排序的)数组。

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

如果它们不同,则没有相交点,您可以将具有最低值的数组前进,直到它发生变化。


举例来说,这里有一些 Python 代码(ideal 伪代码语言)实现了这样的解决方案。数组 a 包含 0 到 18 之间的所有三的倍数(以任意顺序并包括重复),而数组 b 包含该范围内的所有偶数(同样,有一些重复并“随机”排序)。

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]

【讨论】:

  • @padiablo,哇,这是个好主意,但它仍然很复杂,感谢您提供解决方案。问题是当您分配第三个数组时,您必须为其提供大小,如果您甚至为其提供等于两个数组 A 和 B 中最小值的大小,那么有时也会不必要地分配大部分内存。你也有解决方案吗?
  • 还有一个问题,如果我在排序后说 A=[0,1,1,2,5,7] 和 B=[0,1,2,5] 怎么办?现在在第三次检查期间,算法将失败。
  • @struggling,重复不会导致问题,假设您“推进具有最低值的数组,直到它改变”如所述。我添加了一些示例代码作为概念证明。
  • 您的解决方案是完美的解决方案,我的实现中只有一个问题未得到解答(我在问题末尾指出 (1) 的问题),我的意思是创建了额外的内存分配在第三个数组中(因为我们在两个数组中找到了最小大小的数组,并且我们分配了第三个具有相同大小的数组,所以在您的实现中情况也是如此)。在此答案之后,我会将您标记为答案,因为我所有的疑问和问题都很清楚:) 谢谢
  • @struggling,是的,由于最终的相交数组可能比所需的大,因此存在潜在的浪费。但是,如果不进行两次传递或在添加内容时动态扩展数组,它仍然是最好的方法。你知道min(sz(a),sz(b)) 将是相交所需的最大大小,所以只需分配它。如果结果太大,您可以在完成后将其缩小。
【解决方案2】:

也许我没有理解你的正确,但你为什么不使用以下内容;

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())];

【讨论】:

    【解决方案3】:

    您可以使用 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();
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      • 2011-04-12
      • 2011-12-17
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多