วิธีที่มีประสิทธิภาพในการค้นหาองค์ประกอบ


88

เมื่อเร็ว ๆ นี้ฉันได้ให้สัมภาษณ์ซึ่งพวกเขาถามคำถาม " ค้นหา "
คำถามคือ:

ถือว่ามีอาร์เรย์ของ (บวก) จำนวนเต็มซึ่งแต่ละองค์ประกอบเป็นอย่างใดอย่างหนึ่ง+1หรือ-1เมื่อเทียบกับองค์ประกอบที่อยู่ใกล้เคียง

ตัวอย่าง:

ตอนนี้ค้นหา7และส่งคืนตำแหน่ง

ฉันให้คำตอบนี้:

จัดเก็บค่าในอาร์เรย์ชั่วคราวเรียงลำดับแล้วใช้การค้นหาแบบไบนารี

หากพบองค์ประกอบให้คืนตำแหน่งในอาร์เรย์ชั่วคราว
(หากตัวเลขเกิดขึ้นสองครั้งให้คืนค่าที่เกิดขึ้นครั้งแรก)

แต่ดูเหมือนพวกเขาจะไม่พอใจกับคำตอบนี้

คำตอบที่ถูกต้องคืออะไร?


4
เท่าที่ฉันทราบการค้นหาเชิงเส้นเป็นวิธีที่ดีในการค้นหาดัชนีขององค์ประกอบในอาร์เรย์ ฉันยังไม่แน่ใจว่าอัลกอริทึมการค้นหาอื่นที่มีประสิทธิภาพในการค้นหาดัชนีขององค์ประกอบ
Sean Francis N. Ballais

4
หากรับประกันว่า 7 จะปรากฏเพียงครั้งเดียวหรือไม่สำคัญว่าจะส่งคืน 7 ใดคุณสามารถปรับปรุงอัลกอริทึมเชิงเส้นของคำตอบของโคลแมนได้มากขึ้น
user1942027

52
หากโซลูชันดั้งเดิมของคุณต้องการการจัดเรียงจะแย่กว่าการค้นหาเชิงเส้นที่ไร้เดียงสา ดูเหมือนคุณจะไม่ทราบเรื่องนั้น
cubuspl42

5
การเรียงลำดับต้องใช้ O (nlogn) และการค้นหาแบบไบนารีคือ O (logn) หากคุณต้องการค้นหาค่าจำนวนมากจากอาร์เรย์ขนาดใหญ่คำตอบของคุณอาจดีกว่า แต่ถ้าคุณค้นหาเพียงครั้งเดียวอัลกอริทึม O (n) อาจดีกว่า
jingyu9575

23
ฉันไม่รู้ว่าทำไมไม่มีใครพูดถึงเรื่องนี้: วิธีการของคุณไม่เพียง แต่ไม่มีประสิทธิภาพเท่านั้นยังไม่ถูกต้องและที่แย่กว่านั้นคือไม่มีประสิทธิภาพเท่านั้น ความต้องการสำหรับตำแหน่งของจำนวนที่กำหนดในอาร์เรย์เดิม วิธีการของคุณจะส่งกลับตำแหน่งของตัวเลขในอาร์เรย์เรียง ตอนนี้คุณสามารถดึงตำแหน่งเดิมได้โดยการแปลงอาร์เรย์ธรรมดาเป็นอาร์เรย์ของสิ่งที่เพิ่มขึ้น (number, orig_pos) ก่อนที่จะเรียงลำดับ แต่คุณไม่ได้พูดถึงเรื่องนั้นดังนั้นฉันเดาว่าคุณไม่ได้พูดถึงมันในการสัมภาษณ์ด้วย
Tom Zych

คำตอบ:


126

คุณสามารถทำการค้นหาเชิงเส้นที่มีขั้นตอนที่มักจะสูงกว่า 1. สังเกตที่สำคัญคือว่าถ้าเช่นarray[i] == 4และ 7 ยังไม่ได้ปรากฏตัวขึ้นแล้วผู้สมัครต่อไปสำหรับ 7 i+3เป็นที่ดัชนี ใช้ while loop ซึ่งจะส่งตรงไปยังผู้สมัครคนถัดไป

