性能:在堆栈上调用反向更快或将项目弹出到数组中更快

时间:2009-06-18 18:34:45

标签: c# performance

我想知道哪种实施会有更好的表现:

我需要清除堆叠中的所有项目,除了头部的前10个。

然后必须按照原始顺序将这10个放入堆栈。

我想到了两种方法

第一个:

FilterRecord[] records = new FilterRecord[10];

       for (int i = 0; i < records.Length; i++)
       {
           records[i] = _InternalCollection.Pop();
       }

       _InternalCollection.Clear();

       for (int i = records.Length - 1; i >= 0; i--)
       {
           _InternalCollection.Push(records[i]);
       }

第二个:

int count = _InternalCollection.Count - 10;
               _InternalCollection.Reverse();


               for (int i = 0; i < count; i++)
               {
                   _InternalCollection.Pop();
               }

               _InternalCollection.Reverse();

欢迎提供任何帮助或指导或其他建议。

10 个答案:

答案 0 :(得分:2)

你的第一个算法有两个for循环,所以O(2N)。你的第二个算法有一个for循环,但我们必须假设内部Reverse()是一个O(N)操作,所以它的O(3N)。

我不确定第一个算法中的Clear()调用是O(N)还是O(C)。

答案 1 :(得分:2)

这取决于。你可以做两件事:

  • 通过运行一对来测试它 一百次迭代(少或多 视大小而定)
  • 看看他们的实施

分析实现堆栈的方法(概念上),使用反向将是最慢的,因为它必须弹出堆栈的所有内容,然后将它们弹回,相反订购。如果在内部,它只是选择一个不同的起始索引,从哪里开始弹出,它可能会更快。

无论哪种方式,简单地说,使用Reverse()似乎效率低,因为你执行了两次操作。

答案 2 :(得分:1)

如果堆栈中的项目很少,那么差异就会很小,不值得优化。

如果堆栈中有很多项目,这将是最快的:

FilterRecord[] records = new FilterRecord[10];

for (int i = 0; i < records.Length; i++) {
   records[i] = _InternalCollection.Pop();
}

_InternalCollection = new Stack<FilterRecord>();

for (int i = records.Length - 1; i >= 0; i--) {
   _InternalCollection.Push(records[i]);
}

即。在获得要保留的项目后,只需丢弃当前堆栈,让垃圾收集器处理它。如果使用Clear方法,那将从堆栈中删除引用,但它将保持其容量。

由于您只触摸堆栈中的十个项目而不管它们有多少,因此这是O(1)操作而不是O(n)操作。

(请注意,在声明数组时使用数组的大小,而不是最高的索引。)

答案 3 :(得分:0)

这取决于你假设Reverse()方法没有做到这一点。只有两种方法可以知道:1。运行你的代码并告诉我们2.)查找Reverse()的实现。

答案 4 :(得分:0)

通常很难说出这些类型的东西。只需制作一个疯狂的大堆栈,调用它们并计时。那你就知道了。

答案 5 :(得分:0)

我建议看看Reverse()的实际实现 - 你可能会发现.Net Reflector对此有用。

答案 6 :(得分:0)

我认为避免从堆栈中弹出项目会比你建议的两种方法之间的替代方案更能提高代码的性能。我建议使用linq分配一个新堆栈,其中包含堆栈中最后一项的内容,如下所示:

var newStack = new Stack<FilterRecord>( 
   _InternalCollection.Take( 10 )
                      .Reverse() );

_InternalCollection = newStack;

如果你没有使用泛型,你可以对内置的迭代器做同样的事情:

var iterator = _InternalCollection.GetEnumerator();
int i = 9;
while( iterator.MoveNext() && i-- >= 0 )
{
   newStack.Push( iterator.Current );
}
_InternalCollection = newStack;

答案 7 :(得分:0)

首先不使用堆栈怎么样?如何包装LinkedList然后使用RemoveLast方法来完成你需要的工作呢?

一些粗略的代码,需要更多的工作,但你得到了基本的想法:

    public class CustomStack<T>
    {
        private LinkedList<T> list;

        public CustomStack()
        {
            list = new LinkedList<T>();
        }

        public void Push(T item)
        {
            list.AddFirst(item);
        }

        public T Pop()
        {

            var result = list.First.Value;
            list.RemoveFirst();
            return result;
        }

        public void TrimLast(int amountToLeave)
        {
            while (list.Count > amountToLeave)
            {
                list.RemoveLast();
            }
        }
    }

答案 8 :(得分:0)

我认为你可以非常依赖Double反转速度慢。 1)的运行时间在_InternalCollection.Length(假设Clear是常数时间)和O(n)方面是恒定的(O(1)),并且是你要保留的元素数量。 2)将变得更慢,线性地更大的_InternalCollection是(O(n))

您可能还需要考虑将_InternalCollection更改为Deque(双端队列)。 Deque支持O(1)摊销时间来从任一端添加/删除项目。当然(AFAIK)没有标准的实现,所以你必须自己动手或use a third party implementation

答案 9 :(得分:0)

memcpy堆栈中最后10个项目到你想要的位置。

然后将堆栈声明为10个项目。