Performance 链表与动态数组用于实现堆栈

Performance 链表与动态数组用于实现堆栈,performance,data-structures,stack,linked-list,dynamic-arrays,Performance,Data Structures,Stack,Linked List,Dynamic Arrays,我在学校的最后一年开始之前已经开始检查数据结构和算法,以确保我掌握了一切。一个复习题是“使用链表或动态数组实现一个堆栈,并解释为什么您做出了最佳选择” 对我来说,使用带有尾部指针的列表来实现堆栈似乎更直观,因为它可能需要经常调整大小。对于大量数据,列表似乎是更好的选择,因为动态数组重新调整大小是一项昂贵的操作。此外,使用列表,您不需要分配比实际需要更多的空间,因此更节省空间 但是,动态数组肯定可以更快地添加数据(需要调整大小时除外)。但是,我不确定使用数组是否总体上更快,或者仅当它不需要调整大小

我在学校的最后一年开始之前已经开始检查数据结构和算法,以确保我掌握了一切。一个复习题是“使用链表或动态数组实现一个堆栈,并解释为什么您做出了最佳选择”

对我来说,使用带有尾部指针的列表来实现堆栈似乎更直观,因为它可能需要经常调整大小。对于大量数据,列表似乎是更好的选择,因为动态数组重新调整大小是一项昂贵的操作。此外,使用列表,您不需要分配比实际需要更多的空间,因此更节省空间

但是,动态数组肯定可以更快地添加数据(需要调整大小时除外)。但是,我不确定使用数组是否总体上更快,或者仅当它不需要调整大小时

这本书的解决方案说“对于存储非常大的对象,列表是一个更好的实现”,但我不明白为什么


哪条路最好?应该使用哪些因素来确定哪种实现是“最佳的”?还有,我的逻辑是什么?

嗯,对于小对象与大对象的问题,考虑如果在堆栈上有小对象,那么要为链表使用多少额外的空间。然后考虑在堆栈上有一组<强>大/强>对象时需要多少额外空间。


接下来,考虑同样的问题,但是基于动态数组的实现。

这里有很多的折衷,我不认为这个问题有一个“正确”的答案。 如果您使用带有尾部指针的链表实现堆栈,那么最坏情况下的push、pop或peek运行时是O(1)。但是,每个元素都会有一些额外的开销(即指针),这意味着结构总是有O(n)开销。此外,根据内存分配器的速度,为堆栈分配新节点的成本可能是显而易见的。此外,如果要连续弹出堆栈中的所有元素,可能会因位置不佳而影响性能,因为无法保证链表单元格将连续存储在内存中

如果使用动态数组实现堆栈,则推送或弹出的摊销运行时为O(1),最坏情况下的窥视成本为O(1)。这意味着,如果您关心堆栈中任何单个操作的成本,这可能不是最好的方法。也就是说,分配很少,因此添加或删除n个元素的总成本可能比基于链表的方法中相应的成本要快。此外,这种方法的内存开销通常比链表的内存开销好。如果您的动态数组只存储指向元素的指针,那么在最坏的情况下,当填充了一半的元素时,就会出现内存开销,在这种情况下,会有n个额外的指针(与使用链表时的情况相同),在最佳情况下,当动态数组已满时,没有空单元,额外开销为O(1)。另一方面,如果动态数组直接包含元素,那么在最坏的情况下,内存开销可能会更大。最后,由于元素是连续存储的,因此如果要从堆栈中连续推送或弹出元素,则有更好的局部性,因为所有元素在内存中都是紧挨着的

简言之:

  • 链表方法对每个操作都有最坏情况O(1)保证;动态数组已摊销O(1)个担保
  • 链表的局部性不如动态数组的局部性好
  • 假设两个存储指针都指向其元素,则动态数组的总开销可能小于链表的总开销
  • 如果直接存储元素,则动态数组的总开销可能大于链表的总开销
这两种结构显然都不比另一种“更好”。这实际上取决于您的用例。找出哪一个更快的最好方法是同时计时,看看哪一个表现更好


希望这有帮助

重要的是在运行任务的过程中调用malloc()的次数。要获得一块内存,可能需要数百到数千条指令。(free()或GC中的时间应该与此成比例。)此外,保持透视感。这可能占总时间的99%,或仅占1%,具体取决于其他情况。

我想你自己回答了这个问题。对于包含大量项的堆栈,仅在堆栈顶部添加一个额外项时,动态数组将产生过多的开销(复制开销)。对于列表,它只是一个简单的指针切换。

如果设计好实现,调整动态数组的大小将不会是一项昂贵的任务

例如,若要增加阵列,如果阵列已满,请创建一个两倍大小的新阵列,然后复制项


添加N个项目的摊销成本为~3N。

BTW--列表实现不需要尾部指针:而是在头部插入和删除。因此,对于动态数组,必须留出额外空间以避免经常调整大小,当使用大型对象时,这意味着需要留出大量额外空间。另一方面,对于列表,您不必留下任何额外的空间,这使得如果对象非常大,列表是一个更好的选择。这似乎是对的?如果你使用链表,你需要一些额外的空间,因为每个链接都会占用空间。说到局部性,我认为这是支持动态数组的最佳论据。
动态数组的总开销可能大于