องค์ประกอบหนึ่งที่แตกต่างกันในสองอาร์เรย์ วิธีการค้นหาอย่างมีประสิทธิภาพ?


22

ฉันกำลังเตรียมการสัมภาษณ์การเขียนโปรแกรมและฉันไม่สามารถหาวิธีที่มีประสิทธิภาพที่สุดในการแก้ปัญหานี้ได้

สมมติว่าเรามีสองอาร์เรย์ประกอบด้วยตัวเลขที่ไม่เรียงกัน Array 2 มีหมายเลขที่ Array 1 ไม่มี ทั้งสองอาร์เรย์มีหมายเลขที่ตั้งแบบสุ่มไม่จำเป็นต้องอยู่ในลำดับเดียวกันหรือในดัชนีเดียวกัน ตัวอย่างเช่น:

Array 1 [78,11, 143, 84, 77, 1, 26, 35 .... n]

Array 2 [11,84, 35, 25, 77, 78, 26, 143 ... 21 ... n + 1]

อัลกอริทึมที่เร็วที่สุดสำหรับการค้นหาหมายเลขที่แตกต่างคืออะไร? เวลาทำงานคืออะไร ในตัวอย่างนี้จำนวนที่เราจะค้นหาคือ 21

ความคิดของฉันคือการทำงานผ่าน Array 1 และลบค่านั้นออกจากอาร์เรย์ 2 ซ้ำแล้วซ้ำอีกจนกว่าคุณจะเสร็จสิ้น นี่ควรจะเป็นเวลาประมาณใช่ไหม?O(nlogn)


@Jandvorak ขอบคุณพวกคุณสำหรับคำตอบ ฉันมาสายและเผลอหลับไปหลังจากโพสต์ข้อความนี้ อาร์เรย์ไม่ได้เรียงลำดับและรายการทั้งหมดจะปรากฏที่ดัชนีแบบสุ่มในทั้งสองอาร์เรย์
Konstantino Sparakis

@KonstantinoSparakis: การชี้แจงนี้ทำให้คำตอบที่สมมติว่าอาร์เรย์ทั้งสองนั้นมีองค์ประกอบอยู่ในตำแหน่งเดียวกัน
Mario Cervera

ข้ามการโพสต์ขมวดคิ้วเมื่อ softwareengineering.stackexchange.com/users/256931/ …
paparazzo

@Pararazzi เพียงแค่มองหาวิธีการแก้ปัญหาที่ฉันอ่านในวิศวกรรมซอฟต์แวร์เมตาคือที่ที่จะไปหาทางออก แต่ในเวลาที่ฉันไม่ทราบเกี่ยวกับฟอรั่ม CS ฉันได้แจ้ง mods เพื่อทำความสะอาดแล้ว
Konstantino Sparakis

@Pararazzi มีเมตาโพสต์สำรองไว้หรือไม่ โดยส่วนตัวฉันไม่เห็นวิธีที่จะนำนโยบายนั้นไปใช้ได้ดี
djechlin

คำตอบ:


30

