To jest zadanie domowe. Mówią, że to zajmie O(logN + logM)
gdzie N
iM
są długościami tablic.
Nazwijmy tablice a
i b
. Oczywiście możemy zignorować wszystko a[i]
i b[i]
gdzie i> k.
Najpierw porównajmy a[k/2]
i b[k/2]
. Niech b[k/2]
> a[k/2]
. Dlatego możemy odrzucić również wszystko b[i]
, gdzie i> k / 2.
Teraz mamy wszystko a[i]
, gdzie i <k i all b[i]
, gdzie i <k / 2, aby znaleźć odpowiedź.
Jaki jest następny krok?
arrays
algorithm
binary-search
Michael
źródło
źródło
O(logN + logM)
odnosi się tylko do czasu potrzebnego na znalezienie k-tego elementu? Czy można wcześniej wykonać wstępne przetwarzanie w związku?Odpowiedzi:
Masz to, po prostu kontynuuj! I uważaj na indeksy ...
Aby trochę uprościć, przyjmuję, że N i M są> k, więc złożoność tutaj wynosi O (log k), czyli O (log N + log M).
Pseudo kod:
Do demonstracji możesz użyć niezmiennika pętli i + j = k, ale nie odrobię całej twojej pracy domowej :)
źródło
Mam nadzieję, że nie odpowiadam na twoje zadanie domowe, ponieważ minął ponad rok od czasu zadania tego pytania. Oto rekurencyjne rozwiązanie ogonowe, które zajmie log (len (a) + len (b)) czas.
Założenie: dane wejściowe są prawidłowe. tj. k należy do zakresu [0, len (a) + len (b)]
Przypadki podstawowe:
Kroki redukcji:
a
+ mid indexb
jest mniejszy niżk
a
jest większy niż środkowy elementb
, możemy zignorować pierwszą połowęb
, dostosujk
.a
, dostosujk
.k
jest mniejsza niż suma średnich indeksówa
ib
:a
jest większy niż środkowy elementb
, możemy bezpiecznie zignorować drugą połowęa
b
Kod:
Zwróć uwagę, że moim rozwiązaniem jest tworzenie nowych kopii mniejszych tablic w każdym wywołaniu, można to łatwo wyeliminować, przekazując tylko indeksy początku i końca oryginalnych tablic.
źródło
kthlargest()
zwraca(k+1)
-ty najmniejszy element, np.1
jest drugim najmniejszym elementem w0,1,2,3
ie, funkcja zwracasorted(a+b)[k]
.Wiele osób odpowiedziało na to pytanie „k-ty najmniejszy element z dwóch posortowanych tablic”, ale zwykle zawierało tylko ogólne pomysły, a nie jasny działający kod lub analizę warunków brzegowych.
Tutaj chciałbym szczegółowo go rozwinąć, uwzględniając sposób, w jaki poszedłem, aby pomóc niektórym nowicjuszom zrozumieć, z moim poprawnym działającym kodem Java.
A1
iA2
są dwiema sortowanymi rosnącymi tablicami, odpowiednio zsize1
isize2
jako długość. Musimy znaleźć k-ty najmniejszy element z sumy tych dwóch tablic. Tutaj rozsądnie zakładamy, że to(k > 0 && k <= size1 + size2)
oznacza, żeA1
iA2
nie może być jednocześnie puste.Najpierw podejdźmy do tego pytania za pomocą powolnego algorytmu O (k). Metoda polega na porównaniu pierwszego elementu zarówno tablicy, jak
A1[0]
iA2[0]
. Weź mniejszą, powiedzmyA1[0]
do naszej kieszeni. Następnie porównajA1[1]
zA2[0]
i tak dalej. Powtarzaj tę czynność, aż nasza kieszeń osiągniek
elementy. Bardzo ważne: w pierwszym kroku możemy zobowiązać się tylko doA1[0]
kieszeni. Nie możemy uwzględniać ani wykluczaćA2[0]
!!!Poniższy kod O (k) daje jeden element przed poprawną odpowiedzią. Tutaj używam go, aby pokazać mój pomysł i warunek brzegowy analizy. Mam poprawny kod po tym:
Najpotężniejszym pomysłem jest to, że w każdej pętli zawsze używamy podejścia opartego na przypadku podstawowym. Po zatwierdzeniu aktualnego najmniejszego elementu zbliżamy się o krok do celu: k-tego najmniejszego elementu. Nigdy nie wskakuj w sam środek i nie daj się zagubić!
Przestrzegając powyższego przypadku bazowego kodu
k == 1, k == size1+size2
i łącząc z tymA1
iA2
nie można oba być puste. Możemy przekształcić tę logikę w bardziej zwięzły styl.Oto powolny, ale poprawny działający kod:
Teraz możemy wypróbować szybszy algorytm działający na O (log k). Podobnie porównaj
A1[k/2]
zA2[k/2]
; jeśliA1[k/2]
jest mniejszy, to wszystkie elementy odA1[0]
doA1[k/2]
powinny znajdować się w naszej kieszeni. Chodzi o to, aby nie zatwierdzać tylko jednego elementu w każdej pętli; pierwszy krok zawierak/2
elementy. Ponownie, nie możemy włączyć lub wyłączyćA2[0]
, abyA2[k/2]
w każdym razie. Więc w pierwszym kroku nie możemy przejść dalej niżk/2
elementy. W drugim kroku nie możemy przejść dalejk/4
elementy ...Po każdym kroku zbliżamy się znacznie do k-tego elementu. W tym samym czasie każdy krok staje się coraz mniejszy, aż do osiągnięcia
(step == 1)
, czyli(k-1 == index1+index2)
. Następnie możemy ponownie odwołać się do prostego i potężnego przypadku podstawowego.Oto działający poprawny kod:
Niektórzy ludzie mogą się martwić, a co jeśli
(index1+index2)
przeskoczy nad k-1? Czy moglibyśmy przegapić przypadek podstawowy(k-1 == index1+index2)
? To niemożliwe. Możesz dodać 0,5 + 0,25 + 0,125 ... i nigdy nie przekroczysz 1.Oczywiście bardzo łatwo jest przekształcić powyższy kod w algorytm rekurencyjny:
Mam nadzieję, że powyższa analiza i kod Java pomogą ci to zrozumieć. Ale nigdy nie kopiuj mojego kodu jako pracy domowej! Twoje zdrowie ;)
źródło
else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0)
zamiastelse if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)
? (W kodzie kthSmallestSlowWithFault)Oto iteracyjna wersja rozwiązania @ lambdapilgrim w C ++ (zobacz wyjaśnienie algorytmu):
Działa dla wszystkich
0 <= n < (size(a) + size(b))
indeksów i maO(log(size(a)) + log(size(b)))
złożoność.Przykład
źródło
Moja próba dla pierwszych k liczb, k-tej liczby w 2 posortowanych tablicach i n posortowanych tablicach:
Kompletny kod z narzędziami do debugowania można znaleźć pod adresem : https://github.com/brainclone/teasers/tree/master/kth
źródło
Oto mój kod oparty na rozwiązaniu Jules Olleon:
źródło
Oto moja implementacja w C, możesz odwołać się do wyjaśnień @Julesa Olléona dla algorytmu: idea algorytmu polega na tym, że utrzymujemy i + j = k i znajdujemy takie i i j, aby a [i-1] <b [j-1] <a [i] (lub odwrotnie). Ponieważ w „a” są elementy i mniejsze niż b [j-1], a elementy j-1 w „b” mniejsze niż b [j-1], b [j-1] to i + j-1 + 1 = k-ty najmniejszy element. Aby znaleźć takie i, j algorytm przeprowadza przeszukiwanie dychotomiczne na tablicach.
źródło
Oto moje rozwiązanie. Kod C ++ drukuje k-tą najmniejszą wartość, a także liczbę iteracji, aby uzyskać k-tą najmniejszą wartość za pomocą pętli, która moim zdaniem jest rzędu log (k). Kod wymaga jednak, aby k było mniejsze niż długość pierwszej tablicy, co jest ograniczeniem.
źródło
Pierwszy pseudokod podany powyżej nie działa dla wielu wartości. Na przykład tutaj są dwie tablice. int [] a = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};
To nie zadziałało dla k = 3 i k = 9 w nim. Mam inne rozwiązanie. Jest to podane poniżej.
Ale ... to też nie działa dla k = 5. Istnieje parzysty / nieparzysty połów k, który nie pozwala, aby było to proste.
źródło
źródło
Oto moje rozwiązanie w Javie. Spróbuję go dalej zoptymalizować
To jest zainspirowane Algo na wspaniałym filmie na youtube
źródło
Link do złożoności kodu (log (n) + log (m))
Link do kodu (log (n) * log (m))
Implementacja rozwiązania (log (n) + log (m))
Chciałbym dodać swoje wyjaśnienie do problemu. Jest to klasyczny problem, w którym musimy wykorzystać fakt, że dwie tablice są posortowane. otrzymaliśmy dwie posortowane tablice arr1 o rozmiarze sz1 i arr2 o rozmiarze sz2
a) Załóżmy, że jeśli
Sprawdzanie, czy k jest poprawne
wtedy nie możemy znaleźć k-tego najmniejszego elementu w unii obu posortowanych tablic rytm So return Invalid data. b) Teraz, jeśli powyższy warunek okaże się fałszywy i mamy ważną i wykonalną wartość k,
Zarządzanie przypadkami brzegowymi
Dodamy obie tablice przez wartości -infinity na początku i + wartości nieskończoności na końcu, aby pokryć skrajne przypadki k = 1,2 i k = (sz1 + sz2-1), (sz1 + sz2) itd.
Główny algorytm
Teraz wykonamy wyszukiwanie binarne na arr1. Wyszukamy binarnie na arr1, szukając indeksu i, startIndex <= i <= endIndex
takie, że jeśli znajdziemy odpowiedni indeks j w arr2 używając ograniczenia {(i + j) = k}, to jeśli
Ponieważ wiemy, że k-ty najmniejszy element ma (k-1) elementów mniejszych od niego w połączeniu obu tablic ryt? Więc,
W przypadku 1 , co zrobiliśmy, upewniliśmy się, że istnieje łącznie (k-1) mniejszych elementów do arr1 [i], ponieważ elementy mniejsze niż arr1 [i] w tablicy arr1 mają liczbę i-1 niż znamy (arr2 [ j-1] <arr1 [i] <arr2 [j]), a liczba elementów mniejsza niż arr1 [i] w arr2 wynosi j-1, ponieważ j jest znalezione przy użyciu (i-1) + (j-1) = (k -1) Więc k-tym najmniejszym elementem będzie arr1 [i]
Ale odpowiedź nie zawsze może pochodzić z pierwszej tablicy, tj. Arr1, więc sprawdziliśmy przypadek2, który również spełnia podobnie jak przypadek 1, ponieważ (i-1) + (j-1) = (k-1). Teraz, jeśli mamy (arr1 [i-1] <arr2 [j] <arr1 [i]), mamy w sumie k-1 elementów mniejszych niż arr2 [j] w połączeniu obu tablic, więc jest to k-ty najmniejszy element.
W przypadku 3 , aby przekształcić go w dowolny przypadek 1 lub 2, musimy zwiększyć i i j zostanie znalezione zgodnie z ograniczeniem {(i + j) = k} tj. W wyszukiwaniu binarnym przejdź do prawej części, tj. Make startIndex = middleIndex
W przypadku 4 , aby przekształcić go w którykolwiek z przypadków 1 lub 2, musimy zmniejszyć i oraz j zostanie znalezione zgodnie z ograniczeniem {(i + j) = k} tj. W wyszukiwaniu binarnym przejdź do lewej części, czyli make endIndex = middleIndex .
Teraz, jak zdecydować startIndex i endIndex na początku wyszukiwania binarnego po arr1 z startindex = 1 i endIndex = ??. Musimy zdecydować.
Ponieważ jeśli k jest większe niż rozmiar pierwszej tablicy, być może będziemy musieli przeprowadzić przeszukiwanie binarne w całej tablicy arr1, w przeciwnym razie musimy wziąć tylko pierwsze k elementów, ponieważ sz1-k elementów nigdy nie przyczyni się do obliczenia k-tego najmniejszego.
KOD pokazano poniżej
Rozwiązanie złożoności (log (n) * log (m))
Po prostu przegapiłem korzyść z faktu, że dla każdego i j można znaleźć za pomocą ograniczenia {(i-1) + (j-1) = (k-1)} Więc dla każdego ii dalej stosowałem wyszukiwanie binarne na drugiej tablicy znaleźć j takie, że arr2 [j] <= arr1 [i]. Więc to rozwiązanie można dalej zoptymalizować
źródło
Zasadniczo, dzięki temu podejściu możesz odrzucić k / 2 elementów na każdym kroku. K będzie się zmieniać rekurencyjnie od k => k / 2 => k / 4 => ... aż osiągnie 1. Tak więc złożoność czasowa wynosi O (logk)
Przy k = 1 otrzymujemy najniższą z dwóch tablic.
Poniższy kod jest w JAVA. Zwróć uwagę, że w kodzie odejmujemy 1 (-1) od indeksów, ponieważ indeks tablicy Java zaczyna się od 0, a nie 1, np. k = 3 jest reprezentowane przez element w drugim indeksie tablicy.
źródło
Złożoność czasowa wynosi O (log (min (n, m)))
źródło
Większość odpowiedzi, które tu znalazłem, dotyczy obu tablic. chociaż jest dobry, ale trudniej go wdrożyć, ponieważ istnieje wiele skrajnych przypadków, którymi musimy się zająć. Poza tym większość implementacji jest rekurencyjna, co zwiększa złożoność przestrzeni stosu rekurencji. Więc zamiast skupiać się na obu tablicach, zdecydowałem się po prostu skupić się na mniejszej tablicy i przeprowadzić wyszukiwanie binarne tylko na mniejszej tablicy i dostosować wskaźnik do drugiej tablicy na podstawie wartości wskaźnika w pierwszej tablicy. Dzięki poniższej implementacji mamy złożoność
O(log(min(n,m))
zeO(1)
złożonością przestrzeni.Mamy zakres
[low, high]
for arraya
i zawężamy ten zakres, przechodząc dalej przez algorytm.sizeA
pokazuje, ile elementów zk
elementów pochodzi z tablicya
i pochodzi z wartościlow
ihigh
.sizeB
to ta sama definicja, z wyjątkiem tego, że obliczamy wartość w ten sposóbsizeA+sizeB=k
. Opierając się na wartościach na tych dwóch granicach, stwierdzamy, że musimy rozciągnąć do prawej strony w tablicya
lub zmniejszyć do lewej strony. jeśli utknęliśmy w tej samej pozycji, oznacza to, że znaleźliśmy rozwiązanie i zwrócimy maksimum wartości z pozycjisizeA-1
oda
isizeB-1
odb
.źródło
Sprawdź ten kod.
źródło
Poniżej kodu C #, aby znaleźć k-ten najmniejszy element w Unii dwóch posortowanych tablic. Złożoność czasowa: O (logk)
źródło
midA
zendA
ifk < n
. Sprawdź krótkie tablice, zaczynając odreturn B[startB + k - 1];
.)