Связанный список или динамический массив для реализации стека - PullRequest
29 голосов
/ 14 сентября 2011

Я начал пересматривать структуры данных и алгоритмы до того, как начался мой последний год в школе, чтобы убедиться, что я на вершине всего. Одна проблема с рецензией гласила: «Реализуйте стек, используя связанный список или динамический массив, и объясните, почему вы сделали лучший выбор».

Мне показалось более интуитивно понятным использовать список с указателем хвоста для реализации стека, поскольку его часто приходится изменять в размере. Похоже, что для большого объема данных лучше выбрать список, поскольку изменение размера динамического массива - дорогостоящая операция. Кроме того, со списком вам не нужно выделять больше места, чем вам нужно на самом деле, поэтому это более эффективно.

Однако динамический массив определенно позволит добавлять данные намного быстрее (кроме случаев, когда их необходимо изменить в размере). Тем не менее, я не уверен, что использование массива в целом происходит быстрее или только если его не нужно изменять.

В решении книги сказано, что "для хранения очень больших объектов лучше использовать список", но я не понимаю, почему.

Какой путь лучше? Какие факторы следует использовать, чтобы определить, какая реализация является «лучшей»? Кроме того, какая-то моя логика здесь выключена?

Ответы [ 5 ]

30 голосов
/ 14 сентября 2011

Здесь есть много компромиссов, и я не думаю, что есть «правильный» ответ на этот вопрос.

Если вы реализуете стек, используя связанный список с указателем хвоста, тогда худшийрегистром времени для push, pop или peek является O (1).Однако каждый элемент будет иметь некоторые дополнительные издержки, связанные с ним (а именно, указатель), что означает, что для структуры всегда есть O (n) служебных данных.Кроме того, в зависимости от скорости вашего распределителя памяти, стоимость выделения новых узлов для стека может быть заметной.Кроме того, если вы будете постоянно извлекать все элементы из стека, вы можете столкнуться с падением производительности из-за плохой локальности, поскольку нет гарантии, что ячейки связанного списка будут храниться в памяти непрерывно.

ЕслиВы реализуете стек с помощью динамического массива, затем время выполнения амортизируемое для push или pop равно O (1), а стоимость просмотра в худшем случае равна O (1).Это означает, что если вы заботитесь о стоимости какой-либо отдельной операции в стеке, это может быть не лучшим подходом.Тем не менее, распределение происходит нечасто, поэтому общая стоимость добавления или удаления n элементов, вероятно, будет быстрее, чем соответствующая стоимость в подходе на основе связанного списка.Кроме того, накладные расходы памяти при таком подходе обычно лучше, чем накладные расходы памяти в связанном списке.Если ваш динамический массив просто хранит указатели на элементы, то в худшем случае происходит перегрузка памяти, когда половина элементов заполнена, и в этом случае имеется n дополнительных указателей (так же, как в случае, когда вы использовали связанныйlist), и в лучшем случае, когда динамический массив заполнен, пустых ячеек нет, а дополнительные издержки составляют O (1).Если, с другой стороны, ваш динамический массив напрямую содержит элементы, в худшем случае затраты памяти могут быть хуже.Наконец, поскольку элементы хранятся смежно, существует лучшая локальность, если вы хотите непрерывно выталкивать или выталкивать элементы из стека, поскольку все элементы находятся в памяти рядом друг с другом.

Короче говоря:

  • Подход со связанным списком имеет гарантии O (1) в худшем случае для каждой операции;динамический массив имеет амортизированные гарантии O (1).
  • Локальность связанного списка не так хороша, как локальность динамического массива.
  • Вероятна общая нагрузка на динамический массивбыть меньше, чем общая нагрузка связанного списка, при условии, что оба указателя хранилища на их элементы.
  • Суммарные накладные расходы динамического массива, вероятно, будут больше, чем у связанного списка, если элементы хранятся непосредственно.

Ни одна из этих структур явно не "лучше", чем другая.Это действительно зависит от вашего варианта использования.Лучший способ выяснить, что быстрее, - это рассчитать время и посмотреть, что работает лучше.

Надеюсь, это поможет!

1 голос
/ 20 января 2017

Изменение размера динамического массива не будет дорогой задачей, если вы хорошо спроектируете свою реализацию.

Например, чтобы увеличить массив, если он заполнен, создать новый массив в два раза больше и скопироватьitems.

Сумма амортизации составит ~ 3N за добавление N элементов.

1 голос
/ 21 июня 2012

Я думаю, вы сами ответили на вопрос. Для стека с большим количеством элементов динамический массив будет иметь чрезмерные накладные расходы (копирование накладных расходов) при простом добавлении дополнительного элемента в начало стека. Со списком это простой переключатель указателей.

1 голос
/ 14 сентября 2011

Важно то, сколько раз malloc () вызывается в ходе выполнения задачи.Это может занять от сотен до тысяч инструкций, чтобы получить блок памяти.(Время в free () или GC должно быть пропорционально этому.) Кроме того, сохраняйте чувство перспективы.Это может быть 99% от общего времени или только 1%, в зависимости от того, что еще происходит.

1 голос
/ 14 сентября 2011

Что касается вопроса о малых объектах и ​​больших объектах, подумайте, сколько дополнительного пространства нужно использовать для связанного списка, если в вашем стеке есть небольшие объекты. Затем подумайте, сколько дополнительного пространства вам понадобится, если в вашем стеке есть куча больших объектов.

Далее рассмотрим те же вопросы, но с реализацией, основанной на динамических массивах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...