ฉันเห็นสี่วิธีหลักในการแก้ปัญหานี้โดยใช้เวลาในการทำงานต่างกัน:

  • โซลูชัน O ( n 2 ) : นี่จะเป็นโซลูชันที่คุณเสนอ โปรดทราบว่าเนื่องจากอาร์เรย์ไม่ได้เรียงลำดับการลบจึงใช้เวลาเชิงเส้น คุณดำเนินการลบ n ; ดังนั้นอัลกอริทึมนี้ต้องใช้เวลากำลังสองO(n2)n

  • วิธีการแก้ปัญหา: เรียงลำดับอาร์เรย์ไว้ล่วงหน้า; จากนั้นทำการค้นหาเชิงเส้นเพื่อระบุองค์ประกอบที่แตกต่าง ในโซลูชันนี้เวลาทำงานจะถูกควบคุมโดยการดำเนินการเรียงลำดับดังนั้น O ( nO(nlogn)ขอบบนO(nlogn)

เมื่อคุณระบุวิธีแก้ปัญหาคุณควรถามตัวเองอยู่เสมอ: ฉันจะทำได้ดีกว่าได้ไหม ในกรณีนี้คุณสามารถใช้โครงสร้างข้อมูลอย่างชาญฉลาด โปรดทราบว่าสิ่งที่คุณต้องทำคือการวนซ้ำแถวหนึ่งและทำการค้นหาซ้ำในแถวอื่น โครงสร้างข้อมูลใดที่อนุญาตให้คุณค้นหาในเวลาคงที่ (ที่คาดไว้) คุณเดาถูกกตารางแฮช

  • วิธีการแก้ปัญหา (คาดว่า): วนซ้ำอาร์เรย์แรกและเก็บองค์ประกอบไว้ในตารางแฮช จากนั้นทำการสแกนเชิงเส้นในอาเรย์ที่สองค้นหาแต่ละองค์ประกอบในตารางแฮช ส่งคืนองค์ประกอบที่ไม่พบในตารางแฮช วิธีการแก้ปัญหาเชิงเส้นเวลานี้ทำงานสำหรับองค์ประกอบชนิดใดก็ได้ที่คุณสามารถส่งไปยังฟังก์ชันแฮช (เช่นมันจะทำงานคล้ายกับอาร์เรย์ของสตริง)O(n)

หากคุณต้องการการรับประกันแบบ จำกัด ขอบเขตและอาร์เรย์ที่ประกอบไปด้วยจำนวนเต็มอย่างเคร่งครัดทางออกที่ดีที่สุดอาจเป็นสิ่งที่Tobi Alafin แนะนำ (แม้ว่าวิธีนี้จะไม่ให้ดัชนีขององค์ประกอบที่แตกต่างในอาร์เรย์ที่สอง) :

  • โซลูชัน (รับประกัน): สรุปองค์ประกอบของอาร์เรย์แรก จากนั้นสรุปองค์ประกอบของอาร์เรย์ที่สอง ในที่สุดก็ทำการ substraction หมายเหตุว่าวิธีนี้สามารถจริงจะทั่วไปประเภทข้อมูลใด ๆ ที่มีค่าสามารถแสดงเป็นบิตสตริงความยาวคงต้องขอบคุณผู้ประกอบการระดับบิตแฮคเกอร์ นี่คือคำอธิบายอย่างละเอียดในคำตอบ ของ Ilmari KaronenO(n)

ในที่สุดความเป็นไปได้อื่น (ภายใต้สมมติฐานเดียวกันของอาร์เรย์จำนวนเต็ม) จะใช้ algortihm การเรียงลำดับเชิงเส้นเวลาเช่นการเรียงลำดับการนับ สิ่งนี้จะลดเวลาการทำงานของโซลูชันที่ใช้การเรียงลำดับจากจะ O ( n )O(nlogn)O(n)


4
การรวมกันนั้นไม่เชิงเส้นถ้าตัวเลขมีขนาดใหญ่พอ
Sarge Borsch

9
สิ่งหนึ่งที่ดีเกี่ยวกับอัลกอริทึมการรวมคือการทำงานกับกลุ่ม abelian ใด ๆ ไม่ใช่เฉพาะกับจำนวนเต็ม (ที่สะดุดตาที่สุดuint64; cc @sarge)
John Dvorak

6
@Abdul สิ่งคือถ้าจำนวนเต็มของคุณมีขนาดใหญ่มากคุณไม่สามารถหลอกว่าพวกเขาใช้เพื่อเพิ่ม ฉันเชื่อว่าความซับซ้อนจะเพิ่มขึ้นเป็นO ( n ln n )หากคุณอธิบายถึงสิ่งนั้น การใช้ XOR แทนการเพิ่มแบบธรรมดาจะช่วยแก้ปัญหานั้นในขณะที่ยังคงอนุญาตให้ใช้อินพุตจำนวนมากโดยพลการ O(n)O(nlnn)
John Dvorak

2
@JanDvorak ไม่มันไม่ได้เป็นอย่างนั้น คุณกำลังสมมติว่าการดำเนินการที่กำหนดไว้ในกลุ่ม Abelian ใช้เวลาคงที่ ไม่สามารถคาดเดาได้
UTF-8

2
@ UTF-8 ฉันไม่ได้คิดเอาเอง แต่มันทำเช่นนั้นในกลุ่ม จำกัด (uint64) และการเพิ่มด้วยหลักที่ชาญฉลาด (การเพิ่มใน ) คือขนาดเชิงเส้นของตัวถูกดำเนินการนอกสถานที่ ดังนั้นการคำนวณผลรวมในกลุ่มดังกล่าวจึงเป็นเวลาเชิงเส้นในขนาดรวมของตัวถูกดำเนินการ Znd
John Dvorak

16

ความแตกต่างของผลรวมโซลูชั่นที่นำเสนอโดยTobiและมาริโอสามารถในความเป็นจริงจะได้รับการทั่วไปประเภทข้อมูลใด ๆ ที่เราสามารถกำหนด (คงที่เวลา) ดำเนินการทวิภาคที่เป็น:Θ(n)

  • รวมเช่นว่าค่าใด ๆและ , ถูกกำหนดและประเภทเดียวกัน (หรืออย่างน้อยที่สุดของ supertype เหมาะสมบางส่วนของมันซึ่งผู้ประกอบการยังคงกำหนด);abab
  • เชื่อมโยงดังกล่าวว่า( ) = ( ) ;a(bc)=(ab)c
  • commutativeเช่น ; และab=ba
  • cancellativeเช่นว่ามีผู้ประกอบการผกผันที่น่าพอใจ( ) = เทคนิคการดำเนินการผกผันนี้ไม่ได้จำเป็นต้องคงที่เวลาตราบใดที่ "ลบ" สองผลบวกของnองค์ประกอบแต่ละไม่ได้ใช้เวลานานกว่าO ( n )เวลา(ab)b=anO(n)

(ถ้าชนิดเท่านั้นที่สามารถใช้จำนวน จำกัด ของค่าที่แตกต่างกันคุณสมบัติเหล่านี้มีเพียงพอที่จะทำให้มันเป็นกลุ่มศาสนาคริสต์แม้ว่าไม่ก็อย่างน้อยจะมีการสับเปลี่ยน semigroup cancellative .)

เช่นการใช้การดำเนินการเราสามารถกำหนด "ผลรวม" ของอาร์เรย์= ( 1 , 2 , ... , n )เป็น( a=(a1,a2,,an) ได้รับอีกอาร์เรย์= ( 1 , 2 , ... , n , n + 1 )ที่มีองค์ประกอบทั้งหมดของบวกหนึ่งองค์ประกอบเสริม xเราจึงมี (

(a)=a1a2an.
b=(b1,b2,,bn,bn+1)ax , และเพื่อให้เราสามารถหาองค์ประกอบพิเศษนี้ได้โดยการคำนวณ: x = ( (b)=(a)x
x=(b)(a).

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

โดยทั่วไปเราสามารถใช้วิธีการ XOR bitwise กับสตริงที่มีความยาวผันแปรได้โดยการขยายให้ยาวเท่ากับความยาวเท่าที่จำเป็นตราบใดที่เรามีวิธีที่จะลบการขยายแบบกลับด้านในตอนท้าย

ในบางกรณีนี่เป็นเรื่องเล็กน้อย ตัวอย่างเช่น C-style null สิ้นสุดด้วยไบต์สตริงที่เข้ารหัสความยาวของตนเองโดยนัยดังนั้นการใช้วิธีนี้สำหรับพวกเขานั้นเป็นเรื่องเล็กน้อย: เมื่อ XORing สองสายให้วางหนึ่งอันที่สั้นกว่าด้วย null null เพื่อทำการจับคู่ความยาวของมัน ผลลัพธ์สุดท้าย โปรดทราบว่าสตริงผลรวม XOR กลางสามารถมีไบต์ที่เป็นโมฆะดังนั้นคุณจะต้องเก็บความยาวไว้อย่างชัดเจน (แต่คุณต้องการได้เพียงหนึ่งหรือสองสตริงเท่านั้น)

โดยทั่วไปแล้ววิธีการหนึ่งที่จะใช้ได้กับสตริงบิทโดยพลการนั้นจะใช้การแพ็ดหนึ่งบิตโดยที่บิตอินพุตแต่ละอันจะถูกเติมด้วยบิตเดียวและจากนั้นด้วย0บิตมากเท่าที่จำเป็นเพื่อให้ตรงกับความยาว (เบาะ) ของ สตริงอินพุตที่ยาวที่สุด (แน่นอนว่าการขยายนี้ไม่จำเป็นต้องทำล่วงหน้าอย่างชัดเจนเราสามารถนำไปใช้ได้ตามต้องการขณะคำนวณผลรวม XOR) ในที่สุดเราก็ต้องตัด0บิตต่อท้ายและ1บิตสุดท้ายจาก ผล. อีกทางหนึ่งถ้าเรารู้ว่าสายอักขระนั้นมีค่าไม่เกิน2 321001232ไบต์ยาวเราสามารถเข้ารหัสความยาวของแต่ละสตริงเป็นจำนวนเต็ม 32 บิตและเติมเข้าไปในสตริง หรือเราสามารถเข้ารหัสความยาวของสายอักขระได้โดยใช้รหัสนำหน้าบางส่วนและเติมความยาวให้กับสตริง มีการเข้ารหัสที่เป็นไปได้อื่น ๆ เช่นกัน

ในความเป็นจริงตั้งแต่ใด ๆซึ่งแสดงประเภทข้อมูลเกี่ยวกับความสามารถของคอมพิวเตอร์โดยความหมายจะเป็นตัวแทนเป็นสตริงบิต จำกัด ความยาววิธีนี้ผลตอบแทนถัวเฉลี่ยทั่วไปการแก้ไขปัญหาΘ(n)

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


ว้าวน่าสนใจมากในเรื่องนี้ ขอขอบคุณ @IlmariKaronen
Konstantino Sparakis

14

ฉันจะโพสต์สิ่งนี้เป็นความเห็นต่อคำตอบของ Tobi แต่ฉันยังไม่มีชื่อเสียง

เพื่อเป็นทางเลือกในการคำนวณผลรวมของแต่ละรายการ (โดยเฉพาะอย่างยิ่งถ้าเป็นรายการขนาดใหญ่หรือมีตัวเลขจำนวนมากที่อาจล้นประเภทข้อมูลของคุณเมื่อรวม) คุณสามารถใช้ xor แทน

เพียงคำนวณผลรวม xor (เช่น x [0] ^ x [1] ^ x [2] ... x [n]) ของแต่ละรายการแล้ว xor ทั้งสองค่า สิ่งนี้จะทำให้คุณมีค่าของรายการภายนอก (แต่ไม่ใช่ดัชนี)

นี่ยังคงเป็นO (n)และหลีกเลี่ยงปัญหาใด ๆ เกี่ยวกับการล้น


3
ฉันจะใช้ XOR ด้วยเพราะดูเหมือนว่าจะเป็นเพียงเล็กน้อย แต่เพื่อความเป็นธรรมการโอเวอร์โฟลไม่ใช่ปัญหาตราบใดที่ภาษาที่คุณใช้ในการรองรับการโอเวอร์โฟลว์
Martin Ender

14

องค์ประกอบ = ผลรวม (Array2) - ผลรวม (Array1)

ฉันสงสัยอย่างจริงใจว่านี่เป็นอัลกอริธึมที่เหมาะสมที่สุด แต่เป็นอีกวิธีในการแก้ปัญหาและเป็นวิธีที่ง่ายที่สุดในการแก้ปัญหา หวังว่ามันจะช่วย

หากจำนวนองค์ประกอบที่เพิ่มมีมากกว่าหนึ่งองค์ประกอบจะไม่ทำงาน

คำตอบของฉันมีความซับซ้อนของเวลาทำงานเท่ากันสำหรับกรณีที่ดีที่สุดเลวร้ายที่สุดและโดยเฉลี่ย

แก้ไข
หลังจากที่คิดมาบ้างฉันคิดว่าคำตอบของฉันคือทางออกของคุณ

nn11=n12=n+11=n

2n121=1

2n1+1=2n

Θ(n)

แก้ไข:
เนื่องจากปัญหาบางอย่างกับชนิดข้อมูลผลรวม XOR ตามที่แนะนำโดยreffuจะเหมาะกว่า


โปรดทราบว่าวิธีนี้อาจไม่ได้คำตอบที่ถูกต้องหากค่าของคุณลอยเนื่องจากการสรุปตัวเลขอาจทำให้เกิดข้อผิดพลาดในการปัดเศษ มันจะใช้ได้กับค่าจำนวนเต็มโดยที่ a) ประเภทจำนวนเต็มของคุณมีการกำหนดล้อมรอบพฤติกรรมที่ดีในการล้นหรือ b) คุณเก็บผลรวมในตัวแปรประเภทกว้างพอที่พวกเขาไม่สามารถล้น
Ilmari Karonen

คลาส "BigNum" ของรูบี้สามารถจัดการเรื่องนี้ได้
Tobi Alafin

ไม่สามารถใช้งานได้ถ้าอาร์เรย์ของคุณมีสตริงตัวอย่างหรืออะไรก็ตามที่ไม่สามารถเพิ่มความหมายได้
gnasher729

ใช่ฉันตระหนัก แล้วการใช้ 'XOR' ล่ะ? มันจะใช้งานได้กับการลอยตัวหรือไม่?
Tobi Alafin

ใช่และยังเป็นตัวชี้และโดยทั่วไปสิ่งที่ประกอบด้วยบิตจำนวนคงที่ หลายภาษาไม่สนับสนุนสิ่งนั้น แต่นั่นไม่ใช่ปัญหาพื้นฐาน การเพิ่ม / การลบแบบแยกส่วนจะทำงานในกรณีเดียวกัน
แฮโรลด์

1

สมมติว่าอาร์เรย์ 2 ถูกสร้างขึ้นโดยการใช้อาร์เรย์ 1 และแทรกองค์ประกอบที่ตำแหน่งสุ่มหรืออาร์เรย์ 1 ถูกสร้างขึ้นโดยใช้อาร์เรย์ 2 และลบองค์ประกอบแบบสุ่ม

หากองค์ประกอบอาเรย์ทั้งหมดได้รับการรับรองว่ามีความแตกต่างเวลาจะเป็น O (ln n) คุณเปรียบเทียบองค์ประกอบที่ตำแหน่ง n / 2 หากพวกเขามีค่าเท่ากันองค์ประกอบพิเศษคือจาก n / 2 + 1 ถึงจุดสิ้นสุดของอาร์เรย์มิฉะนั้นจะเป็นตั้งแต่ 0 ถึง n / 2 และอื่น ๆ

หากองค์ประกอบของอาร์เรย์ไม่ได้รับประกันว่าจะแตกต่างกัน: คุณสามารถมี n คูณหมายเลข 1 ในอาร์เรย์ 1 และหมายเลข 2 แทรกที่ใดก็ได้ในอาร์เรย์ 2 ในกรณีนั้นคุณไม่สามารถรู้ได้ว่าหมายเลข 2 อยู่ที่ไหนโดยไม่ต้องดูเลย องค์ประกอบอาร์เรย์ ดังนั้น O (n)

PS เนื่องจากข้อกำหนดมีการเปลี่ยนแปลงตรวจสอบไลบรารีของคุณสำหรับสิ่งที่พร้อมใช้งาน บน MacOS / iOS ของคุณ, คุณสร้าง NSCountedSet เพิ่มตัวเลขทั้งหมดจากแถว 2 เอาตัวเลขทั้งหมดจากแถวที่ 1 และสิ่งที่เหลือคือทุกอย่างที่อยู่ในอาร์เรย์ 2 แต่ไม่ได้อยู่ในอาร์เรย์ที่ 1 โดยไม่ต้องอาศัยการอ้างว่ามีหนึ่งเพิ่มเติม ชิ้น


คำตอบนี้ตรงประเด็น แต่คำถามได้ถูกแก้ไขด้วยข้อกำหนดใหม่ที่ทำให้สมมติฐานของคุณใช้ไม่ได้
Mario Cervera

คำตอบใหม่ของคุณดูเหมือนจะถูก ความซับซ้อนของเวลาคืออะไร
Tobi Alafin

ก่อนอื่นเวลาที่ต้องใช้ในการเขียนรหัสคืออะไร มันเป็นเรื่องไม่สำคัญ NSCountedSet ใช้การแปลงแป้นพิมพ์ดังนั้นเวลาที่ซับซ้อนคือ "มักจะเป็นแบบเส้นตรง"
gnasher729

-1

var ที่สั้นที่สุดยาวที่สุด;

แปลงที่สั้นที่สุดไปยังแผนที่เพื่อการอ้างอิงอย่างรวดเร็วและวนซ้ำที่ยาวที่สุดจนกว่าค่าปัจจุบันจะไม่อยู่ในแผนที่

บางอย่างเช่นนี้ในจาวาสคริปต์:

if (arr1.length> arr2.length) {shortest = arr2; ยาวที่สุด = arr1; } else {shortest = arr1; ยาวที่สุด = arr2; }

var map = shortest.reduce (ฟังก์ชัน (obj, value) {obj [value] = true; return obj;}, {});

var difference = longest.find (ฟังก์ชั่น (ค่า) {return !!! map [value];});


รหัสที่ไม่มีคำอธิบายจะไม่ถือว่าเป็นคำตอบที่ดีที่นี่ ทำไมคุณต้องใช้ !!! ?
Evil

-1

โซลูชัน O (N) ในความซับซ้อนของเวลา O (1) ในแง่ของความซับซ้อนของพื้นที่

คำแถลงปัญหา: สมมติว่า array2 มีองค์ประกอบทั้งหมดของ array1 บวกองค์ประกอบหนึ่งที่ไม่ได้อยู่ใน array1

วิธีแก้คือ: เราใช้ xor เพื่อหาองค์ประกอบที่ไม่ได้อยู่ใน array1 ดังนั้นขั้นตอนคือ: 1. เริ่มจาก array1 และทำ xor ขององค์ประกอบทั้งหมดและเก็บไว้ในตัวแปร 2. ใช้ array2 และทำ xor ขององค์ประกอบทั้งหมดด้วยตัวแปรที่เก็บ xor ของ array1 3. หลังจากดำเนินการแล้วตัวแปรของเราจะมีองค์ประกอบที่มีเฉพาะในอาเรย์ 2 อัลกอริทึมด้านบนใช้งานได้เนื่องจากคุณสมบัติต่อไปนี้ของ xor "a xor a = 0" "a xor 0 = a" ฉันหวังว่านี่จะช่วยแก้ปัญหาของคุณได้ ด้วยวิธีแก้ปัญหาที่แนะนำข้างต้นก็ใช้ได้เช่นกัน

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