นี่คือการนำไปใช้โดยทั่วไปเล็กน้อย พบการเกิดขึ้นครั้งแรกkในอาร์เรย์ (ขึ้นอยู่กับข้อ จำกัด + = 1) หรือ-1หากไม่เกิดขึ้น:

เอาต์พุต:


8
แม่นยำในสิ่งที่ฉันคิด นี่เป็นO(N)แต่ฉันไม่คิดว่าจะมีวิธีที่เร็วกว่านี้
shapiro yaacov

2
คุณสามารถทำได้เร็วขึ้นเล็กน้อยโดยเฉลี่ยโดยมีผู้สมัครจำนวนมากขึ้น (เช่นคนแรกและคนสุดท้าย) จากนั้นไปกับคนที่ใกล้เป้าหมายมากที่สุดนั่นคือถ้าคุณต้องหาเหตุการณ์เดียวไม่ใช่ครั้งแรก
mkadunc

2
@mkadunc นั่นเป็นความคิดที่ดี ข้อสังเกตอีกประการหนึ่งคือหากองค์ประกอบแรกและองค์ประกอบสุดท้ายคร่อม 7 ในกรณีพิเศษนั้นคุณสามารถใช้การค้นหาแบบไบนารี (หากคุณไม่สนใจว่าคุณจะพบ 7 ตัวใด)
John Coleman

1
ในกรณีที่คุณต้องการค้นหา 7 ข้อใด ๆ (ไม่จำเป็นต้องเป็นข้อแรก) ฉันขอเสนอการปรับปรุง (ในทางปฏิบัติ) ต่อไปนี้ สร้างรายการของส่วนต่างๆ (จำนวนเต็มสองตัว 'start' และ 'end') และแทนที่จะเริ่มต้นที่จุดเริ่มต้นของอาร์เรย์ให้เริ่มตรงกลาง ตามค่าในเซลล์ละเว้นช่วงที่เกี่ยวข้องและเพิ่มสองส่วนที่เหลือในรายการส่วนของคุณ ทำซ้ำสำหรับรายการถัดไปในรายการ นี่ยังคงเป็น 'O (n)' แต่คุณจะไม่สนใจสองเท่าของช่วงทุกครั้งที่ตรวจสอบเซลล์
shapiro yaacov

3
@ShapiroYaacov: เมื่อรวมกับการตรวจสอบว่าช่วงเวลาจากด้านล่างไปยังค่าที่สูงกว่าของค่าทั้งสองด้านรวมถึง k (7) สิ่งนี้สมควรได้รับคำตอบของตัวเองหรือไม่
greybeard

35

แนวทางของคุณซับซ้อนเกินไป คุณไม่จำเป็นต้องตรวจสอบทุกองค์ประกอบของอาร์เรย์ ค่าแรกคือ4เพื่อให้7เป็นอย่างน้อย 7-4องค์ประกอบออกไปและคุณสามารถข้ามเหล่านั้น

ผลลัพธ์ของโปรแกรม:

แก้ไข: ปรับปรุงหลังจากความคิดเห็นจาก @Raphael Miedl และ @Martin Zabel


2
nitpick if ((skip = 7 - array[i]) < 1) skip = 1;ดูเหมือนจะซับซ้อนเกินไปและมองในแง่ร้ายในความคิดของฉัน หากarray[i] == 200คุณได้รับ-193และข้ามไปทีละ 1 ทุกครั้งแม้ว่าคุณจะข้ามได้ทั้งหมด 193 ข้อทำไมไม่ทำi += abs(7 - array[i])ล่ะ?
user1942027

1
คุณควรตั้งค่าskipความแตกต่างแน่นอนระหว่างวันที่ 7 array[i]และ
Martin Zabel

@Raphael Miedl ไม่องค์ประกอบจะไม่เป็น200คุณจะผ่านไป7แล้ว
ใบพัดสภาพอากาศ

