Có thuật toán hiệu quả nào khác ngoài tìm kiếm vũ phu để tìm ba số nguyên không?
Vâng; chúng ta có thể giải quyết điều này trong thời gian O (n 2 )! Đầu tiên, hãy xem xét rằng vấn đề của bạn P
có thể được diễn đạt tương đương theo một cách hơi khác để loại bỏ sự cần thiết của "giá trị mục tiêu":
vấn đề ban đầu P
: Cho một mảng A
các n
số nguyên và giá trị đích S
, có tồn tại 3 tuple từ A
số tiền đó S
không?
vấn đề được sửa đổi P'
: Cho một mảng A
các n
số nguyên, có tồn tại 3-tuple từ A
tổng đó thành 0 không?
Chú ý rằng bạn có thể đi từ phiên bản này của vấn đề P'
từ P
bằng cách trừ S / 3 của bạn từ mỗi phần tử trong A
, nhưng bây giờ bạn không cần giá trị mục tiêu nữa.
Rõ ràng, nếu chúng ta chỉ đơn giản kiểm tra tất cả các bộ 3 có thể, chúng ta sẽ giải quyết vấn đề trong O (n 3 ) - đó là đường cơ sở vũ phu. Có thể làm tốt hơn? Điều gì sẽ xảy ra nếu chúng ta chọn các bộ dữ liệu theo cách thông minh hơn?
Đầu tiên, chúng tôi đầu tư một chút thời gian để sắp xếp mảng, khiến chúng tôi phải chịu một hình phạt ban đầu là O (n log n). Bây giờ chúng tôi thực hiện thuật toán này:
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.
}
Thuật toán này hoạt động bằng cách đặt ba con trỏ, i
, j
, và k
tại các điểm khác nhau trong mảng. i
bắt đầu từ đầu và từ từ làm việc đến cuối. k
chỉ đến yếu tố cuối cùng j
chỉ vào nơi i
đã bắt đầu tại. Chúng tôi lặp đi lặp lại cố gắng tổng hợp các yếu tố tại các chỉ số tương ứng của chúng và mỗi lần xảy ra một trong những điều sau đây:
- Tổng hợp là chính xác! Chúng tôi đã tìm thấy câu trả lời.
- Số tiền quá nhỏ. Di chuyển
j
đến gần cuối để chọn số lớn nhất tiếp theo.
- Số tiền quá lớn. Di chuyển
k
đến gần đầu để chọn số nhỏ nhất tiếp theo.
Đối với mỗi i
, con trỏ của j
và k
sẽ dần dần gần nhau hơn. Cuối cùng, họ sẽ vượt qua nhau và tại thời điểm đó, chúng tôi không cần phải thử bất cứ điều gì khác cho điều đó i
, vì chúng tôi sẽ tổng hợp các yếu tố giống nhau, chỉ theo một thứ tự khác. Sau thời điểm đó, chúng tôi thử tiếp theo i
và lặp lại.
Cuối cùng, chúng ta sẽ cạn kiệt các khả năng hữu ích, hoặc chúng ta sẽ tìm ra giải pháp. Bạn có thể thấy rằng đây là O (n 2 ) vì chúng ta thực hiện vòng lặp O (n) vòng ngoài và chúng ta thực hiện vòng lặp O (n) vòng trong. Bạn có thể thực hiện điều này theo phương pháp bậc hai nếu bạn thực sự thích, bằng cách biểu diễn mỗi số nguyên dưới dạng một vectơ bit và thực hiện một phép biến đổi Fourier nhanh, nhưng điều đó nằm ngoài phạm vi của câu trả lời này.
Lưu ý: Vì đây là câu hỏi phỏng vấn, tôi đã lừa một chút ở đây: thuật toán này cho phép lựa chọn cùng một yếu tố nhiều lần. Đó là, (-1, -1, 2) sẽ là một giải pháp hợp lệ, như (0, 0, 0). Nó cũng chỉ tìm thấy câu trả lời chính xác , không phải câu trả lời gần nhất, như tiêu đề đề cập. Như một bài tập cho người đọc, tôi sẽ cho phép bạn tìm ra cách làm cho nó hoạt động chỉ với các yếu tố riêng biệt (nhưng đó là một thay đổi rất đơn giản) và câu trả lời chính xác (cũng là một thay đổi đơn giản).