从另一个数组中删除数组的高效算法

时间:2011-09-18 16:41:13

标签: c# algorithm

我想知道是否有人知道更好(如更快)的算法/解决方案来解决我的问题:

在我的程序中,我有一个uint数组,我想从中删除另一个uint数组中包含的条目。但是,我不能使用集合的并集,因为我需要保留重复的值。措辞严厉的解释,但这个例子应该让它更清楚:

    uint[] array_1 = new uint[7] { 1, 1, 1, 2, 3, 4, 4};
    uint[] array_2 = new uint[4] { 1, 2, 3, 4 };

    uint[] result = array_1 .RemoveRange(array_2);
    // result should be: { 1, 1, 4 }

这是我目前最好的主意;但它很慢:

    public static uint[] RemoveRange(this uint[] source_array, uint[] entries_to_remove)
    {
        int current_source_length = source_array.Length;
        for (int i = 0; i < entries_to_remove.Length; i++)
        {
            for (int j = 0; j < current_source_length; j++)
            {
                if (entries_to_remove[i] == source_array[j])
                {
                    // Shifts the entries in the source_array.
                    Buffer.BlockCopy(source_array, (j + 1)* 4 , source_array, j * 4, (current_source_length - j) * 4);
                    current_source_length--;
                    break;
                }
            }
        }
        uint[] new_array = new uint[current_source_length];
        Buffer.BlockCopy(source_array, 0, new_array, 0, current_source_length * 4);
        return new_array;
    }

再说一次,有人能想出一个更聪明的方法来实现我的目标吗?

谢谢!

5 个答案:

答案 0 :(得分:2)

使用uint编号作为密钥使用Dictionary<uint,int>以及编号出现的次数如何?

var source = new Dictionary<uint,int>();
source.Add(1,3);
source.Add(2,1);
source.Add(3,1);
source.Add(4,2);

var remove = new uint[]{ 1, 2, 3, 4 };
for (int i = 0; i<remove.Length; i++) {
    int occurences;
    if (source.TryGet(remove[i], out occurences)) {    
        if (occurences>1) {
            source[remove[i]] = occurences-1;
        } else {
            source.Remove(remove[i]);
        }
    }
}

答案 1 :(得分:1)

根据我的理解,这可以做你想要的,它们的关键是引用次数的引用计数,然后使用剩余的引用计数(如果> 0)作为必须发出数字的次数:

public static uint[] RemoveRange(this uint[] source_array, uint[] entries_to_remove)
{
    var referenceCount = new Dictionary<uint, int>();
    foreach (uint n in source_array)
    {
        if (!referenceCount.ContainsKey(n))
            referenceCount[n] = 1;
        else
            referenceCount[n]++;
    }
    foreach (uint n in entries_to_remove)
    {
        if (referenceCount.ContainsKey(n))
            referenceCount[n]--;
    }
    return referenceCount.Where(x => x.Value > 0)
                         .Select(x => Enumerable.Repeat(x.Key, x.Value))
                         .SelectMany( x => x)
                         .ToArray();
}

答案 2 :(得分:0)

编辑:这对您没有帮助,因为您想保留重复项 我将它留在这里,供那些不想重复的人使用。

从第二个列表中创建HashSet<T>,然后使用哈希集的List<T>.RemoveAll方法调用Contains

var unwanted = new HashSet<uint(...);
list.RemoveAll(unwanted.Contains);

如果您不想就地删除它们,可以使用LINQ:

list.Except(unwanted);

Except将构建两个哈希集并一次返回一个项目(延迟执行0

答案 3 :(得分:0)

如果数组未排序,请对它们进行排序。将3个索引初始化为0.'s'(源)和'd'(dest)索引大数组A,'r'索引“toRemove”数组B.

   While r<B.length,
           While B[r] > A[s], A[d++]= A[s++].   
            If B[r]==A[s], s++.
             r++.
    Endwhile. 
    While s<A.length,  A[d++]= A[s++].
     A.length = d. 

这不需要额外的空间,并且在O(N)中运行(或者如果它们最初未排序则运行N lg N),与原始解决方案的N ^ 2 I相比。

答案 4 :(得分:-1)

您可以尝试在这里使用Linq,

var resultarray = array1.Except(array2);