C# 在指定索引处从列表中删除元素的有效方法
假设我有一个N个元素的列表(C# 在指定索引处从列表中删除元素的有效方法,c#,linq,optimization,C#,Linq,Optimization,假设我有一个N个元素的列表(list1) 我有一张M的清单 或 bool[]shouldwesaybye=newbool[List1.Count]; 对于(var i=0;i您的最佳选择可能是坚持使用for循环。假设list2按相反顺序排序,它们将是最快的(例如) 您可以尝试使用RemoveAll也许 list1.RemoveAll(l1item => list2.Contains(list1.IndexOf(l1item))); 缺点是,虽然这看起来更干净、更直接,但其基础实际上相当复
list1
)
我有一张M的清单
或
bool[]shouldwesaybye=newbool[List1.Count];
对于(var i=0;i您的最佳选择可能是坚持使用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;
}
这将执行一个数组。每次删除一个项目时复制,而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]);
}
for(int i=0;i
这不应依赖于列表顺序。列表。如果未超过容量,则添加实际上是O(1)
。
下面是我想到的,应该在O(n)
:
List resultList=新列表(list1.Count);//容量大!
int curIdx=list1.Count-1;//从list1的末尾开始
//假设列表2是按降序排序的
list2.Add(-1);//添加一个最终的-1索引,以使下面的代码更好
foreach(列表2中的int targetIdx)
{
while(curIdx>targetIdx)
{
Add(list1[curIdx]);//两个操作都是O(1)
库里克斯--;
}
库里克斯--;
}
//结果列表是颠倒的
澄清:
清单1:[10,11,12,13,14]
清单2:[1,3]
通缉结果:[10,12,14]
我们希望list2
按desc排序,并在其中添加一个final-1:
清单2:[3,1,-1]
最终的结果实际上是相反的结果。之后可以在O(n)
中反转列表,这样就不会改变总体复杂性,但是您可以进一步优化代码,这样最终列表的顺序实际上是正确的(家庭作业!)如果订购了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;
var exceptIndex=0;
var newList=新列表();
for(var i=0;i
优化是乐趣的根源,IMHO。过早与否!那么我们可以假设list2
已经被排序了吗?我们可以假设很多关于list2
的事情。我们可以假设它被排序了,或者它是一个HashSet
。我可以控制list2
以及它的创建方式,所以我可以对它做很多有趣的事情。Bu我不同意你的看法,优化很酷!你为什么要交叉?保留新创建的列表。@中间是因为起初它是一个例外,而不是一个中间部分,然后我就不知道我在做什么,但你是对的(尽管如此,这对我来说似乎不是最好的方法)使用LinkedList
而不是List
的第一种方法是O(n)
,因为删除操作变为O(1)非常酷的回答,非常模糊。你介意解释一下为什么它会被执行吗?因为删除元素会迫使列表在它之后移动每个元素。生成一个新的列表要快得多。@Fylax这基本上是第二个解决方案的LINQ版本,删除了不必要的,除了@InBetween我弄明白了这是什么(你已经指出有一个超链接实际上毫无意义,我同意你的观点),但我仍然不清楚为什么它更高效。正如我在问题中所解释的,保存ns是一个巨大的改进,根据我的经验,LINQ往往比a for Loop慢。编辑后,没有理由编写新的答案。我的想法只是更改列表2的结构,使其成为您所称的shallWeSayGoodby
m是开始。似乎是执行此任务的最有效和最干净的方式,尽管你在foreach中看到了一段时间-我们实际上只在n个元素上循环使用反向for循环将使我的最终列表按正确的顺序排列,我想。但最终顺序其实并不是什么大问题,正如我之前所说的,我做了很多随机访问(注意随机性),所以它只会增加熵。另外,我知道容量成本。我在代码中尝试了所有可能的技巧来提高性能:)无论如何,感谢大家,我找到了一个非常好和有效的解决方案(虽然它会大量改变列表2)@Fylax,愿意分享你的解决方案吗?当然,这是在我的计划中。谢谢。最后我意识到我将你的解决方案合并到一个新的解决方案中的方法:将list2作为一个布尔数组,表示哪些索引应该保留,哪些不应该保留。不过,这与我最初的问题相去甚远。
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;
}
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++;
}
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]);
}
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
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;