在指定索引处从列表中删除元素的有效方法

时间:2016-12-09 19:01:43

标签: c# linq optimization

假设我有一个N个元素列表(list1)。

然后我有一个M< = N元素的列表,其中包含我要从其他列表中删除的所有元素索引(list2)。

我想以最有效的方式使用list1索引相应删除list2上的元素。

第一种方法是通过for循环:

for (int i = 0; i < list2.Count; i++)
    list1.RemoveAt(list2[i]);

这项工作非常好(我当然需要list2订购)但在最坏的情况下具有O(M * N)复杂度(M次迭代,RemoveAt为O(n))

另一个解决方案是创建一个临时列表,用 not 删除的元素填充它,然后使用LINQ intersect方法

List<T> tempList = new List<T>();
for (int i = 0; i < list1.Count; i++)
    if (list2.Contains(i))
        tempList.Add(list1[i]);
list1.Intersect(tempList);

虽然起初我对Intersect的O(N + M)复杂性感到兴奋,但我最终意识到我需要首先进行N次迭代来填充tempList,然后任何优势都会丢失(我们假设list2是HashSet,在第二种情况下,我不关心排序,只是关于O(1)Contains

经过一番挖掘后,我仍无法找到一种方法来执行RemoveAll类似的方法,该方法会根据list1中存储的值从list2中删除所有元素。< / p>

是否有机会尽可能提高效率?

PS:对于所有会认为“过早优化是所有邪恶的根源”的人,请考虑我的代码实际上工作正常但是,因为我正在处理一个严格的时间依赖问题,每次迭代节省几ns (我将有大约150k次迭代)可以带来显着的改进。

编辑:正如@InBetween正确指出的那样,在第二个解决方案上使用Intersect实际上是无用的,减少它的复杂性就会降低。

4 个答案:

答案 0 :(得分:2)

如果订购了list2,那么只需使用您优化的第二个解决方案:

var exceptIndex = 0;
var newList = new List<T>();

for (var i = 0; i < list1.Length; i++)
{
    if (i != list2[exceptIndex]) newList.Add(list1[i]);
    else exceptIndex++
}

return newList;

答案 1 :(得分:1)

List1 = Enumerable.Range(0,List1.Count).Except(List2).Select(i=>List1[i]).ToList()

bool[] shouldWeSayGoodbye = new bool[List1.Count];
for(var i=0;i<List2.Count;i++){
   shouldWeSayGoodbye[List2[i]]=true;
}

typeof_list1 List3 = new List<typeof_list1>();
for(var i=0;i<List1.Count;i++){
   if(!shouldWeSayGoodbye[i]){
      List3.Add(List1[i])
   }
}

一些测试显示,循环至少击败Linq x4次。

答案 2 :(得分:1)

如果未超过容量,

List<T>.Add实际上是O(1)。 这是我想出的,应该去O(n)

List<T> resultList = new List<T>(list1.Count); // high capacity!
int curIdx = list1.Count - 1; // start at the end of list1

// assumes list2 is sorted descendingly
list2.Add(-1); // add a final -1 index to make following code nicer

foreach (int targetIdx in list2)
{
    while (curIdx > targetIdx)
    {
        resultList.Add(list1[curIdx]); // both operations are O(1)
        curIdx--;
    }
    curIdx--;
}

// resultList is reversed

澄清:

  

list1:[10,11,12,13,14]
  list2:[1,3]   想要的结果:[10,12,14]

我们希望将list2排序为desc,并为其添加最后的-1:

  

list2:[3,1,-1]

最终的结果实际上是反向所需的结果。之后反转列表可以在O(n)中完成,因此它不会改变整体复杂性,但是您可以进一步优化代码,以便最终列表实际处于正确的顺序(主页工作!)

答案 3 :(得分:0)

您最好的选择可能是坚持使用for循环。它们将是最快的,假设list2按相反顺序排序(对于您的示例)。

您可以尝试使用RemoveAll或许......

list1.RemoveAll(l1item => list2.Contains(list1.IndexOf(l1item)));

缺点是,虽然这看起来更清晰,更直接,但它的基础实际上相当复杂。

public int RemoveAll(Predicate<T> match)
{
    if (match == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    int num = 0;
    while (num < this._size && !match(this._items[num]))
    {
        num++;
    }
    if (num >= this._size)
    {
        return 0;
    }
    int i = num + 1;
    while (i < this._size)
    {
        while (i < this._size && match(this._items[i]))
        {
            i++;
        }
        if (i < this._size)
        {
            this._items[num++] = this._items[i++];
        }
    }
    Array.Clear(this._items, num, this._size - num);
    int result = this._size - num;
    this._size = num;
    this._version++;
    return result;
}

另一方面,查看您当前使用的RemoveAt

public void RemoveAt(int index)
{
    if (index >= this._size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException();
    }
    this._size--;
    if (index < this._size)
    {
        Array.Copy(this._items, index + 1, this._items, index, this._size - index);
    }
    this._items[this._size] = default(T);
    this._version++;
}

每次删除某个项目时Array.Copy执行RemoveAll,而for (int i = 0; i < list1.Count; i++) { bool exists = false; for (int j = 0; j < list2.Count; j++) if (i == list2[j]) { exists = true; break; } if (!exists) newList.Add(list1[i]); } 通过项目进行处理,并将已编入索引的项目移至&#34;已删除&#34;项目

您可以对这两者进行一些简单的基准测试,看看哪个更好。

使用以下内容可能会给您带来最佳效果:

<input id="search" type="submit" value="Search Google Images">
<input id="keyword" type="text" value="" placeholder="Type text here">

<script>

$(document).ready(function(){

$("#search").click(function(){
       window.open("https://www.google.de/search?q="+document.getElementById('keyword').value+"&biw=1678&bih=790&source=lnms&tbm=isch&sa=X&ved=0ahUKEwjNzbLf6OfQAhVD6RQKHZp5BRsQ_AUIBigB&dpr=1.09")
});
});

</script>

这不应该依赖于列表顺序。