EDYCJA: To nie spełnia ograniczenia „stałej przestrzeni” - w zasadzie podwaja wymaganą przestrzeń. Bardzo wątpię, czy istnieje rozwiązanie, które tego nie robi, bez niszczenia gdzieś złożoności środowiska wykonawczego (np. Tworzenie push / pop O (n)). Zwróć uwagę, że nie zmienia to złożoności wymaganej przestrzeni, np. Jeśli masz stos z wymaganiami dotyczącymi miejsca O (n), nadal będzie to O (n) z innym stałym współczynnikiem.
Rozwiązanie niestałej przestrzeni
Zachowaj „zduplikowany” stos „minimum wszystkich wartości niżej w stosie”. Kiedy usuniesz główny stos, zdejmij również minimalny stos. Kiedy wepchniesz główny stos, wepchnij nowy element lub bieżące min, w zależności od tego, która z tych wartości jest niższa. getMinimum()
jest następnie implementowany jako sprawiedliwy minStack.peek()
.
Korzystając z twojego przykładu, mielibyśmy:
Real stack Min stack
5 --> TOP 1
1 1
4 2
6 2
2 2
Po dwukrotnym wystrzeleniu otrzymujesz:
Real stack Min stack
4 2
6 2
2 2
Daj mi znać, jeśli to nie wystarczy. To proste, kiedy się go pogłębisz, ale na początku może to zająć trochę drapania po głowie :)
(Wadą jest oczywiście to, że podwaja zapotrzebowanie na miejsce. Czas wykonania nie ulega jednak znacznemu pogorszeniu - tj. Nadal jest to ta sama złożoność).
EDYCJA: istnieje odmiana, która jest nieco bardziej skomplikowana, ale ogólnie ma lepszą przestrzeń. Nadal mamy minimalny stos, ale wyskakujemy z niego tylko wtedy, gdy wartość, którą zdejmujemy z głównego stosu, jest równa tej na minimalnym stosie. Mamy tylko naciskać na min stosie, gdy wartość pchanych na głównym stosie jest mniejsza niż lub równa bieżącej wartości min. Pozwala to na zduplikowane wartości minimalne. getMinimum()
to wciąż tylko podglądowa operacja. Na przykład, biorąc oryginalną wersję i ponownie wciskając 1, otrzymamy:
Real stack Min stack
1 --> TOP 1
5 1
1 2
4
6
2
Wyskakiwanie z powyższego wyskakuje z obu stosów, ponieważ 1 == 1, pozostawiając:
Real stack Min stack
5 --> TOP 1
1 2
4
6
2
Popping ponownie wyskakuje tylko z głównego stosu, ponieważ 5> 1:
Real stack Min stack
1 1
4 2
6
2
Popping ponownie powoduje zdemaskowanie obu stosów, ponieważ 1 == 1:
Real stack Min stack
4 2
6
2
Kończy się to z taką samą złożonością miejsca w najgorszym przypadku (dwukrotność oryginalnego stosu), ale znacznie lepszym wykorzystaniem miejsca, jeśli rzadko otrzymujemy „nowe minimum lub równe”.
EDYCJA: Oto implementacja złego planu Pete'a. Nie przetestowałem tego dokładnie, ale myślę, że jest w porządku :)
using System.Collections.Generic;
public class FastMinStack<T>
{
private readonly Stack<T> stack = new Stack<T>();
// Could pass this in to the constructor
private readonly IComparer<T> comparer = Comparer<T>.Default;
private T currentMin;
public T Minimum
{
get { return currentMin; }
}
public void Push(T element)
{
if (stack.Count == 0 ||
comparer.Compare(element, currentMin) <= 0)
{
stack.Push(currentMin);
stack.Push(element);
currentMin = element;
}
else
{
stack.Push(element);
}
}
public T Pop()
{
T ret = stack.Pop();
if (comparer.Compare(ret, currentMin) == 0)
{
currentMin = stack.Pop();
}
return ret;
}
}
Dodaj pole do przechowywania wartości minimalnej i zaktualizuj je podczas operacji Pop () i Push (). W ten sposób getMinimum () będzie miało wartość O (1), ale Pop () i Push () będą musiały wykonać trochę więcej pracy.
Jeśli zdejmie się minimalną wartość, Pop () będzie równe O (n), w przeciwnym razie obie będą nadal O (1). Podczas zmiany rozmiaru Push () staje się O (n), zgodnie z implementacją Stack.
Oto szybka implementacja
źródło
Przechowuje bieżące minimum jawnie, a jeśli minimalne zmiany, zamiast przesuwać wartość, wypycha wartość z tą samą różnicą po drugiej stronie nowego minimum (jeśli min = 7 i naciśniesz 5, zamiast tego wypycha 3 (5- | 7-5 | = 3) i ustawia min na 5; jeśli następnie zdejmiesz 3, gdy min wynosi 5, zobaczy, że wartość zderzaka jest mniejsza niż min, więc odwraca procedurę, aby uzyskać 7 dla nowej minuty, a następnie zwraca poprzednią min). Ponieważ każda wartość, która nie powoduje zmiany, bieżące minimum jest większe niż bieżące minimum, masz coś, co może być użyte do rozróżnienia między wartościami, które zmieniają minimum, a tymi, które nie.
W językach, które używają liczb całkowitych o stałym rozmiarze, pożyczasz trochę miejsca z reprezentacji wartości, więc może to spowodować niedopełnienie, a potwierdzenie zakończy się niepowodzeniem. Ale poza tym jest to stała dodatkowa przestrzeń i wszystkie operacje są nadal O (1).
Stosy, które zamiast tego są oparte na połączonych listach, mają inne miejsca, z których można trochę pożyczyć, na przykład w C najmniej znaczący bit następnego wskaźnika lub w Javie typ obiektów na połączonej liście. W przypadku języka Java oznacza to, że jest używane więcej miejsca w porównaniu do ciągłego stosu, ponieważ masz narzut obiektu na łącze:
W C nie ma narzutu i możesz pożyczyć lsb następnego wskaźnika:
Jednak żaden z nich nie jest naprawdę O (1). W praktyce nie wymagają więcej miejsca, ponieważ wykorzystują dziury w reprezentacjach liczb, obiektów lub wskaźników w tych językach. Ale teoretyczna maszyna, która używałaby bardziej zwartej reprezentacji, wymagałaby dodania dodatkowego bitu do tej reprezentacji w każdym przypadku.
źródło
pop()
jeśli ostatnią wypchniętą wartością byłaInteger.MIN_VALUE
(np. Push 1, push Integer.MIN_VALUE, pop). Wynika to z niedomiaru, jak wspomniano powyżej. W przeciwnym razie działa dla wszystkich wartości całkowitych.Znalazłem rozwiązanie, które spełnia wszystkie wspomniane ograniczenia (operacje na stałym czasie) i stałą dodatkową przestrzeń .
Pomysł polega na zapisaniu różnicy między wartością minimalną a liczbą wejściową i zaktualizowaniu wartości minimalnej, jeśli nie jest już wartością minimalną.
Kod wygląda następująco:
Kredyt trafia do: https://leetcode.com/discuss/15679/share-my-java-solution-with-only-one-stack
źródło
Jakie są ograniczenia czasu wykonywania
push
ipop
? Jeśli nie wymaga się, aby były stałe, po prostu oblicz minimalną wartość w tych dwóch operacjach (czyniąc je O ( n )). W przeciwnym razie nie widzę, jak można to zrobić ze stałą dodatkową przestrzenią.źródło
Oto mój kod, który działa z O (1). Poprzedni kod, który opublikowałem, miał problem z wyskakiwaniem minimalnego elementu. Zmodyfikowałem swój kod. Ten używa innego stosu, który utrzymuje minimalny element na stosie powyżej aktualnie wypchniętego elementu.
źródło
Użyłem innego rodzaju stosu. Oto realizacja.
Wynik:
Spróbuj. Myślę, że to odpowiada na pytanie. Drugi element każdej pary podaje minimalną wartość widzianą podczas wstawiania tego elementu.
źródło
Wrzucam tutaj cały kod, aby znaleźć min i max w podanym stosie.
Złożoność czasowa będzie wynosić O (1) ..
Daj mi znać, jeśli napotkasz jakiś problem
Dzięki, Vikash
źródło
Możesz rozszerzyć swoją oryginalną klasę stosu i po prostu dodać do niej śledzenie min. Pozwól, aby oryginalna klasa nadrzędna obsługiwała wszystko inne w zwykły sposób.
źródło
Oto moje rozwiązanie w javie przy użyciu listy ulubionych.
}
źródło
Załóżmy, że stos, nad którym będziemy pracować, jest następujący:
W powyższej reprezentacji stos jest budowany tylko przez lewą wartość, a [minvalue] prawej wartości jest zapisywana tylko w celach ilustracyjnych, która będzie przechowywana w jednej zmiennej.
Rzeczywisty problem polega na tym, że wartość, która jest minimalną wartością get, jest usuwana w tym momencie, skąd możemy wiedzieć, jaki jest następny minimalny element bez iteracji po stosie.
Na przykład w naszym stosie, gdy wyskoczyło 6 get, wiemy, że nie jest to element minimalny, ponieważ element minimalny wynosi 2, więc możemy bezpiecznie usunąć to bez aktualizowania naszej wartości min.
Ale kiedy wyskoczymy 2, widzimy, że minimalna wartość wynosi teraz 2, a jeśli to get wyskoczyło, musimy zaktualizować minimalną wartość do 3.
Punkt 1:
Teraz, jeśli przyjrzysz się uważnie, musimy wygenerować minvalue = 3 z tego konkretnego stanu [2, minvalue = 2]. lub jeśli pójdziesz depper w stosie, musimy wygenerować minvalue = 7 z tego konkretnego stanu [3, minvalue = 3] lub jeśli pójdziesz bardziej depper w stosie, musimy wygenerować minvalue = 8 z tego konkretnego stanu [7, minvalue = 7]
Czy zauważyłeś coś wspólnego we wszystkich powyższych 3 przypadkach, wartość, którą musimy wygenerować, zależy od dwóch zmiennych, które są równe. Poprawny. Dlaczego tak się dzieje, ponieważ kiedy przesuwamy jakiś element mniejszy niż bieżąca minvalue, to po prostu umieszczamy ten element na stosie i aktualizujemy tę samą liczbę również w minvalue.
Punkt 2:
Więc w zasadzie przechowujemy duplikat tej samej liczby raz na stosie i raz w zmiennej minvalue. Musimy skupić się na unikaniu tego powielania i przechowywać jakieś przydatne dane na stosie lub minimalną wartość, aby wygenerować poprzednie minimum, jak pokazano w przypadku powyżej.
Skoncentrujmy się na tym, co powinniśmy przechowywać na stosie, gdy wartość do przechowywania w push jest mniejsza niż minimalna wartość. Nazwijmy tę zmienną y, więc teraz nasz stos będzie wyglądał mniej więcej tak:
Zmieniłem ich nazwy na y1, y2, y3, aby uniknąć nieporozumień, że wszystkie będą miały taką samą wartość.
Punkt3:
Teraz spróbujmy znaleźć jakieś ograniczenie na y1, y2 i y3. Czy pamiętasz, kiedy dokładnie musimy zaktualizować minvalue podczas wykonywania pop (), tylko wtedy, gdy usunęliśmy element, który jest równy minvalue. Jeśli wystrzelimy coś większego niż minvalue, nie musimy aktualizować minvalue. Tak więc, aby wyzwolić aktualizację minvalue, y1, y2 i y3 powinny być mniejsze niż odpowiadająca im minvalue. [Uwolnimy równość, aby uniknąć powielania [Point2]], więc ograniczenie wynosi [y <minValue].
Teraz wróćmy do wypełnienia y, musimy wygenerować jakąś wartość i umieścić y w momencie wypychania, pamiętaj. Przyjmijmy, że wartością, która nadchodzi dla push jest x, która jest mniejsza niż wartość prevMinvalue, a wartością, którą faktycznie wepchniemy na stosie, będzie y. Więc jedno jest oczywiste, że newMinValue = x i y <newMinvalue.
Teraz musimy obliczyć y (pamiętaj, że y może być dowolną liczbą, która jest mniejsza niż newMinValue (x), więc musimy znaleźć jakąś liczbę, która może spełnić nasze ograniczenie) za pomocą prevMinvalue i x (newMinvalue).
Więc w momencie wypychania x, jeśli jest mniejsze niż prevMinvalue, wtedy wciskamy y [2 * x-prevMinValue] i aktualizujemy newMinValue = x.
A w momencie pop, jeśli stos zawiera coś mniejszego niż minValue, to jest to nasz wyzwalacz do aktualizacji minVAlue. Musimy obliczyć prevMinValue z curMinValue i y. y = 2 * curMinValue - prevMinValue [Udowodniono] prevMinVAlue = 2 * curMinvalue - y.
2 * curMinValue - y to liczba, którą musimy teraz zaktualizować do prevMinValue.
Kod tej samej logiki jest wspólny poniżej ze złożonością czasu O (1) i przestrzeni O (1).
źródło
Oto moja wersja realizacji.
źródło
źródło
Znalazłem to rozwiązanie tutaj
źródło
źródło
źródło
źródło
Oto mój kod, który działa z O (1). Tutaj użyłem pary wektorów, która zawiera wartość, która została wypchnięta, a także zawiera minimalną wartość do tej przesuniętej wartości.
Oto moja wersja implementacji C ++.
źródło
źródło
Class
-MinStack
? Zaleca się korzystanie z dokumentacji Java firmy OracleDeque
.Praktyczna implementacja do znajdowania minimum w stosie obiektów zaprojektowanych przez użytkownika o nazwie: Szkoła
Stos będzie przechowywał Szkoły w stosie na podstawie rangi przypisanej szkole w określonym regionie, powiedzmy findMin () podaje szkole, gdzie otrzymamy maksymalną liczbę wniosków o Rekrutację, która z kolei ma być zdefiniowana przez komparator wykorzystujący rangę związaną ze szkołami w poprzednim sezonie.
Również obiekt szkolny przedstawia się następująco:
Ten przykład obejmuje następujące zagadnienia: 1. Implementacja stosu dla obiektów zdefiniowanych przez użytkownika, tutaj szkoła 2. Implementacja metody hashcode () i equals () przy użyciu wszystkich pól obiektów do porównania 3. Praktyczna implementacja scenariusza, w którym rqeuire, aby uzyskać operację stosu zawiera, aby była w kolejności O (1)
źródło
language-agnostic
proszę określić, czego używasz dla kodu (i wcześniej usuń spacjeThe Code for same is below:
). Jak to obsługujestack.pop()
? (ipush()
?)Oto implementacja PHP tego, co wyjaśniono w odpowiedzi Jona Skeeta, jako nieco lepszą implementację złożoności przestrzeni w celu uzyskania maksymalnego stosu w O (1).
źródło
Oto implementacja Jon Skeets Answer w języku C ++ . Może nie jest to najbardziej optymalny sposób implementacji, ale robi dokładnie to, co powinien.
A oto kierowca tej klasy
Wynik:
źródło
Możemy to zrobić w O (n) czasie i O (1) złożoności przestrzeni, na przykład:
źródło
Myślę, że możesz po prostu użyć LinkedList w swojej implementacji stosu.
Kiedy pierwszy raz wypychasz wartość, umieszczasz tę wartość jako nagłówek listy połączonej.
następnie za każdym razem, gdy wciskasz wartość, jeśli nowa wartość <head.data, wykonaj operację prepand (co oznacza, że głowa staje się nową wartością)
jeśli nie, wykonaj operację dołączania.
Kiedy robisz pop (), sprawdzasz, czy min == linkedlist.head.data, jeśli tak, to head = head.next;
Oto mój kod.
źródło
źródło
źródło
Znalazłem tutaj świetne rozwiązanie: https://www.geeksforgeeks.org/design-a-stack-that-supports-getmin-in-o1-time-and-o1-extra-space/
Poniżej znajduje się kod Pythona, który napisałem zgodnie z algorytmem:
źródło
Aby pobraćMin elementów ze stosu. Musimy użyć Two stack .ie Stack s1 i Stack s2.
--------------------- Wywołaj rekurencyjnie krok 2 do 4 -----------------------
if Nowy element dodany do stosu s1, a następnie zdejmij elementy ze stosu s2
porównaj nowe elementy z s2. który jest mniejszy, przesuń do s2.
pop ze stosu s2 (który zawiera min element)
Kod wygląda następująco:
źródło
Myślę, że tylko operacja wypychania cierpi, wystarczy. Moja implementacja zawiera stos węzłów. Każdy węzeł zawiera element danych, a także minimum w tym momencie. To minimum jest aktualizowane za każdym razem, gdy wykonywana jest operacja wypychania.
Oto kilka punktów do zrozumienia:
Zaimplementowałem stos używając listy połączonej.
Górna część wskaźnika zawsze wskazuje na ostatni wypchnięty element. Kiedy na tym stosie nie ma żadnego elementu, jest to NULL.
Kiedy element jest wypychany, przydzielany jest nowy węzeł, który ma następny wskaźnik wskazujący na poprzedni stos, a wierzchołek jest aktualizowany tak, aby wskazywał na ten nowy węzeł.
Jedyną różnicą w stosunku do normalnej implementacji stosu jest to, że podczas wypychania aktualizuje on min członka dla nowego węzła.
Proszę spojrzeć na kod, który został zaimplementowany w C ++ w celach demonstracyjnych.
A wynik programu wygląda następująco:
źródło
źródło