Czy istnieje inny skuteczny algorytm niż wyszukiwanie siłowe do znalezienia trzech liczb całkowitych?
Tak; można rozwiązać w O (n = 2 ) Czas! Po pierwsze, zastanów się, że problem Pmożna sformułować równoważnie w nieco inny sposób, co eliminuje potrzebę określenia „wartości docelowej”:
Oryginalny problemu P: Biorąc pod tablicę Az nliczb i wartości docelowej S, czy istnieje taka 3-krotki z Atym, że sum S?
Problem modyfikowany P': Biorąc pod tablicę Az nliczb całkowitych, czy istnieje taka 3-krotki z Atym, że kwoty do zera?
Zauważ że można przejść z tą wersją problemu P'z Podejmując swój S / 3 z każdego elementu A, 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:
for (i in 1..n-2) {
j = i+1 // Start right after i.
k = n // Start at the end of the array.
while (k >= j) {
// We got a match! All done.
if (A[i] + A[j] + A[k] == 0) return (A[i], A[j], A[k])
// We didn't match. Let's try to get a little closer:
// If the sum was too big, decrement k.
// If the sum was too small, increment j.
(A[i] + A[j] + A[k] > 0) ? k-- : j++
}
// When the while-loop finishes, j and k have passed each other and there's
// no more useful combinations that we can try with this i.
}
Algorytm ten działa poprzez umieszczenie trzy wskaźniki, i, j, i kw różnych punktach w tablicy. izaczyna się na początku i powoli dociera do końca. kwskazuje na ostatni element. jwskazuje, gdzie isię zaczęło. Iteracyjnie próbujemy zsumować elementy w ich odpowiednich indeksach i za każdym razem, gdy zachodzi jedna z poniższych sytuacji:
- Suma jest dokładnie poprawna! Znaleźliśmy odpowiedź.
- Suma była za mała. Zbliż
jsię do końca, aby wybrać następną największą liczbę.
- Suma była za duża. Przejdź
kbliżej początku, aby wybrać następną najmniejszą liczbę.
Dla każdego iwskaźniki ji kbędą się do siebie stopniowo zbliżać. W końcu mijają się nawzajem i na tym etapie nie musimy próbować niczego innego i, ponieważ sumowalibyśmy te same elementy, tylko w innej kolejności. Następnie próbujemy następnego ii 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ą).