Biorąc pod uwagę tablicę liczb całkowitych, A 1 , A 2 , ..., A n , w tym ujemne i dodatnie, oraz inną liczbę całkowitą S. Teraz musimy znaleźć trzy różne liczby całkowite w tablicy, których suma jest najbliższa podanej liczbie całkowitej S Jeśli istnieje więcej niż jedno rozwiązanie, każde z nich jest w porządku.
Możesz założyć, że wszystkie liczby całkowite mieszczą się w zakresie int32_t i przy obliczaniu sumy nie nastąpi przepełnienie arytmetyczne. S to nic specjalnego, tylko losowo wybrana liczba.
Czy istnieje inny skuteczny algorytm niż wyszukiwanie siłowe do znalezienia trzech liczb całkowitych?
Odpowiedzi:
Tak; można rozwiązać w O (n = 2 ) Czas! Po pierwsze, zastanów się, że problem
P
można sformułować równoważnie w nieco inny sposób, co eliminuje potrzebę określenia „wartości docelowej”:Zauważ że można przejść z tą wersją problemu
P'
zP
odejmując swój S / 3 z każdego elementuA
, ale teraz nie trzeba już wartość docelową.Oczywiście, gdybyśmy po prostu przetestowali wszystkie możliwe 3 krotki, rozwiązalibyśmy problem w O (n 3 ) - to jest linia bazowa brutalnej siły. Czy można zrobić lepiej? A jeśli wybierzemy krotki w nieco sprytniejszy sposób?
Najpierw poświęcamy trochę czasu na posortowanie tablicy, co kosztuje nas początkową karę w wysokości O (n log n). Teraz wykonujemy ten algorytm:
Algorytm ten działa poprzez umieszczenie trzy wskaźniki,
i
,j
, ik
w różnych punktach w tablicy.i
zaczyna się na początku i powoli dociera do końca.k
wskazuje na ostatni element.j
wskazuje, gdziei
się zaczęło. Iteracyjnie próbujemy zsumować elementy w ich odpowiednich indeksach i za każdym razem, gdy zachodzi jedna z poniższych sytuacji:j
się do końca, aby wybrać następną największą liczbę.k
bliżej początku, aby wybrać następną najmniejszą liczbę.Dla każdego
i
wskaźnikij
ik
będą się do siebie stopniowo zbliżać. W końcu mijają się nawzajem i na tym etapie nie musimy próbować niczego innegoi
, ponieważ sumowalibyśmy te same elementy, tylko w innej kolejności. Następnie próbujemy następnegoi
i powtarzamy.W końcu albo wyczerpimy przydatne możliwości, albo znajdziemy rozwiązanie. Widać, że jest to O (n 2 ), ponieważ wykonujemy zewnętrzną pętlę O (n) razy, a wewnętrzną pętlę wykonujemy O (n) razy. Można to zrobić pod-kwadratowo, jeśli masz naprawdę ochotę, przedstawiając każdą liczbę całkowitą jako wektor bitowy i wykonując szybką transformatę Fouriera, ale to wykracza poza zakres tej odpowiedzi.
Uwaga: ponieważ jest to pytanie do wywiadu, trochę tutaj oszukałem: ten algorytm pozwala na wielokrotny wybór tego samego elementu. Oznacza to, że (-1, -1, 2) byłoby prawidłowym rozwiązaniem, podobnie jak (0, 0, 0). Znajduje również tylko dokładne odpowiedzi, a nie najbliższą odpowiedź, jak wspomina tytuł. Jako ćwiczenie dla czytelnika, pozwolę ci dowiedzieć się, jak sprawić, by działało tylko z różnymi elementami (ale to bardzo prosta zmiana) i dokładnymi odpowiedziami (co jest również prostą zmianą).
źródło
z pewnością jest to lepsze rozwiązanie, ponieważ jest łatwiejsze do odczytania, a przez to mniej podatne na błędy. Jedynym problemem jest to, że musimy dodać kilka wierszy kodu, aby uniknąć wielokrotnego wybierania jednego elementu.
Kolejne rozwiązanie O (n ^ 2) (przy użyciu skrótu).
źródło
s2
może być już wybranym elementem. Np. Jeśli tablica jest0,1,2
iK
jest2
, nie powinno być odpowiedzi. Myślę, że twój algorytm wyświetli wynik,0,1,1
co jest oczywiście niepoprawne.Rozwiązanie Johna Feminelli zawiera błąd.
Na linii
Musimy sprawdzić, czy i, j, k są różne. W przeciwnym razie, jeśli mój element docelowy to
6
i jeśli moja tablica wejściowa zawiera{3,2,1,7,9,0,-4,6}
. Jeśli wydrukuję krotki, które sumują się do 6, otrzymam również0,0,6
jako wyjście. Aby tego uniknąć, musimy w ten sposób zmodyfikować warunek.źródło
Co powiesz na coś takiego, czyli O (n ^ 2)
Sprawdza, czy suma 3 elementów jest dokładnie równa Twojej liczbie. Jeśli chcesz najbliżej, możesz ją zmodyfikować, aby zapamiętać najmniejszą deltę (różnicę między twoją liczbą bieżących trioli), a na końcu wydrukować trójkę odpowiadającą najmniejszej delcie.
źródło
Zauważ, że mamy posortowaną tablicę. To rozwiązanie jest podobne do rozwiązania Johna, tylko że szuka sumy i nie powtarza tego samego elementu.
źródło
a[r] + a[l] + a[i] - sum
. Przymierzarr = [-1, 2, 1, -4] sum = 1
.Oto kod w C ++:
źródło
Bardzo proste rozwiązanie N ^ 2 * logN: posortuj tablicę wejściową, następnie przejdź przez wszystkie pary A i , A j (N ^ 2 razy) i dla każdej pary sprawdź, czy (S - A i - A j ) jest w tablicy ( czas logowania).
Inne rozwiązanie O (S * N) wykorzystuje klasyczne podejście do programowania dynamicznego .
W skrócie:
Utwórz dwuwymiarową macierz V [4] [S + 1]. Wypełnij w taki sposób, aby:
V [0] [0] = 1, V [0] [x] = 0;
V 1 [A i ] = 1 dla dowolnego i, V 1 [x] = 0 dla wszystkich pozostałych x
V [2] [A i + A j ] = 1, dla każdego i, j. V [2] [x] = 0 dla wszystkich pozostałych x
V [3] [suma dowolnych 3 elementów] = 1.
Aby go wypełnić, wykonaj iterację przez A i , dla każdego A i przejdź przez tablicę od prawej do lewej.
źródło
Można to skutecznie rozwiązać w O (n log (n)) w następujący sposób. Podaję rozwiązanie, które mówi, czy suma dowolnych trzech liczb jest równa danej liczbie.
źródło
leftIndex
lubrightIndex
kiedy wszystkie elementy w środku są albo ściśle mniejsze, albo większe od pożądanej liczby. Ale co w przypadku, gdy wyszukiwanie binarne zatrzymało się gdzieś pośrodku? Musisz sprawdzić oba oddziały (gdzierightIndex--
ileftIndex++
). W swoim rozwiązaniu po prostu ignorujesz tę sytuację. Ale nie sądzę, aby można było rozwiązać ten problem.Redukcja: myślę, że rozwiązanie @John Feminella O (n2) jest najbardziej eleganckie. Nadal możemy zredukować A [n], w którym szukać krotki. Obserwując A [k] tak, że wszystkie elementy znajdowałyby się w A [0] - A [k], gdy nasza tablica wyszukiwania jest ogromna, a SUMA (s) naprawdę małe.
Minimum [0]: - rosnąco posortowana tablica.
s = 2A [0] + A [k]: Biorąc pod uwagę s i A [], możemy znaleźć A [k] używając wyszukiwania binarnego w czasie log (n).
źródło
Oto program w javie, czyli O (N ^ 2)
źródło
Problem można rozwiązać w O (n ^ 2), rozszerzając problem sumy 2 z niewielkimi modyfikacjami: A jest wektorem zawierającym elementy, a B jest sumą wymaganą.
int Rozwiązanie :: threeSumClosest (vector & A, int B) {
źródło
Oto kod Python3
źródło
Inne rozwiązanie, które wcześnie sprawdza i zawodzi:
Dodałem tutaj kilka testów jednostkowych: GivenArrayReturnTrueIfThreeElementsSumZeroTest .
Jeśli zestaw zużywa zbyt dużo miejsca można łatwo używać java.util.BitSet że użyje O (n / w) przestrzeni .
źródło
Program, aby uzyskać te trzy elementy. Właśnie posortowałem najpierw tablicę / listę i zaktualizowałem je na
minCloseness
podstawie każdej trójki.źródło
Zrobiłem to w n ^ 3, mój pseudokod jest poniżej;
// Utwórz mapę mieszania z kluczem jako liczbą całkowitą i wartością jako ArrayList // iteruj po liście przy użyciu pętli for, dla każdej wartości na liście wykonaj iterację ponownie, zaczynając od następnej wartości;
// jeśli suma arr [i] i arr [j] jest mniejsza niż żądana suma, to istnieje możliwość znalezienia trzeciej cyfry, więc wykonaj kolejną pętlę for
// w tym przypadku szukamy teraz trzeciej wartości; jeśli suma arr [i] i arr [j] i arr [k] jest sumą pożądaną, to dodaj je do HashMap, tworząc klucz arr [i], a następnie dodając arr [j] i arr [k] do ArrayList w wartości tego klucza
po tym masz teraz słownik, który zawiera wszystkie wpisy reprezentujące trzy wartości dodawane do żądanej sumy. Wyodrębnij wszystkie te wpisy za pomocą funkcji HashMap. To działało doskonale.
źródło