Стек
Min Стек
Реализовать стек с получением минимума за O(1)
Постановка задачи
Описание (LeetCode - 155. Min Stack)
Нужно реализовать стек, который поддерживает операции:
push(val)- добавить числоvalв стек.pop()- удалить верхний элемент.top()- вернуть верхний элемент.getMin()- вернуть минимальный элемент в стеке.
Все операции должны работать за O(1).
Пример:
MinStack minStack = new MinStack()
minStack.push(-2)
minStack.push(0)
minStack.push(-3)
minStack.getMin()
minStack.pop()
minStack.top()
minStack.getMin()
Результат:
-3
0
-2
Ограничения:
-2³¹ <= val <= 2³¹ - 1- Операции
pop,topиgetMinвызываются только для непустого стека. - Будет выполнено не более
3 * 10⁴операций.
Решение
Обычный стек легко отдает верхний элемент, но не отслеживает минимум. Если каждый раз
искать минимум проходом по стеку, операция getMin() будет работать за O(n),
а по условию нужно O(1).
Значит, минимум нужно поддерживать заранее.
Идея решения:
Будем хранить в стеке не одно число, а пару:
значение, минимум на момент добавления этого значения
Когда добавляем новое число val, текущий минимум после добавления равен:
min(val, предыдущий минимум)
Эту пару кладем в стек. Тогда getMin() просто смотрит на минимум в верхней
паре.
Рассмотрим пример:
push(-2): кладем пару(val, min).
push(0): минимум после добавления равенmin(0, -2) = -2.
push(-3): минимум становится-3.
getMin()смотрит на минимум в верхней паре.
- После
pop()верхняя пара снова описывает актуальное состояние стека:top() = 0,getMin() = -2.
Мы не пересчитываем минимум после удаления. Он уже сохранен в следующей паре
ниже, потому что эта пара описывает состояние стека до последнего push.
Оценка сложности
Временная сложность
Все операции работают за O(1): мы только добавляем, удаляем или читаем верхний
элемент стека.
Пространственная сложность
Для каждого элемента мы храним пару значений, поэтому пространственная сложность
равна O(n).
Код решения
Приведем код решения.
type Entry struct {
val int
min int
}
type MinStack struct {
stack []Entry
}
func Constructor() MinStack {
return MinStack{
stack: []Entry{},
}
}
func (s *MinStack) Push(val int) {
currentMin := val
if len(s.stack) > 0 && s.stack[len(s.stack)-1].min < currentMin {
currentMin = s.stack[len(s.stack)-1].min
}
s.stack = append(s.stack, Entry{val: val, min: currentMin})
}
func (s *MinStack) Pop() {
s.stack = s.stack[:len(s.stack)-1]
}
func (s *MinStack) Top() int {
return s.stack[len(s.stack)-1].val
}
func (s *MinStack) GetMin() int {
return s.stack[len(s.stack)-1].min
}Итоги
Задача Min Stack показывает, что стек может хранить не только сами элементы, но и состояние структуры данных после каждого добавления.
- Суть алгоритма: вместе с каждым значением мы сохраняем текущий минимум. Верхняя пара всегда описывает актуальное состояние стека.
- Эффективность:
- Временная сложность:
O(1)дляpush,pop,topиgetMin. - Пространственная сложность:
O(n), так как для каждого элемента мы храним дополнительный минимум.
- Временная сложность:
- Граничные случаи:
- Повторяющиеся минимумы: каждая пара хранит свой минимум, поэтому удаление одного значения не ломает ответ.
- Один элемент: его значение одновременно является вершиной и минимумом.
- Оптимальное решение: мы заранее сохраняем минимум для каждого состояния
стека. Поэтому после
popне нужно искать новый минимум проходом по всем элементам.