Чи існує якийсь ефективний алгоритм, окрім грубої сили пошуку, щоб знайти три цілі числа?
Так; ми можемо вирішити це за O (n 2 ) час! По-перше, врахуйте, що вашу проблему P
можна вирівняти рівнозначно дещо по-іншому, що виключає необхідність "цільового значення":
оригінальна проблема P
: Дано масив A
з n
цілих чисел і заданого значення S
, чи існує 3-кортеж від того, A
що суми в S
?
змінена проблема P'
: Дано масив A
з n
цілих чисел, чи існує 3-кортеж від того, A
що суми до нуля?
Зверніть увагу , що ви можете перейти від цієї версії проблеми P'
з P
вирахуванням вашого S / 3 від кожного елемента A
, але тепер вам не потрібно цільове значення більше.
Зрозуміло, що якщо ми просто перевіримо всі можливі 3-кортежі, ми вирішили б проблему в O (n 3 ) - це основна лінія грубої сили. Чи можна зробити краще? Що робити, якщо ми збираємо кортежі дещо розумнішим способом?
По-перше, ми вкладаємо певний час для сортування масиву, що коштує нам початкового штрафу O (n log n). Тепер ми виконуємо цей алгоритм:
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.
}
Цей алгоритм працює шляхом розміщення трьох покажчиків, i
, j
, і k
в різних точках масиву. i
починається на початку і повільно працює до кінця. k
вказує на самий останній елемент.j
вказує на те, з чого i
почалося. Ми ітеративно намагаємося підсумовувати елементи за відповідними індексами, і кожного разу відбувається таке:
- Сума цілком правильна! Ми знайшли відповідь.
- Сума була занадто мала. Рухатися
j
ближче до кінця, щоб вибрати наступне найбільше число.
- Сума була занадто великою. Рухатися
k
ближче до початку, щоб вибрати наступне найменше число.
Для кожного i
вказівники j
та k
поступово наближатимуться один до одного. Врешті-решт вони пройдуть один одного, і в цей момент нам не потрібно нічого іншого намагатисяi
, оскільки ми підсумовуємо ті самі елементи, просто в іншому порядку. Після цього ми пробуємо наступне i
і повторюємо.
Зрештою ми або вичерпаємо корисні можливості, або знайдемо рішення. Ви можете бачити, що це О (n 2 ), оскільки ми виконуємо зовнішній цикл O (n) разів і виконуємо внутрішній цикл O (n) разів. Це можна зробити підквадратично, якщо ви по-справжньому фантазії, представляючи кожне ціле число як бітовий вектор і виконуючи швидке перетворення Фур'є, але це виходить за межі цієї відповіді.
Примітка: Оскільки це питання інтерв'ю, я тут трохи обдурив: цей алгоритм дозволяє вибирати один і той же елемент кілька разів. Тобто, (-1, -1, 2) було б дійсним рішенням, як і (0, 0, 0). Він також знаходить лише точні відповіді, а не найближчу відповідь, як зазначає заголовок. Як вправу для читача, я дозволю вам зрозуміти, як змусити його працювати лише з різними елементами (але це дуже проста зміна) і точними відповідями (що також є простою зміною).