Вы находитесь на странице: 1из 3

1 Задача 1-3

Постановка задания: Предложить реализацию стека на основе одного масси-


ва которая поддерживает операции добавления в конец и удаления из конца.
Хотим, чтобы отношение capacity и size было ограничено константой, а также
учетная стоимость была константной. N.B. я считаю, де/аллокация имеет не
более чем Cmax · n сложность.

Доказательство. Опишем для начала поля, которые нам понадобятся.


Algorithm 1: stack
int size ;
int capacity ;
T* data ;
Я предлагаю так: опишем основные операции, потом поймем почему они
O(1) в среднем и поймем, что требуемые условия выполнены. Я специально не
пишу тип возвращаемых объектов, стараясь сделать более-менее абстрактную
реализацию. Считаю, что суть задачи именно в идее и ее доказательстве.

Algorithm 2: push(a)
if capacity < size then
data[capacity] = a;
++capacity;
else
newdata = new T[2 × size];
for i = 0 . . . size - 1 do
newdata[i] = data[i]
end
∼data ;
size *= 2 ;
newdata[capacity] = a ;
++capacity ;
data = newdata;
end

1
Algorithm 3: pop()
if capacity == 0 then
return NaN
end
if capacity == 1 then
–capacity ;
else
if size / capacity < 4 then
–capacity
else
newdata = new T[size / 2] ;
for i = 0 . . . capacity - 2 do
newdata[i] = stack[i]
end
∼ data ;
–capacity ;
size /= 2 ;
data = newdata ;
end
end

Теперь можно приступить к обсуждению наших вопросов. Легко видеть, что


push(a) мы по сути не трогали, поэтому у него стоимость O(1). Перейдем к
pop. Возникает вопрос: а зачем мы удаляли память именно так? Суть в том,
что когда у нас очень маленький capacity (меньше чем size/4), то нам и нет
смысла держать эту кучу свободных ячеек массива. Давайте освободим память
в два раза. Почему в два, а не четыре? Потому что если мы потом захотим что-
то добавить, то у нас будет буфер, который позволит нам добавлять на O(1)
чистого времени (без выделения памяти).
Поймем почему тут O(1). Давайте воспользуемся "монетками". Докажем для
push: будем брать 3Cmax +1+1 (3Cmax - это на последующую деаллокацию разме-
ра n и аллокацию размера 2n, первая единичка понадобится при переписывании,
а еще одна на действие). Итого, когда нам надо будет выделить новый участок
под данные у нас уже накопится 3Cmax n + n монеток, что позволит нам дешево

2
переписывать и аллоцировать!
Почему O(1) в pop: аналогичная идея возьмем с огромным запасом 3Cmax +
1 + 1 монеток в каждую операцию pop. Тогда в случае, когда мы память осво-
бождаем, то у нас будет свободными еще 3Cmax k + k монеток, которые мы и
потратим.
Почему C0 < size/capacity < C1 . Очевидно, что 0 < size/capacity, а также,
если капасити маленький, то мы уменьшаем массив в два раза, что показывает
нам, что size/capacity < 4.