合并两个排序数组而无重复项的C#算法

合并两个排序数组而无重复项的C#算法,c#,array-merge,C#,Array Merge,我需要写一个算法,将两个排序的整数数组合并成一个没有重复的数组 我设法合并了它们,但我不确定如何从最终数组中删除重复项,而且用两个数组的总和启动合并数组似乎也不正确,因为重复项的默认值为int public static int[] MergeArrays(int[] firstArray, int[] secondArray) { var firstArrayLength = firstArray.Length; var secondArrayLenght = secondAr

我需要写一个算法,将两个排序的整数数组合并成一个没有重复的数组

我设法合并了它们,但我不确定如何从最终数组中删除重复项,而且用两个数组的总和启动合并数组似乎也不正确,因为重复项的默认值为
int

public static int[] MergeArrays(int[] firstArray, int[] secondArray)
{
    var firstArrayLength = firstArray.Length;
    var secondArrayLenght = secondArray.Length;
    var mergedArray = new int[firstArrayLength + secondArrayLenght];
    var i = 0;
    var j = 0;
    var k = 0;

    while (i < firstArrayLength && j < secondArrayLenght)
    {
        if (firstArray[i] < secondArray[j])
        {
            mergedArray[k] = firstArray[i];
            i++;
        }
        else
        {
            mergedArray[k] = secondArray[j];
            j++;
        }

        k++;
    }

    while (i < firstArrayLength)
    {
        mergedArray[k++] = firstArray[i++];
    }

    while (j < secondArrayLenght)
    {
        mergedArray[k++] = secondArray[j++];
    }

    return mergedArray;
}
公共静态int[]mergeArray(int[]firstArray,int[]secondArray)
{
var firstArrayLength=firstArray.Length;
var secondArrayLenght=secondArray.Length;
var mergedArray=新整数[firstArrayLength+SecondArrayLength];
var i=0;
var j=0;
var k=0;
而(i

如果不使用LINQ或特定于数组的方法,您将如何做到这一点呢?

您几乎做到了。合并算法的主要部分已经开始工作

你只剩下两件事要做:

  • 返回正确大小的数组。由于您已经在
    k
    变量中跟踪合并数组的大小,因此可以使用
    array.resize(ref mergedArray,k)来调整数组的大小

  • 您想对阵列进行重复数据消除。由于要按排序顺序维护合并数组,因此可以在向数组中添加新值之前检查重复值,如果该值已经位于合并数组的末尾,则只需跳过添加即可。请注意,必须小心处理合并数组当前为空的情况

  • 为了尽可能多地保留原始代码,这里有一个解决方案

    它引入了一个
    candidate
    值,我们使用该值检查将插入结果数组的值是否已经在该数组中。在返回数组之前,它还会将数组大小调整为正确的大小:

    public static int[] MergeArrays(int[] firstArray, int[] secondArray)
    {
        var firstArrayLength  = firstArray.Length;
        var secondArrayLength = secondArray.Length;
        var mergedArray       = new int[firstArrayLength + secondArrayLength];
        var i                 = 0;
        var j                 = 0;
        var k                 = 0;
        int candidate;
    
        while (i < firstArrayLength && j < secondArrayLength)
        {
            if (firstArray[i] < secondArray[j])
            {
                candidate = firstArray[i];
                i++;
            }
            else
            {
                candidate = secondArray[j];
                j++;
            }
    
            if (k == 0 || mergedArray[k-1] != candidate)
            {
                mergedArray[k] = candidate;
                k++;
            }
        }
    
        while (i < firstArrayLength)
        {
            candidate = firstArray[i++];
    
            if (k == 0 || mergedArray[k-1] != candidate)
            {
                mergedArray[k] = candidate;
                k++;
            }
        }
    
        while (j < secondArrayLength)
        {
            candidate = secondArray[j++];
    
            if (k == 0 || mergedArray[k-1] != candidate)
            {
                mergedArray[k] = candidate;
                k++;
            }
        }
    
        Array.Resize(ref mergedArray, k);
        return mergedArray;
    }
    
    这或多或少与
    Array.Resize()
    的作用相同-它创建一个正确大小的新数组,并将源数组的元素复制到其中

    您可能会认为您可以使用Linq来实现这一点

    var result = array1.Union(array2).ToArray();
    
    这样做的问题是,结果没有按给定顺序排序

    int[] array1 = {1, 3, 5, 7, 9};
    int[] array2 = {2, 4, 6, 8, 10};
    int[] merged = array1.Union(array2).ToArray();
    
    Console.Write(string.Join(", ", merged));
    
    输出为未排序的
    1,3,5,7,9,2,4,6,8,10

    您必须添加一个排序步骤:

    int[] merged = array1.Union(array2).OrderBy(i => i).ToArray();
    
    但是现在我们把一个
    O(N)
    算法变成了
    O(N.Log(N))
    算法,因为
    OrderBy()
    O(N.Log(N))
    。这是否真的是一个问题取决于你的情况

    还请注意,在返回数组之前,也可以通过返回
    mergedArray.Distinct().ToArray()
    ,从数组中删除重复项,但这会引入不必要的额外数据副本以及添加O(N)操作。如果这是家庭作业,那可能不是导师想要的

    最后,您可能会注意到有一些重复的代码检查重复的值。您可以将该代码放入本地函数中以避免重复。如果这样做,最终的方法可能会如下所示:

    public static int[] MergeArrays(int[] firstArray, int[] secondArray)
    {
        var firstArrayLength  = firstArray.Length;
        var secondArrayLength = secondArray.Length;
        var mergedArray       = new int[firstArrayLength + secondArrayLength];
        var i                 = 0;
        var j                 = 0;
        var k                 = 0;
    
        void addIfNotDupe(int candidate) // Local function.
        {
            if (k != 0 && mergedArray[k - 1] == candidate)
                return;
    
            mergedArray[k++] = candidate;
        }
    
        while (i < firstArrayLength && j < secondArrayLength)
        {
            addIfNotDupe(firstArray[i] < secondArray[j] ? firstArray[i++] : secondArray[j++]);
        }
    
        while (i < firstArrayLength)
        {
            addIfNotDupe(firstArray[i++]);
        }
    
        while (j < secondArrayLength)
        {
            addIfNotDupe(secondArray[j++]);
        }
    
        Array.Resize(ref mergedArray, k);
        return mergedArray;
    }
    
    公共静态int[]mergeArray(int[]firstArray,int[]secondArray)
    {
    var firstArrayLength=firstArray.Length;
    var secondArrayLength=secondArray.Length;
    var mergedArray=新整数[firstArrayLength+secondArrayLength];
    var i=0;
    var j=0;
    var k=0;
    void addifynotdupe(int候选)//局部函数。
    {
    if(k!=0&&mergedArray[k-1]==候选)
    返回;
    mergedArray[k++]=候选人;
    }
    而(i
    如果它们都已排序,那么您需要做的就是检查
    mergedArray[k-1]
    (其中k>0)是否不等于您尝试插入的值?显然,在插入新值之前,不要递增
    k
    。因为您不知道预先有多少个重复项,所以您必须分两次执行此操作(一次计数重复项,一次合并),以便正确调整结果数组的大小,或使用列表进行输出(如果确实需要数组中的结果,则在合并后转换为数组)。或者在合并结束时调整最终数组的大小(效率较低,因为它通常会使数组过大,但除非数组很大,否则这可能不会成为问题)。我恐怕不太清楚您在这里问什么。这个问题似乎是一个家庭作业问题,因为在实际代码中处理这个问题的通常方法是使用LINQ。您所要做的就是在迭代已有的合并排序时排除新值,而这些值与您刚刚编写的相同(因为重复项必须分组在一起)。你试过了吗?你试过什么了吗?你具体需要什么帮助?@PeterDuniho“在实际代码中实现这一点的通常方法是使用LINQ”。好吧,这取决于。给定排序的输入,合并应该是
    O(N)
    ,但使用LINQ可能会使其
    O(N.Log(N))
    ,因此如果数组很大,这可能是一个问题。如果OP首先对数组进行排序
    public static int[] MergeArrays(int[] firstArray, int[] secondArray)
    {
        var firstArrayLength  = firstArray.Length;
        var secondArrayLength = secondArray.Length;
        var mergedArray       = new int[firstArrayLength + secondArrayLength];
        var i                 = 0;
        var j                 = 0;
        var k                 = 0;
    
        void addIfNotDupe(int candidate) // Local function.
        {
            if (k != 0 && mergedArray[k - 1] == candidate)
                return;
    
            mergedArray[k++] = candidate;
        }
    
        while (i < firstArrayLength && j < secondArrayLength)
        {
            addIfNotDupe(firstArray[i] < secondArray[j] ? firstArray[i++] : secondArray[j++]);
        }
    
        while (i < firstArrayLength)
        {
            addIfNotDupe(firstArray[i++]);
        }
    
        while (j < secondArrayLength)
        {
            addIfNotDupe(secondArray[j++]);
        }
    
        Array.Resize(ref mergedArray, k);
        return mergedArray;
    }