มีอัลกอริทึมที่มีประสิทธิภาพนอกเหนือจากการค้นหาแบบ brute force เพื่อหาจำนวนเต็มสามตัวหรือไม่?
อ๋อ; เราสามารถแก้ปัญหานี้ในเวลา O (n 2 )! ก่อนอื่นให้พิจารณาว่าปัญหาของคุณP
อาจถูกใช้ถ้อยคำอย่างเท่าเทียมกันในวิธีที่แตกต่างกันเล็กน้อยซึ่งไม่จำเป็นต้องใช้ "มูลค่าเป้าหมาย":
ปัญหาดั้งเดิมP
:กำหนดอาร์เรย์A
ของn
จำนวนเต็มและค่าเป้าหมายS
, มี 3-tuple จากA
ผลรวมนั้นS
หรือไม่?
ปัญหาที่แก้ไขP'
:กำหนดอาร์เรย์A
ของn
จำนวนเต็มมี 3-tuple จากA
ผลรวมนั้นเป็นศูนย์หรือไม่
ขอให้สังเกตว่าคุณสามารถไปจากรุ่นของปัญหานี้P'
มาจากP
โดยการลบ S / 3 ของคุณจากองค์ประกอบในแต่ละA
แต่ตอนนี้คุณไม่จำเป็นต้องมีค่าเป้าหมายอีกต่อไป
เห็นได้ชัดว่าถ้าเราทดสอบ 3 tuples ที่เป็นไปได้ทั้งหมดเราจะแก้ปัญหาใน 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 2 ) เนื่องจากเราดำเนินการลูปด้านนอก O (n) ครั้งและเราดำเนินการลูปภายใน O (n) ครั้ง เป็นไปได้ที่จะทำ sub-quadratically นี้ถ้าคุณนึกออกจริง ๆ โดยการแทนจำนวนเต็มแต่ละตัวเป็นบิตเวกเตอร์และทำการแปลงฟูริเยร์ที่รวดเร็ว แต่มันอยู่นอกเหนือขอบเขตของคำตอบนี้
หมายเหตุ:เนื่องจากนี่เป็นคำถามสัมภาษณ์ฉันจึงโกงนิดหน่อยที่นี่: อัลกอริทึมนี้อนุญาตให้เลือกองค์ประกอบเดียวกันหลาย ๆ ครั้ง นั่นคือ (-1, -1, 2) จะเป็นโซลูชันที่ถูกต้องเช่นเดียวกับ (0, 0, 0) นอกจากนี้ยังค้นหาเฉพาะคำตอบที่แน่นอนไม่ใช่คำตอบที่ใกล้เคียงที่สุดตามที่กล่าวถึงชื่อ ในฐานะที่เป็นแบบฝึกหัดให้กับผู้อ่านฉันจะให้คุณหาวิธีทำให้มันใช้งานได้กับองค์ประกอบที่แตกต่างเท่านั้น (แต่เป็นการเปลี่ยนแปลงที่ง่ายมาก) และคำตอบที่แน่นอน (ซึ่งก็เป็นการเปลี่ยนแปลงที่ง่ายด้วย)