세 개의 정수를 찾기 위해 무차별 대입 검색 이외의 효율적인 알고리즘이 있습니까?
네; 우리는 이것을 O (n 2 ) 시간 안에 해결할 수 있습니다 ! 먼저, P
"목표 값"의 필요성을 제거하면서 약간 다른 방식으로 문제를 표현할 수 있다고 생각하십시오 .
원래의 문제 P
: 배열을 지정해 A
의 n
정수와 목표 값 S
으로부터 3 튜플이 존재 A
하는 그 합계를 S
?
수정 된 문제 P'
: 정수 배열 A
이 주어지면 그 합계에서 0 n
으로 3 튜플 A
이 있습니까?
의 각 요소에서 S / 3를 빼서이 버전의 문제 P'
에서 P
벗어날 수 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
하고 반복합니다.
결국 우리는 유용한 가능성을 소진하거나 해결책을 찾을 것입니다. 외부 루프 O (n) 시간을 실행하고 내부 루프 O (n) 시간을 실행하기 때문에 이것이 O (n 2 ) 임을 알 수 있습니다 . 각 정수를 비트 벡터로 표현하고 빠른 푸리에 변환을 수행하여 정말 멋진 경우이 점을 차등 적으로 수행 할 수는 있지만이 답변의 범위를 벗어납니다.
참고 : 이것은 인터뷰 질문이기 때문에 여기에서 약간 부정했습니다.이 알고리즘은 동일한 요소를 여러 번 선택할 수 있습니다. 즉, (-1, -1, 2)는 (0, 0, 0)과 마찬가지로 유효한 솔루션입니다. 제목에서 언급했듯이 가장 가까운 답이 아닌 정확한 답만 찾습니다 . 독자들에게 연습으로, 당신은 그것이 독특한 요소들 (매우 간단한 변화 임)과 정확한 답변들 (또한 간단한 변화이기도 함)에서만 작동하게하는 방법을 알아낼 것입니다.