3
@WeatherVane เราไม่มีการรับประกันนั้นมีเพียงค่าที่อยู่ติดกันเท่านั้นที่เป็น+1/ -1จากกันและกัน ดังนั้นมันก็อาจจะเป็นarray[0] == 200และคนอื่น ๆ ส่วนใหญ่จะเป็น-1ของ
user1942027

1
@WeatherVane สิ่งนี้มีสมมติฐานว่าพบองค์ประกอบในอาร์เรย์เสมอซึ่งอาจไม่เป็นเช่นนั้น -1 คือผลตอบแทนที่ถูกต้องในกรณีนั้น ซึ่งเปลี่ยนรหัสที่คุณมีไม่น้อย
ยูจีน

20

รูปแบบของการค้นหาเชิงเส้นทั่วไปอาจเป็นวิธีที่ดี array[i] = 2ให้เราเลือกพูดองค์ประกอบ ตอนนี้array[i + 1]จะเป็น 1 หรือ 3 (คี่)array[i + 2]จะเป็น (จำนวนเต็มบวกเท่านั้น) 2 หรือ 4 (เลขคู่)

ในการดำเนินการต่อเช่นนี้รูปแบบสามารถสังเกตได้ - array[i + 2*n]จะมีตัวเลขคู่และดัชนีเหล่านี้ทั้งหมดจึงไม่สามารถมองข้ามได้

นอกจากนี้เรายังสามารถดูได้

ดังนั้นi + 5ควรตรวจสอบดัชนีถัดไปและสามารถใช้ while loop เพื่อกำหนดดัชนีถัดไปที่จะตรวจสอบขึ้นอยู่กับค่าที่พบในดัชนีi + 5ควรจะตรวจสอบต่อไปและในขณะที่วงสามารถนำมาใช้ในการกำหนดดัชนีต่อไปเพื่อตรวจสอบขึ้นอยู่กับค่าที่พบในดัชนี

ขณะนี้มีความซับซ้อน O(n) (เวลาเชิงเส้นในแง่ของความซับซ้อนของ asymptotic) แต่จะดีกว่าการค้นหาเชิงเส้นปกติในแง่ปฏิบัติเนื่องจากไม่ได้เข้าชมดัชนีทั้งหมด

เห็นได้ชัดว่าทั้งหมดนี้จะกลับกันถ้าarray[i](จุดเริ่มต้นของเรา) เป็นเลขคี่


8

