Mam nieposortowaną tablicę . Mam zapytania, w których podaję zakres, a następnie zwracana jest maksymalna wartość z tego zakresu. Na przykład:
array[]={23,17,9,45,78,2,4,6,90,1};
query(both inclusive): 2 6
answer: 78
Jaki algorytm lub strukturę danych tworzę, aby szybko uzyskać maksymalną wartość z dowolnego zakresu. (Istnieje wiele zapytań)
EDYCJA: To jest rzeczywiście prosta wersja rzeczywistego problemu. Mogę mieć rozmiar tablicy nawet 100000, a liczbę zapytań do 100000. Zdecydowanie wymagam wcześniejszego przetworzenia, które ułatwi szybką odpowiedź na zapytanie.
algorithms
array
sudeepdino008
źródło
źródło
Odpowiedzi:
Myślę, że możesz zbudować jakieś drzewo binarne, w którym każdy węzeł reprezentuje maksymalną wartość jego dzieci:
Następnie musisz tylko znaleźć sposób, aby określić, które węzły minimalnie musisz sprawdzić, aby znaleźć maksymalną wartość w pytanym zakresie. W tym przykładzie, aby uzyskać maksymalną wartość z zakresu indeksu
[2, 6]
(włącznie),max(45, 78, 4)
zamiast tegomax(9, 45, 78, 2, 4)
. W miarę wzrostu drzewa zysk będzie większy.źródło
78
(i pomijać2
), ponieważ dla wszystkich wie, że indeks6
znajduje się w tym poddrzewie.Uzupełnienie odpowiedzi ngoaho91.
Najlepszym sposobem rozwiązania tego problemu jest użycie struktury danych drzewa segmentów. To pozwala ci odpowiadać na takie zapytania w O (log (n)), co oznaczałoby, że całkowita złożoność twojego algorytmu wynosiłaby O (Q logn), gdzie Q jest liczbą zapytań. Jeśli użyjesz naiwnego algorytmu, całkowita złożoność wyniesie O (Q n), co jest oczywiście wolniejsze.
Wadą drzew segmentowych jest jednak wada. Zajmuje dużo pamięci, ale często mniej zależy ci na pamięci niż na szybkości.
Pokrótce opiszę algorytmy używane przez ten DS:
Drzewo segmentów jest tylko specjalnym przypadkiem drzewa wyszukiwania binarnego, w którym każdy węzeł przechowuje wartość zakresu, do którego jest przypisany. Węzłu root przypisano zakres [0, n]. Lewemu dziecku przypisano zakres [0, (0 + n) / 2], a prawemu dziecku [(0 + n) / 2 + 1, n]. W ten sposób drzewo zostanie zbudowane.
Utwórz drzewo :
Drzewo zapytań
Jeśli potrzebujesz dodatkowych wyjaśnień, daj mi znać.
BTW, Drzewo segmentów obsługuje także aktualizację pojedynczego elementu lub zakresu elementów w O (log n)
źródło
O(log(n))
każdy element musi zostać dodany do drzewa. Dlatego całkowita złożoność toO(nlog(n))
Najlepszy algorytm byłby w czasie O (n), ponieważ poniżej zacznijmy od końca, indeks granic zakresu
źródło
max
sięa[i]
i rozpocząćfor
w pętlii+1
).start
, zatrzymaj się naend
). I zgadzam się, że jest to najlepszy sposób na jednorazowe wyszukiwanie. @ Odpowiedź ThijsvanDien jest lepsza tylko wtedy, gdy wyszukiwanie ma się odbyć wiele razy, ponieważ początkowo trwa to dłużej.Rozwiązania oparte na drzewku binarnym / drzewie segmentu rzeczywiście wskazują właściwy kierunek. Można jednak sprzeciwić się, że wymagają one dużo dodatkowej pamięci. Istnieją dwa rozwiązania tych problemów:
Pierwszą kwestią jest to, że ponieważ drzewo ma wysoką strukturę, możesz użyć struktury podobnej do sterty, aby domyślnie zdefiniować drzewo, zamiast reprezentować drzewo za pomocą węzłów, lewego i prawego wskaźnika, interwału itp. To oszczędza dużo pamięci, zasadniczo brak wydajności - musisz wykonać trochę więcej arytmetyki wskaźnika.
Druga kwestia polega na tym, że kosztem nieco więcej pracy podczas oceny można użyć drzewa M-ary zamiast drzewa binarnego. Na przykład, jeśli używasz drzewa 3-arylowego, obliczysz maksymalnie 3 elementy jednocześnie, następnie 9 elementów jednocześnie, a następnie 27 itd. Wymagane dodatkowe miejsce to N / (M-1) - możesz udowodnij, używając formuły serii geometrycznej. Na przykład, jeśli wybierzesz M = 11, będziesz potrzebować 1/10 miejsca na metodę drzewa binarnego.
Możesz sprawdzić, czy te naiwne i zoptymalizowane implementacje w Pythonie dają te same wyniki:
vs.
źródło
wypróbuj strukturę danych „segment tree”
istnieją 2 kroki
build_tree () O (n)
zapytanie (int min, int max) O (nlogn)
http://en.wikipedia.org/wiki/Segment_tree
edytować:
po prostu nie czytacie wiki, którą wysłałem!
algorytm ten:
- przemierzasz tablicę 1 raz, aby zbudować drzewo. O (n)
- kolejne 100000000+ razy, kiedy chcesz poznać maksimum dowolnej części tablicy, po prostu wywołaj funkcję zapytania. O (logowanie) dla każdego zapytania
- c ++ zaimplementuj tutaj geeksforgeeks.org/segment-tree-set-1-range-minimum-query/
stary algorytm to:
każde zapytanie, wystarczy przejść przez zaznaczony obszar i znaleźć.
więc jeśli użyjesz tego algorytmu do przetworzenia raz, OK, będzie wolniejszy niż stary sposób. ale jeśli będziemy przetwarzać ogromną liczbę zapytań (w mld), to bardzo wydajny można wygenerować plik tekstowy takiego, dla testowanej
linii 1: 50000 liczba losowa z 0-1000000, podzielone przez „(spacja)” (jest to tablica)
linia 2: 2 losowa liczba od 1 do 50000, podzielona przez „(spację)” (to zapytanie)
...
linia 200000: lubi linię 2, to również losowe zapytanie
jest to przykładowy problem, przepraszam, ale jest to w języku wietnamskim
http://vn.spoj.com/problems/NKLINEUP/,
jeśli rozwiążesz go starym sposobem, nigdy nie przejdziesz.
źródło
O(n)
przeszukiwanie tablicy, jak opisano w odpowiedzi tarun_telang. Pierwszym instynktem jest to, żeO(log n + k)
jest szybsze niżO(n)
, aleO(log n + k)
jest to po prostu pobieranie pod-macierzy - równoważneO(1)
dostępowi do tablicy, biorąc pod uwagę punkty początkowe i końcowe. Będziesz musiał przejść przez to, aby znaleźć maksimum.Możesz osiągnąć O (1) na zapytanie (z konstrukcją O (n log n)) za pomocą struktury danych zwanej tabelą rzadką. Dla każdej potęgi 2 zachowajmy maksimum dla każdego odcinka tej długości. Teraz dany segment [l, r) daje maksimum maksimum dla [l + 2 ^ k) i [r-2 ^ k, r) dla odpowiedniego k. Nakładają się na siebie, ale jest OK
źródło