แนวทางที่ John Coleman นำเสนอคือสิ่งที่ผู้สัมภาษณ์คาดหวังในความเป็นไปได้ทั้งหมด
หากคุณมีความยินดีที่จะไปไม่น้อยที่มีความซับซ้อนมากขึ้นคุณสามารถเพิ่มความยาวเฮี๊ยบคาดว่า:
โทรค่าเป้าหมายk เริ่มต้นด้วยองค์ประกอบแรกของค่าโวลต์ที่ตำแหน่งPและเรียกแตกต่าง KV DVกับค่าสัมบูรณ์AV การค้นหาเชิงลบความเร็วได้มองที่องค์ประกอบสุดท้ายเป็นค่าอื่น ๆยูในตำแหน่งo: ถ้า dv × du เป็นค่าลบจะมีค่า k อยู่ (หากยอมรับการเกิด k ใด ๆ คุณอาจ จำกัด ช่วงดัชนีให้แคบลงตามวิธีการค้นหาไบนารี) ถ้า av + au มากกว่าความยาวของอาร์เรย์จะไม่มี k (ถ้า DV ×ดู่เป็นศูนย์วี u หรือเท่ากับ k.)
ดัชนีถนัดความถูกต้อง: การวัดนี้ ( "ถัดไป") ตำแหน่งที่ลำดับอาจจะกลับไปที่วีกับ k o = p + 2*avที่อยู่ตรงกลาง:
ถ้า dv × du เป็นลบให้หา k (เรียกซ้ำ?) จาก p + av ถึง o-au;
ถ้าเป็นศูนย์คุณจะเท่ากับ k ที่ o
หาก du เท่ากับ dv และค่าที่อยู่ตรงกลางไม่ใช่ k หรือ au เกิน av
หรือคุณไม่พบ k จาก p + av ถึง o-au
ให้ปล่อยp=o; dv=du; av=au;และตรวจสอบต่อไป
(สำหรับการย้อนกลับไปยังข้อความ '60ies เต็มรูปแบบให้ดูด้วย Courier "ความคิดที่ 2 ครั้งที่ 1" ของฉันคือการใช้o = p + 2*av - 1ซึ่งไม่รวมdu เท่ากับ dv .)


4

ขั้นตอนที่ 1

เริ่มต้นด้วยองค์ประกอบแรกและตรวจสอบว่าเป็น 7 หรือไม่สมมติว่าcเป็นดัชนีของตำแหน่งปัจจุบัน c = 0ดังนั้นในขั้นต้น

ขั้นตอนที่ 2

ถ้าเป็น 7 คุณจะพบดัชนี มันc. หากคุณมาถึงจุดสิ้นสุดของอาร์เรย์แล้วให้แยกออก

ขั้นตอนที่ 3

หากไม่เป็นเช่นนั้น 7 จะต้องอยู่|array[c]-7|ห่างออกไปอย่างน้อยที่สุดเนื่องจากคุณสามารถเพิ่มหน่วยต่อดัชนีได้เท่านั้น ดังนั้นเพิ่ม|array[c]-7|ในดัชนีปัจจุบันของคุณ c และไปที่ขั้นตอนที่ 2 อีกครั้งเพื่อตรวจสอบ

ในกรณีที่เลวร้ายที่สุดเมื่อมีตัวเลือก 1 และ -1 ความซับซ้อนของเวลาอาจถึง O (n) แต่กรณีโดยเฉลี่ยจะถูกส่งอย่างรวดเร็ว


สิ่งนี้แตกต่างจากคำตอบของ John Coleman อย่างไร? (นอกเหนือจากการแนะนำใน|c-7|ที่ที่ |array[c]-7|ดูเหมือนถูกเรียก)
greybeard

ฉันเพิ่งเห็นคำตอบของเขา ฉันยอมรับว่าความคิดหลักเหมือนกัน
Akeshwar Jha

คำถามเดิมไม่ได้กำหนดว่าอาร์เรย์เริ่มต้นด้วยตัวเลขที่น้อยกว่า 7 ดังนั้นarray[c]-7อาจเป็นบวกหรือลบ คุณต้องสมัครabs()ก่อนที่จะข้ามไปข้างหน้า
arielf

ใช่คุณพูดถูก. นั่นเป็นเหตุผลที่ผมใช้กับผู้ประกอบการโมดูลัส,array[c] - 7 |array[c] - 7|
Akeshwar Jha

4

ที่นี่ฉันกำลังให้การใช้งานใน java ...


2
รหัสที่ไม่มีเอกสารในภาษาที่มีอนุสัญญากึ่งทางการเป็นอย่างน้อย สิ่งนี้แตกต่างจากคำตอบของ John Coleman และ Akeshwar อย่างไรนอกเหนือจากการตีความแท็ก "c" อย่างเสรี
greybeard

3

นี่คือวิธีแก้ปัญหาแบบแบ่งและพิชิต ด้วยค่าใช้จ่ายในการทำบัญชีที่มากขึ้น (มาก) เราสามารถข้ามองค์ประกอบต่างๆได้มากขึ้น แทนที่จะสแกนจากซ้ายไปขวาให้ทดสอบตรงกลางแล้วข้ามไปทั้งสองทิศทาง


neal-fultz คำตอบของคุณจะไม่แสดงผลครั้งแรก แต่จะเกิดขึ้นแบบสุ่มขององค์ประกอบการค้นหาในขณะที่คุณเริ่มจากตรงกลางและข้ามจากด้านใดด้านหนึ่ง
รามภัทรา

การสลับลำดับการเรียกซ้ำถือเป็นแบบฝึกหัดสำหรับผู้อ่าน
Neal Fultz

1
neal-fultz แล้วโปรดแก้ไขข้อความในการเรียก method printf () ของคุณ
รามภัทรา

2

ต้องการรวมวิธีแก้ปัญหาแบบวนซ้ำสำหรับปัญหา สนุก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.