เขียนโปรแกรมเพื่อค้นหาตัวเลขที่ใหญ่ที่สุด 100 รายการจากอาเรย์ 1 พันล้านหมายเลข


300

เมื่อเร็ว ๆ นี้ฉันได้เข้าร่วมสัมภาษณ์ที่ฉันถูกถามว่า "เขียนโปรแกรมเพื่อค้นหาตัวเลขที่ใหญ่ที่สุด 100 รายการจากจำนวน 1 พันล้านรายการ"

ฉันสามารถให้วิธีแก้ปัญหากำลังดุร้ายซึ่งเป็นการเรียงลำดับอาร์เรย์ในความซับซ้อนของเวลา O (nlogn) และรับ 100 หมายเลขล่าสุด

Arrays.sort(array);

ผู้สัมภาษณ์กำลังมองหาช่วงเวลาที่ดีกว่านี้ฉันลองใช้วิธีแก้ปัญหาอื่น ๆ สองสามอย่าง แต่ไม่สามารถตอบเขาได้ มีวิธีแก้ปัญหาความซับซ้อนของเวลาที่ดีกว่านี้หรือไม่?


70
อาจเกิดปัญหาก็คือว่ามันไม่ได้เรียงลำดับคำถาม แต่ที่กำลังมองหาหนึ่ง
geomagas

11
ในฐานะที่เป็นบันทึกทางเทคนิคการเรียงลำดับอาจไม่ใช่วิธีที่ดีที่สุดในการแก้ปัญหา แต่ฉันไม่คิดว่ามันเป็นกำลังที่ดุร้าย - ฉันสามารถคิดวิธีที่แย่กว่านั้นในการทำเช่นนั้น
Bernhard Barker

88
ฉันแค่คิดถึงวิธีบังคับเดรัจฉานที่โง่เขลายิ่งกว่าเดิม ... ค้นหาชุดค่าผสมที่เป็นไปได้ทั้งหมด 100 รายการจากชุดองค์ประกอบ 1 พันล้านชุดและดูว่าชุดค่าผสมใดมีผลรวมมากที่สุด
Shashank

10
โปรดทราบว่าทั้งหมดที่กำหนด (และถูกต้อง) ขั้นตอนวิธีO(1)ในกรณีนี้เพราะไม่มีการเพิ่มมิติ ผู้สัมภาษณ์ควรถามว่า "จะหาองค์ประกอบที่ใหญ่ที่สุดจากอาร์เรย์ n กับ n >> m ได้อย่างไร"
Bakuriu

คำตอบ:


328

คุณสามารถเก็บลำดับความสำคัญของ 100 ตัวเลขที่ใหญ่ที่สุดทำซ้ำผ่านตัวเลขพันล้านเมื่อใดก็ตามที่คุณพบจำนวนที่มากกว่าจำนวนที่เล็กที่สุดในคิว (หัวของคิว) เอาหัวของคิวและเพิ่มหมายเลขใหม่ ไปที่คิว

แก้ไข: ตามที่ Dev ตั้งข้อสังเกตด้วยคิวลำดับความสำคัญที่นำมาใช้กับฮีปความซับซ้อนของการแทรกไปยังคิวคือO(logN)

ในกรณีที่เลวร้ายที่สุดคุณจะได้รับซึ่งดีกว่าbillionlog2(100)billionlog2(billion)

โดยทั่วไปหากคุณต้องการตัวเลข K ที่มากที่สุดจากชุดตัวเลข N ความซับซ้อนจะO(NlogK)มากกว่าO(NlogN)ซึ่งอาจมีความสำคัญมากเมื่อ K มีขนาดเล็กมากเมื่อเทียบกับ N

EDIT2:

เวลาที่คาดหวังของอัลกอริทึมนี้ค่อนข้างน่าสนใจเนื่องจากในแต่ละการวนซ้ำการแทรกอาจหรือไม่เกิดขึ้นก็ได้ ความน่าจะเป็นของหมายเลข i'th ที่จะถูกแทรกลงในคิวคือความน่าจะเป็นของตัวแปรสุ่มที่มีขนาดใหญ่กว่าi-Kตัวแปรสุ่มอย่างน้อยจากการแจกแจงแบบเดียวกัน (ตัวเลข k แรกจะถูกเพิ่มในคิวโดยอัตโนมัติ) เราสามารถใช้สถิติการสั่งซื้อ (ดูลิงค์ ) เพื่อคำนวณความน่าจะเป็นนี้ ตัวอย่างเช่นสมมติว่าตัวเลขถูกสุ่มเลือกจาก{0, 1}ค่าที่คาดหวังของ (iK) หมายเลข th (out of i numbers) คือ(i-k)/iและโอกาสของตัวแปรสุ่มที่ใหญ่กว่าค่านี้คือ1-[(i-k)/i] = k/iและมีโอกาสของตัวแปรสุ่มเป็นขนาดใหญ่กว่าค่านี้

ดังนั้นจำนวนการแทรกที่คาดไว้คือ:

ป้อนคำอธิบายรูปภาพที่นี่

และเวลาทำงานที่คาดหวังสามารถแสดงเป็น:

ป้อนคำอธิบายรูปภาพที่นี่

( kเวลาในการสร้างคิวด้วยkองค์ประกอบแรกจากนั้นทำการn-kเปรียบเทียบและจำนวนการแทรกที่คาดไว้ตามที่อธิบายไว้ข้างต้นแต่ละรายการใช้เวลาเฉลี่ยlog(k)/2)

ทราบว่าเมื่อNมีขนาดใหญ่มากเมื่อเทียบกับKการแสดงออกนี้เป็นจำนวนมากใกล้ชิดกับมากกว่าn NlogKในกรณีของคำถามแม้จะมีการวนซ้ำ 10,000 ครั้ง (ซึ่งน้อยมากเมื่อเทียบกับหนึ่งพันล้านครั้ง) โอกาสที่ตัวเลขที่จะแทรกลงในคิวนั้นมีขนาดเล็กมาก


6
จริงๆแล้วมันเป็นเพียงO (100)สำหรับการแทรกแต่ละครั้ง
MrSmith42

8
@RonTeller คุณไม่สามารถค้นหารายการที่เชื่อมโยงได้อย่างมีประสิทธิภาพแบบไบนารีนั่นคือเหตุผลที่มักจะใช้คิวลำดับความสำคัญกับฮีป เวลาที่คุณแทรกตามที่อธิบายไว้คือ O (n) ไม่ใช่ O (logn) คุณได้ถูกต้องในครั้งแรก (คิวที่สั่งซื้อหรือคิวลำดับความสำคัญ) จนกระทั่ง Skizz ทำให้คุณเดาตัวเองเป็นครั้งที่สอง
Dev

17
@ThomasJungblut พันล้านก็เป็นค่าคงที่ดังนั้นถ้าเป็นกรณีนั้น O (1): P
Ron Teller

9
@RonTeller: โดยปกติแล้วคำถามประเภทนี้เกี่ยวข้องกับการคิดว่าการค้นหาหน้า 10 อันดับแรกจากผลการค้นหาของ Google นับพันล้านรายการหรือ 50 คำที่พบบ่อยที่สุดสำหรับ word cloud หรือ 10 เพลงยอดนิยมใน MTV เป็นต้นผมเชื่อว่าในสถานการณ์ปกติมันปลอดภัยที่จะต้องพิจารณาk อย่างต่อเนื่องและมีขนาดเล็กnเมื่อเทียบกับ แม้ว่าหนึ่งควรจำไว้ว่า "สถานการณ์ปกติ" นี้
เพื่อน

5
เนื่องจากคุณมีรายการ 1G ให้สุ่มตัวอย่างองค์ประกอบ 1000 รายการและเลือก 100 รายการที่ใหญ่ที่สุดซึ่งควรหลีกเลี่ยงเคสที่เสื่อมสภาพ (เรียงลำดับเรียงกลับกันจัดเรียงส่วนใหญ่) ลดจำนวนเม็ดมีด
ChuckCottrill

136

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

คำอธิบายค่อนข้างทั่วไปดังนั้นคุณอาจถามเขาถึงช่วงหรือความหมายของตัวเลขเหล่านี้เพื่อทำให้ปัญหาชัดเจน การทำเช่นนี้อาจทำให้ผู้สัมภาษณ์ประทับใจ ตัวอย่างเช่นหากตัวเลขเหล่านี้หมายถึงอายุของผู้คนในประเทศ (เช่นจีน) แสดงว่าเป็นปัญหาที่ง่ายกว่ามาก ด้วยสมมติฐานที่สมเหตุสมผลว่าไม่มีใครมีชีวิตอยู่ที่มีอายุมากกว่า 200 คุณสามารถใช้อาร์เรย์ขนาด 200 (อาจ 201) เพื่อนับจำนวนคนที่มีอายุเท่ากันในการทำซ้ำเพียงครั้งเดียว นี่ดัชนีหมายถึงอายุ หลังจากนี้มันเป็นชิ้นส่วนของเค้กเพื่อค้นหา 100 จำนวนมากที่สุด โดยวิธีการนี้อัลโกเรียกว่าการเรียงลำดับการนับการนับเรียงลำดับ

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


26
จุดที่ดีมาก ไม่มีใครถามหรือบอกอะไรเกี่ยวกับการกระจายตัวของตัวเลขเหล่านั้น - มันสามารถสร้างความแตกต่างในวิธีการเข้าถึงปัญหา
NealB

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

2
ฉันเห็นด้วยการถามคำถามกลับไปยังผู้สัมภาษณ์สร้างความแตกต่างอย่างมาก ในความเป็นจริงคำถามเช่นคุณถูก จำกัด ด้วยพลังการคำนวณหรือไม่สามารถช่วยให้คุณแก้ปัญหาขนานกันโดยใช้หลายโหนดการคำนวณ
สุมิตรนิกาม

1
@R_G ไม่จำเป็นต้องผ่านรายการทั้งหมด พอที่จะสุ่มตัวอย่างเพียงเล็กน้อย (เช่นหนึ่งล้าน) ของสมาชิกสุ่มของรายการเพื่อรับสถิติที่เป็นประโยชน์
Itamar

สำหรับผู้ที่จะไม่ได้คิดเกี่ยวกับการแก้ปัญหาที่ผมอยากแนะนำให้ไปอ่านเกี่ยวกับการนับเรียงen.wikipedia.org/wiki/Counting_sort นั่นเป็นคำถามสัมภาษณ์ทั่วไปที่ค่อนข้างสวย: คุณสามารถเรียงลำดับอาร์เรย์ได้ดีกว่า O (nlogn) หรือไม่ คำถามนี้เป็นเพียงการขยาย
Maxime Chéramy

69

คุณสามารถทำซ้ำตัวเลขที่ใช้ O (n)

เมื่อใดก็ตามที่คุณพบค่าที่มากกว่าค่าต่ำสุดในปัจจุบันให้เพิ่มค่าใหม่ในคิวแบบวงกลมที่มีขนาด 100

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


3
มันใช้งานไม่ได้ เช่นค้นหา 2 อันดับแรกของ {1, 100, 2, 99} จะให้ {100,1} เป็นอันดับสูงสุด 2
Skizz

7
คุณไม่สามารถหยุดรอคิวที่เรียงไว้ได้ (ถ้าคุณไม่ต้องการค้นหาคิวรูทุกครั้งสำหรับองค์ประกอบที่เล็กที่สุดถัดไป)
MrSmith42

3
@ MrSmith42 การเรียงลำดับบางส่วนในกองมีเพียงพอ ดูคำตอบของ Ron Teller
Christopher Creutzig

1
ใช่ฉันคิดอย่างเงียบ ๆ ว่าการแตกคิวนาทีจะดำเนินการเป็นกอง
Regenschein

แทนที่จะใช้คิวแบบวนรอบใช้กองขนาดต่ำสุด 100 นี่จะมีจำนวนขั้นต่ำเป็นร้อยอยู่ด้านบน สิ่งนี้จะใช้เวลาเพียง O (บันทึก n) สำหรับการแทรกเมื่อเทียบกับ o (n) ในกรณีของคิว
techExplorer

33

ฉันรู้ว่าสิ่งนี้ถูกติดแท็กด้วย 'อัลกอริทึม' แต่จะโยนตัวเลือกอื่น ๆ เนื่องจากอาจมีการติดแท็ก 'สัมภาษณ์'

แหล่งที่มาของตัวเลข 1 พันล้านคืออะไร หากเป็นฐานข้อมูล 'เลือกค่าจากลำดับของตารางตามค่าจากมากไปน้อย 100' จะทำงานได้ค่อนข้างดี - อาจมีความแตกต่างของภาษา

นี่เป็นครั้งเดียวหรือสิ่งที่จะเกิดขึ้นซ้ำแล้วซ้ำอีกหรือไม่? หากทำซ้ำบ่อยแค่ไหน? หากเป็นข้อมูลแบบครั้งเดียวและข้อมูลอยู่ในไฟล์ให้ระบุ 'cat srcfile | จัดเรียง (ตัวเลือกตามต้องการ) | head -100 'จะทำให้คุณทำงานได้อย่างรวดเร็วมีประสิทธิผลซึ่งคุณจะได้รับรายได้ขณะที่คอมพิวเตอร์จัดการกับงานที่น่าเบื่อนี้

หากมีการทำซ้ำคุณควรแนะนำให้เลือกวิธีการที่เหมาะสมเพื่อรับคำตอบเบื้องต้นและจัดเก็บ / แคชผลลัพธ์เพื่อให้คุณสามารถรายงาน 100 อันดับแรกได้อย่างต่อเนื่อง

ในที่สุดก็มีการพิจารณานี้ คุณกำลังมองหางานระดับเริ่มต้นและสัมภาษณ์ผู้จัดการ geeky หรือเพื่อนร่วมงานในอนาคตหรือไม่? ถ้าเป็นเช่นนั้นคุณสามารถโยนแนวทางในการอธิบายข้อดีและข้อเสียทางเทคนิคที่เกี่ยวข้องทั้งหมด หากคุณกำลังมองหางานที่มีการจัดการมากขึ้นให้เข้าหาผู้จัดการที่เกี่ยวข้องกับค่าใช้จ่ายในการพัฒนาและการบำรุงรักษาและพูดว่า "ขอบคุณมาก ๆ " และออกไปหากนั่นคือผู้สัมภาษณ์ต้องการให้ความสำคัญกับ CS trivia . เขาและคุณจะไม่น่าจะมีศักยภาพก้าวหน้าที่นั่น

ขอให้โชคดีในการสัมภาษณ์ครั้งต่อไป


2
คำตอบที่ยอดเยี่ยม ทุกคนมีสมาธิในด้านเทคนิคของคำถามในขณะที่การตอบสนองนี้จัดการกับส่วนธุรกิจทางสังคมของมัน
vbocan

2
ฉันไม่เคยจินตนาการว่าคุณจะพูดขอบคุณและออกจากการสัมภาษณ์และไม่รอให้เสร็จ ขอบคุณที่เปิดใจ
UrsulRosu

1
ทำไมเราไม่สามารถสร้างองค์ประกอบหลายพันล้านรายการและแยกองค์ประกอบที่ใหญ่ที่สุด 100 รายการ วิธีนี้มีค่า = O (พันล้าน) + 100 * O (บันทึก (พันล้าน)) ??
Mohit Shah

17

ปฏิกิริยาของฉันในทันทีสำหรับสิ่งนี้คือการใช้ฮีป แต่มีวิธีใช้ QuickSelect โดยไม่เก็บค่าอินพุตทั้งหมดไว้ในมือในเวลาใดก็ได้

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

ในตอนท้ายคุณจะมีค่า 100 อันดับแรก สำหรับค่า N คุณได้รัน QuickSelect โดยประมาณ N / 100 ครั้ง Quickselect แต่ละอันมีค่าใช้จ่ายประมาณ 200 เท่าของค่าคงที่ดังนั้นค่าใช้จ่ายทั้งหมดคือ 2N คูณค่าคงที่ สิ่งนี้มีลักษณะเป็นเส้นตรงในขนาดของอินพุตสำหรับฉันโดยไม่คำนึงถึงขนาดของพารามิเตอร์ที่ฉันอยากจะเป็น 100 ในคำอธิบายนี้


10
คุณสามารถเพิ่มการปรับให้เหมาะสมขนาดเล็ก แต่มีความสำคัญ: หลังจากรัน QuickSelect เพื่อแบ่งพาร์ติชันอาร์เรย์ขนาด 200 จะทราบองค์ประกอบขั้นต่ำ 100 อันดับแรก จากนั้นเมื่อวนซ้ำชุดข้อมูลทั้งหมดให้เติมเฉพาะค่าที่ต่ำกว่า 100 หากค่าปัจจุบันมากกว่าค่าต่ำสุดในปัจจุบัน การติดตั้งอัลกอริทึมแบบง่าย ๆ ใน C ++ นั้นเทียบเท่ากับ libstdc ++ ที่partial_sortรันโดยตรงบนชุดข้อมูล 200 ล้าน 32- บิตint(สร้างผ่าน MT19937 ซึ่งมีการกระจายแบบสม่ำเสมอ)
dyp

1
ความคิดที่ดี - ไม่ส่งผลกระทบต่อการวิเคราะห์กรณีที่เลวร้ายที่สุด แต่ก็ดูคุ้มค่าที่จะทำ
mcdowella

@mcdowella มันคุ้มค่าลองและฉันจะทำมันขอบคุณ!
userx

8
นี่คือสิ่งที่Guava Ordering.greatestOf(Iterable, int)ทำ มันเป็นเส้นตรงเวลาและรอบเดียวและเป็นอัลกอริธึมที่น่ารักสุด ๆ FWIW เรายังมีเกณฑ์มาตรฐานจริง ๆ : ปัจจัยคงที่ของมันคือผมช้ากว่าคิวลำดับความสำคัญแบบดั้งเดิมในกรณีเฉลี่ย แต่การดำเนินการนี้มีความต้านทานต่ออินพุต "กรณีที่เลวร้ายที่สุด" มากขึ้น
Louis Wasserman

15

คุณสามารถใช้อัลกอริธึมเลือกด่วนเพื่อค้นหาหมายเลขที่ดัชนี (ตามคำสั่ง) [billion-101] จากนั้นวนซ้ำตัวเลขและค้นหาตัวเลขที่ใหญ่กว่าจากหมายเลขนั้น

array={...the billion numbers...} 
result[100];

pivot=QuickSelect(array,billion-101);//O(N)

for(i=0;i<billion;i++)//O(N)
   if(array[i]>=pivot)
      result.add(array[i]);

เวลาอัลกอริทึมนี้คือ: 2 XO (N) = O (N) (ประสิทธิภาพของเคสโดยเฉลี่ย)

ตัวเลือกที่สองเช่นThomas Jungblutแนะนำคือ:

ใช้กองอาคาร MAX กองจะใช้เวลา O (N) แล้วบน 100 ตัวเลขสูงสุดจะอยู่ในด้านบนของกองทั้งหมดที่คุณต้องการก็คือการให้พวกเขาออกจากกอง (100 XO (เข้าสู่ระบบ (N))

เวลาอัลกอริทึมนี้คือ: O (N) + 100 XO (Log (N)) = O (N)


8
คุณทำงานผ่านรายการทั้งหมดสามครั้ง 1 ชีวภาพ จำนวนเต็มมีขนาดประมาณ 4gb คุณจะทำอย่างไรถ้าคุณไม่พอดีกับหน่วยความจำ quickselect เป็นตัวเลือกที่แย่ที่สุดในกรณีนี้ การวนซ้ำหนึ่งครั้งและเก็บรักษารายการ 100 อันดับแรกไว้เป็น IMHO โซลูชั่นที่มีประสิทธิภาพดีที่สุดใน O (n) (โปรดทราบว่าคุณสามารถตัด O (บันทึก n) ของส่วนแทรกได้มากเนื่องจาก n ในกองคือ 100 = ค่าคงที่ = เล็กมาก )
Thomas Jungblut

3
ถึงแม้ว่ามันจะยังคงO(N)ทำ QuickSelects สองรายการและการสแกนเชิงเส้นอื่นเป็นวิธีที่เหนือศีรษะมากกว่าที่ต้องการ
เควิน

นี่คือรหัส PSEUDO โซลูชั่นทั้งหมดที่นี่จะใช้เวลามากขึ้น (O (NLOG (N) หรือ 100 * O (N))
ลูกเรือชายคนหนึ่ง

1
100*O(N)(หากเป็นไวยากรณ์ที่ถูกต้อง) = O(100*N)= O(N)(ยอมรับ 100 อาจเป็นตัวแปรหากเป็นเช่นนั้นสิ่งนี้จะไม่เป็นจริงอย่างเคร่งครัด) โอ้และQuickselect นั้นมีประสิทธิภาพเป็นตัวพิมพ์ใหญ่ที่สุดของ O (N ^ 2) (ouch) และถ้ามันไม่พอดีกับหน่วยความจำคุณจะโหลดข้อมูลจากดิสก์สองครั้งซึ่งเลวร้ายยิ่งกว่าหนึ่งครั้ง (นี่คือคอขวด)
Bernhard Barker

มีปัญหาที่คาดว่าจะใช้เวลาและไม่ใช่กรณีที่เลวร้ายที่สุด แต่โดยใช้กลยุทธ์การเลือกเดือยที่เหมาะสม (เช่นเลือก 21 องค์ประกอบโดยการสุ่มและเลือกค่ามัธยฐานของ 21 เหล่านั้นเป็นเดือย) จากนั้นจำนวนการเปรียบเทียบสามารถ รับประกันด้วยความน่าจะเป็นสูงที่สุดที่จะมากที่สุด (2 + c) n สำหรับค่าคงตัวเล็ก ๆ
ลูกเรือ One Man

10

แม้ว่าโซลูชัน Quickselect อื่น ๆ จะถูกลดลง แต่ความจริงก็ยังคงอยู่ที่ Quickselect จะค้นหาโซลูชันได้เร็วกว่าการใช้คิวขนาด 100 Quickselect มีเวลาทำงานที่คาดไว้ที่ 2n + o (n) ในแง่ของการเปรียบเทียบ การใช้งานที่ง่ายมากก็คือ

array = input array of length n
r = Quickselect(array,n-100)
result = array of length 100
for(i = 1 to n)
  if(array[i]>r)
     add array[i] to result

นี่จะใช้การเปรียบเทียบ 3n + o (n) โดยเฉลี่ย ยิ่งไปกว่านั้นมันสามารถทำให้มีประสิทธิภาพมากขึ้นโดยใช้ความจริงที่ว่าการเลือกอย่างรวดเร็วจะปล่อยให้ 100 รายการที่ใหญ่ที่สุดในอาร์เรย์ใน 100 ตำแหน่งที่ถูกต้องที่สุด ดังนั้นในความเป็นจริงเวลาทำงานสามารถปรับปรุงเป็น 2n + o (n)

มีปัญหาที่คาดว่าจะใช้เวลาและไม่ใช่กรณีที่เลวร้ายที่สุด แต่โดยใช้กลยุทธ์การเลือกเดือยที่เหมาะสม (เช่นเลือก 21 องค์ประกอบโดยการสุ่มและเลือกค่ามัธยฐานของ 21 เหล่านั้นเป็นเดือย) จากนั้นจำนวนการเปรียบเทียบสามารถ รับประกันด้วยความน่าจะเป็นสูงที่สุดที่จะมากที่สุด (2 + c) n สำหรับค่าคงตัวเล็ก ๆ

ในความเป็นจริงโดยใช้กลยุทธ์การสุ่มตัวอย่างที่ดีที่สุด (ตัวอย่างองค์ประกอบ sqrt (n) โดยการสุ่มและเลือกเปอร์เซ็นไทล์ 99th) เวลาทำงานสามารถลดลงไปที่ (1 + c) n + o (n) สำหรับขนาดเล็กโดยพลการ (สมมติว่า K จำนวนองค์ประกอบที่จะเลือกคือ o (n))

ในทางกลับกันการใช้คิวขนาด 100 จะต้องมีการเปรียบเทียบ O (บันทึก (100) n) และฐานบันทึก 2 จาก 100 จะเท่ากับ 6.6

ถ้าเราคิดถึงปัญหานี้ในแง่นามธรรมที่มากขึ้นในการเลือกองค์ประกอบ K ที่ใหญ่ที่สุดจากอาร์เรย์ที่มีขนาด N โดยที่ K = o (N) แต่ K และ N ทั้งสองไปที่อินฟินิตี้ดังนั้นเวลาทำงานของเวอร์ชัน Quickselect จะเป็น O (N) และรุ่นคิวจะเป็น O (N log K) ดังนั้นในกรณีนี้การเลือกอย่างรวดเร็วก็ยังดีกว่า asymptotically

ในความคิดเห็นมีการกล่าวถึงว่าการแก้ปัญหาคิวจะทำงานในเวลาที่คาดไว้ N + K log N บนอินพุตแบบสุ่ม แน่นอนว่าสมมติฐานสุ่มอินพุตไม่ถูกต้องเว้นแต่คำถามจะระบุอย่างชัดเจน วิธีแก้ปัญหาคิวสามารถทำการสำรวจอาร์เรย์ในลำดับสุ่ม แต่สิ่งนี้จะต้องเสียค่าใช้จ่ายเพิ่มเติมของการโทรไปยังเครื่องกำเนิดตัวเลขสุ่มเช่นเดียวกับการอนุญาตอาร์เรย์อินพุตทั้งหมดหรือมิฉะนั้นการจัดสรรอาร์เรย์ความยาวใหม่ที่มี N ดัชนีสุ่ม

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


4
ย่อหน้าสุดท้ายของคุณคือประเด็นสำคัญ: ด้วยตัวเลขพันล้านเป็นไปไม่ได้ที่จะเก็บข้อมูลทั้งหมดในหน่วยความจำหรือเพื่อสลับองค์ประกอบ (อย่างน้อยนั่นก็เป็นวิธีที่ฉันจะตีความปัญหาเนื่องจากมันเป็นคำถามสัมภาษณ์)
Ted Hopp

14
ในคำถามอัลกอริทึมใด ๆ หากการอ่านข้อมูลเป็นปัญหาต้องมีการกล่าวถึงในคำถาม คำถามระบุว่า "ให้อาเรย์" ไม่ใช่ "ให้อาเรย์บนดิสก์ที่ไม่เหมาะสมในหน่วยความจำและไม่สามารถจัดการได้ตามแบบจำลอง von neuman ซึ่งเป็นมาตรฐานในการวิเคราะห์อัลกอริธึม" วันนี้คุณจะได้แล็ปท็อปที่มี ram 8gigs ฉันไม่แน่ใจว่าแนวคิดในการเก็บหน่วยความจำหนึ่งพันล้านหมายเลขนั้นเป็นไปไม่ได้ ตอนนี้ฉันมีหน่วยความจำหลายพันล้านหมายเลขบนเวิร์กสเตชันของฉัน
mrip

FYI รันไทม์กรณีที่แย่ที่สุดของการเลือกอย่างรวดเร็วคือ O (n ^ 2) (ดูen.wikipedia.org/wiki/Quickselect ) และยังแก้ไขลำดับของอิลิเมนต์ในอาร์เรย์อินพุต เป็นไปได้ที่จะมีวิธีแก้ปัญหากรณี O (n) ที่แย่ที่สุดโดยมีค่าคงที่ขนาดใหญ่มาก ( en.wikipedia.org/wiki/Median_of_medians )
pts

กรณีที่เลวร้ายที่สุดของการเลือกอย่างรวดเร็วนั้นไม่น่าจะเกิดขึ้นแบบทวีคูณซึ่งหมายความว่าสำหรับวัตถุประสงค์ในทางปฏิบัติสิ่งนี้ไม่เกี่ยวข้อง มันง่ายที่จะแก้ไขการเลือกอย่างรวดเร็วเพื่อให้มีความน่าจะเป็นสูงจำนวนการเปรียบเทียบคือ (2 + c) n + o (n) สำหรับขนาดเล็กโดยพล
mrip

"ความจริงยังคงอยู่ที่การเลือกอย่างรวดเร็วจะค้นหาวิธีแก้ปัญหาได้เร็วกว่าการใช้คิวขนาด 100" - Nope โซลูชันฮีปใช้เวลาประมาณ N + Klog (N) เปรียบเทียบกับค่าเฉลี่ย 2N สำหรับการเลือกอย่างรวดเร็วและ 2.95 สำหรับ Median of Medians เห็นได้ชัดว่าเร็วขึ้นสำหรับเค
นีลจี

5

รับ 100 หมายเลขแรกของพันล้านและเรียงลำดับ ทีนี้ก็วนซ้ำไปเรื่อย ๆ จนถึงพันล้านถ้าจำนวนที่มาสูงกว่า 100 ที่เล็กที่สุดให้เรียงลำดับ สิ่งที่คุณท้ายที่สุดคือสิ่งที่ใกล้กับ O (n) มากกว่าขนาดของเซต


3
อุ๊ปส์ไม่เห็นคำตอบที่ละเอียดกว่าของฉัน
ซามูเอลเทอร์สตัน

รับ 500 หรือมากกว่านั้นและหยุดเรียงลำดับ (และโยนต่ำ 400) เมื่อรายการเต็ม (และมันจะไปโดยไม่บอกว่าคุณเพียงเพิ่มในรายการถ้าหมายเลขใหม่คือ> ต่ำที่สุดใน 100 เลือก)
Hot Licks

4

สองตัวเลือก:

(1) ฮีป (ลำดับความสำคัญของคิว)

รักษา min-heap ที่มีขนาด 100 สำรวจอาร์เรย์ เมื่อองค์ประกอบมีขนาดเล็กกว่าองค์ประกอบแรกในกองให้แทนที่

InSERT ELEMENT INTO HEAP: O(log100)
compare the first element: O(1)
There are n elements in the array, so the total would be O(nlog100), which is O(n)

(2) โมเดลลดแผนที่

นี่คล้ายกับตัวอย่างการนับจำนวนคำใน hadoop งานแผนที่: นับความถี่หรือเวลาขององค์ประกอบทุกอย่างที่ปรากฏ ลด: รับองค์ประกอบ K อันดับสูงสุด

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


+1 สำหรับ MapReduce ฉันไม่อยากเชื่อเลยว่าคุณเป็นคนเดียวที่พูดถึง Hadoop สำหรับตัวเลขพันล้าน จะเป็นอย่างไรถ้าผู้สัมภาษณ์ขอหมายเลข 1 พันล้านชุด? คุณสมควรได้รับคะแนนมากขึ้นในความคิดของฉัน
Silviu Burcea

@Silviu Burcea ขอบคุณมาก ฉันให้ความสำคัญกับ MapReduce ด้วย :)
Chris Su

ถึงแม้ว่าขนาดของ 100 จะเป็นค่าคงที่ในตัวอย่างนี้คุณควรจะพูดคุยกับตัวแปรที่แยกต่างหาก k เนื่องจาก 100 มีค่าคงที่เท่ากับ 1 พันล้านแล้วเหตุใดคุณจึงกำหนดขนาดของชุดตัวเลขขนาดใหญ่ให้มีขนาดผันแปรเป็น n และไม่ใช่สำหรับชุดตัวเลขที่เล็กกว่า ความซับซ้อนของคุณควรเป็น O (nlogk) ซึ่งไม่ใช่ O (n)
Tom Heard

1
แต่ประเด็นของฉันคือถ้าคุณเพียงแค่ตอบคำถาม 1 พันล้านยังได้รับการแก้ไขในคำถามดังนั้นทำไมพูดคุยกันถึง 1 พันล้านถึง n ไม่ใช่ 100 ถึง k ตามตรรกะของคุณความซับซ้อนควรเป็น O (1) เนื่องจากทั้ง 1 พันล้านและ 100 ได้รับการแก้ไขในคำถามนี้
Tom Heard

1
@ TomHeard เอาล่ะ O (nlogk) มีเพียงปัจจัยเดียวเท่านั้นที่จะส่งผลต่อผลลัพธ์ ซึ่งหมายความว่าหาก n เพิ่มมากขึ้นเรื่อย ๆ "ระดับผลลัพธ์" จะเพิ่มขึ้นเป็นเส้นตรง หรือเราสามารถพูดได้ว่าแม้จะได้รับจำนวนล้านล้านฉันยังสามารถได้รับ 100 จำนวนมากที่สุด อย่างไรก็ตามคุณไม่สามารถพูดได้: เมื่อเพิ่ม n, k จะเพิ่มขึ้นเพื่อที่ k จะส่งผลต่อผลลัพธ์ นั่นเป็นเหตุผลที่ฉันใช้ O (nlogk) แต่ไม่ใช่ O (nlogn)
Chris Su

4

วิธีแก้ปัญหาที่ง่ายมากคือวนซ้ำ 100 ครั้ง ซึ่งเป็นO(n)ซึ่งเป็น

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


1
ข้อเสียสองประการ - (1) คุณกำลังทำลายอินพุตในกระบวนการ - ควรหลีกเลี่ยงอย่างยิ่ง (2) คุณกำลังผ่านอาร์เรย์หลายครั้ง - ถ้าอาร์เรย์ถูกเก็บไว้ในดิสก์และไม่สามารถใส่หน่วยความจำได้สิ่งนี้อาจช้ากว่าคำตอบที่ยอมรับได้เกือบ 100 เท่า (ใช่พวกเขาทั้งคู่ O (n) แต่ยังคง)
แบร์นฮาร์ดบาร์เกอร์

Good call @Dukeling ฉันได้เพิ่มถ้อยคำเพิ่มเติมเกี่ยวกับวิธีการหลีกเลี่ยงการเปลี่ยนแปลงอินพุตต้นฉบับโดยการติดตามดัชนีคำตอบก่อนหน้า ซึ่งยังคงเป็นรหัสที่ค่อนข้างง่าย
James Oravec

ตัวอย่างที่ยอดเยี่ยมของโซลูชัน O (n) ที่ช้ากว่า O (n log n) มาก log2 (1 พันล้าน) เพียง 30 ...
gnasher729

@ gnasher729 ค่าคงที่ใหญ่ใน O (n log n) เท่าใด
miracle173

1

ได้รับแรงบันดาลใจจากคำตอบของ @ron teller นี่เป็นโปรแกรม B barebones ที่จะทำสิ่งที่คุณต้องการ

#include <stdlib.h>
#include <stdio.h>

#define TOTAL_NUMBERS 1000000000
#define N_TOP_NUMBERS 100

int 
compare_function(const void *first, const void *second)
{
    int a = *((int *) first);
    int b = *((int *) second);
    if (a > b){
        return 1;
    }
    if (a < b){
        return -1;
    }
    return 0;
}

int 
main(int argc, char ** argv)
{
    if(argc != 2){
        printf("please supply a path to a binary file containing 1000000000"
               "integers of this machine's wordlength and endianness\n");
        exit(1);
    }
    FILE * f = fopen(argv[1], "r");
    if(!f){
        exit(1);
    }
    int top100[N_TOP_NUMBERS] = {0};
    int sorts = 0;
    for (int i = 0; i < TOTAL_NUMBERS; i++){
        int number;
        int ok;
        ok = fread(&number, sizeof(int), 1, f);
        if(!ok){
            printf("not enough numbers!\n");
            break;
        }
        if(number > top100[0]){
            sorts++;
            top100[0] = number;
            qsort(top100, N_TOP_NUMBERS, sizeof(int), compare_function);
        }

    }
    printf("%d sorts made\n"
    "the top 100 integers in %s are:\n",
    sorts, argv[1] );
    for (int i = 0; i < N_TOP_NUMBERS; i++){
        printf("%d\n", top100[i]);
    }
    fclose(f);
    exit(0);
}

บนเครื่องของฉัน (คอร์ i3 ที่มี SSD เร็ว) ใช้เวลา 25 วินาทีและ 1724 เรียงลำดับ ฉันสร้างไฟล์ไบนารีด้วยdd if=/dev/urandom/ count=1000000000 bs=1สำหรับการทำงานนี้

เห็นได้ชัดว่ามีปัญหาเรื่องประสิทธิภาพในการอ่านเพียง 4 ไบต์ต่อครั้ง - จากดิสก์ แต่นี่เป็นเพียงตัวอย่างเท่านั้น ในด้านบวกจำเป็นต้องใช้หน่วยความจำน้อยมาก


1

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

ฉันต้องการประเมินจำนวนของการแทรกในบัฟเฟอร์อาร์เรย์องค์ประกอบขนาดเล็ก 100 เมื่อสแกนอาร์เรย์องค์ประกอบ 10 ^ 9 โปรแกรมจะสแกนองค์ประกอบ 1,000 รายการแรกของอาร์เรย์ขนาดใหญ่นี้และต้องแทรกองค์ประกอบ 1,000 รายการในบัฟเฟอร์ บัฟเฟอร์ประกอบด้วย 100 องค์ประกอบขององค์ประกอบ 1000 ที่สแกนนั่นคือ 0.1 ขององค์ประกอบที่สแกน ดังนั้นเราจึงสรุปว่าความน่าจะเป็นที่ค่าจากอาร์เรย์ขนาดใหญ่นั้นใหญ่กว่าค่าต่ำสุดของบัฟเฟอร์ปัจจุบันคือประมาณ 0.1 องค์ประกอบดังกล่าวจะต้องถูกแทรกเข้าไปในบัฟเฟอร์ ตอนนี้โปรแกรมสแกนองค์ประกอบ 10 ^ 4 ถัดไปจากอาร์เรย์ขนาดใหญ่ เนื่องจากค่าต่ำสุดของบัฟเฟอร์จะเพิ่มขึ้นทุกครั้งที่มีการใส่องค์ประกอบใหม่ เราคาดว่าอัตราส่วนขององค์ประกอบที่มีขนาดใหญ่กว่าค่าต่ำสุดในปัจจุบันของเราคือประมาณ 0.1 ดังนั้นจึงมีการแทรกองค์ประกอบ 0.1 * 10 ^ 4 = 1,000 ที่จริงแล้วจำนวนองค์ประกอบที่คาดว่าจะถูกแทรกเข้าไปในบัฟเฟอร์จะมีขนาดเล็กลง หลังจากการสแกนองค์ประกอบ 10 ^ 4 ของเศษส่วนของตัวเลขในบัฟเฟอร์จะอยู่ที่ประมาณ 0.01 ขององค์ประกอบที่สแกนจนถึงตอนนี้ ดังนั้นเมื่อทำการสแกนหมายเลข 10 ^ 5 ถัดไปเราจะสันนิษฐานว่าจะใส่เข้าไปในบัฟเฟอร์ไม่เกิน 0.01 * 10 ^ 5 = 1,000 ดำเนินการโต้แย้งนี้เราได้ใส่ค่าประมาณ 7000 หลังจากสแกน 1,000 + 10 ^ 4 + 10 ^ 5 + ... + 10 ^ 9 ~ 10 ^ 9 องค์ประกอบของอาร์เรย์ขนาดใหญ่ ดังนั้นเมื่อสแกนอาเรย์ด้วยองค์ประกอบแบบสุ่มขนาด 10 ^ 9 เราคาดว่าจะมีการแทรกในบัฟเฟอร์ไม่เกิน 10 ^ 4 (= 7000 มม.) หลังจากการแทรกแต่ละครั้งลงในบัฟเฟอร์จะต้องพบค่าต่ำสุดใหม่ หากบัฟเฟอร์เป็นอาร์เรย์แบบง่ายเราต้องเปรียบเทียบ 100 ค่าเพื่อค้นหาค่าต่ำสุดใหม่ หากบัฟเฟอร์เป็นโครงสร้างข้อมูลอื่น (เช่นฮีป) เราต้องการการเปรียบเทียบอย่างน้อย 1 รายการเพื่อค้นหาค่าต่ำสุด ในการเปรียบเทียบองค์ประกอบของอาร์เรย์ขนาดใหญ่เราต้องทำการเปรียบเทียบ 10 ^ 9 ดังนั้นทั้งหมดเราต้องการการเปรียบเทียบประมาณ 10 ^ 9 + 100 * 10 ^ 4 = 1.001 * 10 ^ 9 เมื่อใช้อาร์เรย์เป็นบัฟเฟอร์และเปรียบเทียบอย่างน้อย 1.000 * 10 ^ 9 เมื่อใช้โครงสร้างข้อมูลชนิดอื่น (เช่นฮีป) . ดังนั้นการใช้ฮีปจะทำให้ได้รับ 0.1% เท่านั้นหากประสิทธิภาพถูกกำหนดโดยจำนวนการเปรียบเทียบ แต่ความแตกต่างในเวลาดำเนินการระหว่างการแทรกองค์ประกอบใน 100 องค์ประกอบคืออะไรและแทนที่องค์ประกอบในอาร์เรย์ 100 องค์ประกอบและการค้นหาขั้นต่ำใหม่ 000 * 10 ^ 9 การเปรียบเทียบเมื่อใช้โครงสร้างข้อมูลชนิดอื่น (เช่นฮีป) ดังนั้นการใช้ฮีปจะทำให้ได้รับ 0.1% เท่านั้นหากประสิทธิภาพถูกกำหนดโดยจำนวนการเปรียบเทียบ แต่ความแตกต่างในเวลาดำเนินการระหว่างการแทรกองค์ประกอบใน 100 องค์ประกอบคืออะไรและแทนที่องค์ประกอบในอาร์เรย์ 100 องค์ประกอบและการค้นหาขั้นต่ำใหม่ 000 * 10 ^ 9 การเปรียบเทียบเมื่อใช้โครงสร้างข้อมูลชนิดอื่น (เช่นฮีป) ดังนั้นการใช้ฮีปจะทำให้ได้รับ 0.1% เท่านั้นหากประสิทธิภาพถูกกำหนดโดยจำนวนการเปรียบเทียบ แต่อะไรคือความแตกต่างในเวลาดำเนินการระหว่างการแทรกอิลิเมนต์ใน 100 อิลิเมนต์ฮีปและการแทนที่อิลิเมนต์ในอาร์เรย์อิลิเมนต์ 100 และการค้นหาขั้นต่ำใหม่

  • ในระดับทฤษฎี: จำเป็นต้องมีการเปรียบเทียบจำนวนมากสำหรับการแทรกในกอง ฉันรู้ว่ามันคือ O (log (n)) แต่ตัวประกอบคงที่มีขนาดใหญ่แค่ไหน? ผม

  • ที่ระดับเครื่อง: ผลกระทบของการแคชและการคาดคะเนสาขาในเวลาดำเนินการของการแทรกฮีปและการค้นหาเชิงเส้นในอาร์เรย์คืออะไร

  • ที่ระดับการใช้งาน: มีค่าใช้จ่ายเพิ่มเติมใดบ้างที่ซ่อนอยู่ในโครงสร้างข้อมูลฮีปที่จัดหาโดยไลบรารีหรือคอมไพเลอร์

ฉันคิดว่าคำถามเหล่านี้เป็นคำถามที่ต้องตอบก่อนที่จะลองประเมินความแตกต่างที่แท้จริงระหว่างประสิทธิภาพขององค์ประกอบ 100 ฮีปหรืออาร์เรย์องค์ประกอบ 100 ดังนั้นจึงสมเหตุสมผลที่จะทำการทดสอบและวัดประสิทธิภาพที่แท้จริง


1
นั่นคือสิ่งที่กองทำ
Neil G

@Neil G: อะไร "นั่น"?
miracle173

1
ด้านบนของฮีปคือองค์ประกอบขั้นต่ำในฮีปและองค์ประกอบใหม่จะถูกปฏิเสธด้วยการเปรียบเทียบหนึ่งรายการ
Neil G

1
ฉันเข้าใจสิ่งที่คุณกำลังพูด แต่ถึงแม้ว่าคุณจะเปรียบเทียบด้วยจำนวนที่แน่นอนมากกว่าการเปรียบเทียบแบบไม่แสดงจำนวนอาเรย์ก็ยังช้ากว่ามากเพราะเวลาที่ "แทรกองค์ประกอบใหม่ยกเลิกขั้นต่ำเก่าและหาค่าต่ำสุดใหม่" คือ 100 มากกว่าประมาณ 7
Neil G

1
โอเค แต่การคาดคะเนของคุณนั้นค่อนข้างมาก คุณสามารถคำนวณจำนวนเม็ดมีดที่คาดว่าจะได้โดยตรงคือ k (digamma (n) - digamma (k)) ซึ่งน้อยกว่า klog (n) ไม่ว่าในกรณีใดทั้งฮีปและโซลูชันอาเรย์ใช้การเปรียบเทียบเพียงครั้งเดียวเพื่อทิ้งองค์ประกอบ ความแตกต่างเพียงอย่างเดียวคือจำนวนการเปรียบเทียบสำหรับองค์ประกอบที่แทรกคือ 100 สำหรับวิธีการแก้ปัญหาของคุณเทียบกับสูงสุด 14 สำหรับกอง (แม้ว่ากรณีโดยเฉลี่ยอาจน้อยกว่ามาก)
Neil G

1
 Although in this question we should search for top 100 numbers, I will 
 generalize things and write x. Still, I will treat x as constant value.

อัลกอริทึมองค์ประกอบ x ที่ใหญ่ที่สุดจาก n:

ฉันจะเรียกผลตอบแทนคุ้มค่าLIST มันเป็นชุดขององค์ประกอบ x (ในความคิดของฉันที่ควรจะเชื่อมโยงรายการ)

  • อิลิเมนต์ x แรกถูกนำมาจากพูล "ตามที่มา" และเรียงลำดับในรายการ (จะทำในเวลาคงที่เนื่องจาก x ถูกถือว่าเป็นค่าคงที่ - เวลา O (x log (x)))
  • สำหรับทุกองค์ประกอบที่มาถัดไปเราจะตรวจสอบว่ามันมีขนาดใหญ่กว่าองค์ประกอบที่เล็กที่สุดในรายการและถ้าเราโผล่ออกมามีขนาดเล็กที่สุดและแทรกองค์ประกอบปัจจุบันลงในรายการ เนื่องจากว่ามีการเรียงลำดับรายการทุกองค์ประกอบควรหาตำแหน่งของมันในเวลาลอการิทึม (การค้นหาแบบไบนารี) และเนื่องจากการแทรกรายการนั้นถูกเรียงลำดับจึงไม่เป็นปัญหา ทุกขั้นตอนจะทำในเวลาคงที่ (O (log (x)))

ดังนั้นสถานการณ์กรณีที่เลวร้ายที่สุดคืออะไร?

x log (x) + (nx) (log (x) +1) = nlog (x) + n - x

นั่นคือเวลา O (n) สำหรับกรณีที่แย่ที่สุด +1 คือการตรวจสอบว่าจำนวนมากกว่าจำนวนที่น้อยที่สุดใน LIST หรือไม่ เวลาที่คาดหวังสำหรับกรณีเฉลี่ยจะขึ้นอยู่กับการกระจายทางคณิตศาสตร์ขององค์ประกอบ n เหล่านั้น

การปรับปรุงที่เป็นไปได้

อัลกอริทึมนี้สามารถปรับปรุงเล็กน้อยสำหรับสถานการณ์กรณีที่เลวร้ายที่สุด แต่ IMHO (ฉันไม่สามารถพิสูจน์การอ้างสิทธิ์นี้) ที่จะลดพฤติกรรมโดยเฉลี่ย พฤติกรรมแบบซีมโทติคจะเหมือนกัน

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

x log (x) + (nx) log (x) = nlog (x)

การดำเนินงาน

สำหรับกรณีการใช้งานนี้ฉันไม่เห็นการปรับปรุงเพิ่มเติมใด ๆ แต่คุณต้องถามตัวเองว่าจะทำอย่างไรถ้าฉันต้องทำมากกว่า log (n) เท่าและใช้ x-es ต่างกัน? เห็นได้ชัดว่าเราจะจัดเรียงแถวนั้นใน O (n log (n)) และนำองค์ประกอบ x ของเราเมื่อใดก็ตามที่เราต้องการ


1

คำถามนี้จะตอบด้วยความซับซ้อน N log (100) (แทนที่จะเป็น N log N) ด้วยรหัส C ++ เพียงบรรทัดเดียว

 std::vector<int> myvector = ...; // Define your 1 billion numbers. 
                                 // Assumed integer just for concreteness 
 std::partial_sort (myvector.begin(), myvector.begin()+100, myvector.end());

คำตอบสุดท้ายจะเป็นเวกเตอร์ที่รับประกัน 100 องค์ประกอบแรกจะเป็น 100 ตัวเลขที่ใหญ่ที่สุดของคุณในขณะที่องค์ประกอบที่เหลือไม่ได้เรียงลำดับ

C ++ STL (ไลบรารีมาตรฐาน) ค่อนข้างมีประโยชน์สำหรับปัญหาประเภทนี้

หมายเหตุ: ฉันไม่ได้บอกว่านี่เป็นทางออกที่ดีที่สุด แต่จะช่วยให้การสัมภาษณ์ของคุณดีขึ้น


1

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

หากตัวเลขอยู่ในลำดับแบบสุ่มสิ่งนี้จะได้ผลเพราะเมื่อเราวนตัวเลขสุ่มนับพันล้านครั้งมันจะหายากมากที่หมายเลขต่อไปจะเป็นหนึ่งใน 100 อันดับที่ใหญ่ที่สุด แต่ตัวเลขอาจไม่สุ่ม หากอาร์เรย์ที่ถูกเรียงอยู่แล้วในลำดับแล้วเราจะเสมอแทรกองค์ประกอบคิวลำดับความสำคัญที่

ดังนั้นเราเลือกตัวเลขสุ่ม 100,000 ตัวจากอาร์เรย์ก่อน เพื่อหลีกเลี่ยงการเข้าถึงแบบสุ่มซึ่งอาจจะช้าเราเพิ่มกลุ่ม 400 กลุ่มที่สุ่มจาก 250 หมายเลขติดต่อกัน ด้วยการเลือกแบบสุ่มนั้นเราสามารถมั่นใจได้ว่าจำนวนที่เหลือน้อยมากนั้นอยู่ในร้อยอันดับแรกดังนั้นเวลาในการดำเนินการจะใกล้เคียงกับการวนรอบแบบง่ายมากเมื่อเปรียบเทียบจำนวนพันล้านกับค่าสูงสุด


1

การค้นหา 100 อันดับแรกจากตัวเลขพันล้านนั้นทำได้ดีที่สุดโดยใช้จำนวนต่ำสุดขององค์ประกอบ 100 รายการ

ครั้งแรกที่สำคัญ min-heap เมื่อพบ 100 หมายเลขแรก min-heap จะเก็บหมายเลขที่เล็กที่สุดของ 100 หมายเลขแรกที่รูท (ด้านบน)

ทีนี้เมื่อคุณไปตามตัวเลขที่เหลือจะเปรียบเทียบกับรูทเท่านั้น (น้อยที่สุดจาก 100)

หากหมายเลขใหม่ที่พบมีขนาดใหญ่กว่ารูทของ min-heap ให้แทนที่รูทด้วยหมายเลขนั้นมิฉะนั้นจะละเว้น

ในส่วนของการแทรกหมายเลขใหม่ใน min-heap ตัวเลขที่เล็กที่สุดใน heap จะมาถึงด้านบน (root)

เมื่อเราผ่านตัวเลขทั้งหมดแล้วเราจะมีตัวเลขที่ใหญ่ที่สุด 100 อันดับในหน่วยนาที


0

ฉันได้เขียนวิธีง่ายๆใน Python ในกรณีที่ใครสนใจ มันใช้bisectโมดูลและรายการส่งคืนชั่วคราวซึ่งจะเรียงลำดับ สิ่งนี้คล้ายกับการใช้คิวลำดับความสำคัญ

import bisect

def kLargest(A, k):
    '''returns list of k largest integers in A'''
    ret = []
    for i, a in enumerate(A):
        # For first k elements, simply construct sorted temp list
        # It is treated similarly to a priority queue
        if i < k:
            bisect.insort(ret, a) # properly inserts a into sorted list ret
        # Iterate over rest of array
        # Replace and update return array when more optimal element is found
        else:
            if a > ret[0]:
                del ret[0] # pop min element off queue
                bisect.insort(ret, a) # properly inserts a into sorted list ret
    return ret

การใช้งานกับองค์ประกอบ 100,000,000 และการป้อนข้อมูลกรณีที่เลวร้ายที่สุดซึ่งเป็นรายการที่เรียงลำดับ:

>>> from so import kLargest
>>> kLargest(range(100000000), 100)
[99999900, 99999901, 99999902, 99999903, 99999904, 99999905, 99999906, 99999907,
 99999908, 99999909, 99999910, 99999911, 99999912, 99999913, 99999914, 99999915,
 99999916, 99999917, 99999918, 99999919, 99999920, 99999921, 99999922, 99999923,
 99999924, 99999925, 99999926, 99999927, 99999928, 99999929, 99999930, 99999931,
 99999932, 99999933, 99999934, 99999935, 99999936, 99999937, 99999938, 99999939,
 99999940, 99999941, 99999942, 99999943, 99999944, 99999945, 99999946, 99999947,
 99999948, 99999949, 99999950, 99999951, 99999952, 99999953, 99999954, 99999955,
 99999956, 99999957, 99999958, 99999959, 99999960, 99999961, 99999962, 99999963,
 99999964, 99999965, 99999966, 99999967, 99999968, 99999969, 99999970, 99999971,
 99999972, 99999973, 99999974, 99999975, 99999976, 99999977, 99999978, 99999979,
 99999980, 99999981, 99999982, 99999983, 99999984, 99999985, 99999986, 99999987,
 99999988, 99999989, 99999990, 99999991, 99999992, 99999993, 99999994, 99999995,
 99999996, 99999997, 99999998, 99999999]

ใช้เวลาประมาณ 40 วินาทีในการคำนวณสิ่งนี้สำหรับองค์ประกอบ 100,000,000 ชิ้นดังนั้นฉันจึงกลัวที่จะทำเพื่อ 1 พันล้านชิ้น เพื่อความเป็นธรรมฉันได้ป้อนอินพุตที่แย่ที่สุด (เรียงเป็นอาร์เรย์ที่เรียงลำดับแล้ว)


0

ฉันเห็นการสนทนา O (N) มากมายดังนั้นฉันจึงเสนอสิ่งที่แตกต่างสำหรับการฝึกคิด

มีข้อมูลใดที่ทราบเกี่ยวกับธรรมชาติของตัวเลขเหล่านี้หรือไม่ หากเป็นการสุ่มโดยธรรมชาติแล้วอย่าไปต่อและดูคำตอบอื่น ๆ คุณจะไม่ได้ผลลัพธ์ที่ดีไปกว่านี้

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

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


0
Time ~ O(100 * N)
Space ~ O(100 + N)
  1. สร้างรายการว่างของช่องว่าง 100 ช่อง

  2. สำหรับทุกหมายเลขในรายการอินพุต:

    • หากจำนวนน้อยกว่าหมายเลขแรกให้ข้าม

    • มิฉะนั้นแทนที่ด้วยหมายเลขนี้

    • จากนั้นกดตัวเลขผ่านการสลับที่อยู่ติดกัน จนกว่ามันจะเล็กกว่าอันถัดไป

  3. ส่งคืนรายการ


หมายเหตุ:ถ้าหากlog(input-list.size) + c < 100วิธีที่ดีที่สุดคือการเรียงลำดับรายการอินพุตจากนั้นแยก 100 รายการแรก


0

ความซับซ้อนคือ O (N)

ขั้นแรกสร้างอาร์เรย์ที่มี 100 int เริ่มต้นเป็นองค์ประกอบแรกของอาร์เรย์นี้เป็นองค์ประกอบแรกของค่า N ติดตามค่าดัชนีขององค์ประกอบปัจจุบันด้วยตัวแปรอื่นเรียกว่า CurrentBig

ทำซ้ำแม้ว่าค่า N

if N[i] > M[CurrentBig] {

M[CurrentBig]=N[i]; ( overwrite the current value with the newly found larger number)

CurrentBig++;      ( go to the next position in the M array)

CurrentBig %= 100; ( modulo arithmetic saves you from using lists/hashes etc.)

M[CurrentBig]=N[i];    ( pick up the current value again to use it for the next Iteration of the N array)

} 

เมื่อเสร็จแล้วให้พิมพ์อาร์เรย์ M จาก CurrentBig 100 ครั้ง modulo 100 :-) สำหรับนักเรียน: ตรวจสอบให้แน่ใจว่าบรรทัดสุดท้ายของรหัสไม่ได้ใช้ข้อมูลที่ถูกต้องก่อนที่จะออกจากรหัส


0

อีกขั้นตอนวิธี O (n) -

อัลกอริทึมค้นหา 100 ที่ใหญ่ที่สุดโดยการกำจัด

พิจารณาจำนวนล้านทั้งหมดในการเป็นตัวแทนไบนารีของพวกเขา เริ่มต้นจากบิตที่สำคัญที่สุด การค้นหาว่า MSB เป็น 1 สามารถทำได้โดยการคูณการดำเนินการบูลีนด้วยจำนวนที่เหมาะสม หากมีมากกว่า 100 1 ในล้านเหล่านี้กำจัดตัวเลขอื่น ๆ ด้วยศูนย์ ตอนนี้ของจำนวนที่เหลือดำเนินการกับบิตที่สำคัญที่สุดต่อไป เก็บจำนวนตัวเลขที่เหลืออยู่หลังจากการกำจัดและดำเนินการต่อไปตราบใดที่จำนวนนี้มากกว่า 100

การดำเนินการบูลีนที่สำคัญสามารถทำได้แบบขนานบน GPU


0

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


0

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


1
วิธีนี้เกือบจะเหมือนกันกับคำตอบที่มากที่สุดและอันดับที่สองที่ upvoted สำหรับคำถามนี้
Bernhard Barker

0

การจัดการรายการแยกต่างหากเป็นงานพิเศษและคุณต้องย้ายสิ่งต่าง ๆ รอบรายการทั้งหมดทุกครั้งที่คุณพบสิ่งทดแทนใหม่ เพียงแค่ qsort มันและรับ 100 อันดับแรก


-1 quicksort คือ O (n log n) ซึ่งเป็นสิ่งที่ OP ทำและขอให้ปรับปรุง คุณไม่จำเป็นต้องจัดการรายการแยกต่างหากเพียงรายการ 100 หมายเลข คำแนะนำของคุณมีผลข้างเคียงที่ไม่พึงประสงค์จากการเปลี่ยนรายการดั้งเดิมหรือคัดลอก นั่นคือ 4GiB หรือมากกว่านั้นของหน่วยความจำหายไป

0
  1. ใช้องค์ประกอบที่ n เพื่อรับองค์ประกอบที่ 100 O (n)
  2. ทำซ้ำครั้งที่สอง แต่เพียงครั้งเดียวและส่งออกทุกองค์ประกอบที่มากกว่าองค์ประกอบเฉพาะนี้

โปรดทราบโดยเฉพาะอย่างยิ่ง ขั้นตอนที่สองอาจจะง่ายในการคำนวณแบบขนาน! และจะมีประสิทธิภาพเมื่อคุณต้องการองค์ประกอบที่ใหญ่ที่สุดนับล้าน


0

เป็นคำถามจาก Google หรือ บริษัท ยักษ์ใหญ่ในอุตสาหกรรมบางทีโค้ดต่อไปนี้คือคำตอบที่ถูกต้องจากผู้สัมภาษณ์ของคุณ ต้นทุนเวลาและต้นทุนพื้นที่ขึ้นอยู่กับจำนวนสูงสุดในอินพุตอาร์เรย์สำหรับอินพุตอาร์เรย์ int แบบ 32 บิตต้นทุนพื้นที่สูงสุดคือ 4 * 125M ไบต์ค่าใช้จ่ายเวลาเท่ากับ 5 * พันล้าน

public class TopNumber {
    public static void main(String[] args) {
        final int input[] = {2389,8922,3382,6982,5231,8934
                            ,4322,7922,6892,5224,4829,3829
                            ,6892,6872,4682,6723,8923,3492};
        //One int(4 bytes) hold 32 = 2^5 value,
        //About 4 * 125M Bytes
        //int sort[] = new int[1 << (32 - 5)];
        //Allocate small array for local test
        int sort[] = new int[1000];
        //Set all bit to 0
        for(int index = 0; index < sort.length; index++){
            sort[index] = 0;
        }
        for(int number : input){
            sort[number >>> 5] |= (1 << (number % 32));
        }
        int topNum = 0;
        outer:
        for(int index = sort.length - 1; index >= 0; index--){
            if(0 != sort[index]){
                for(int bit = 31; bit >= 0; bit--){
                    if(0 != (sort[index] & (1 << bit))){
                        System.out.println((index << 5) + bit);
                        topNum++;
                        if(topNum >= 3){
                            break outer;
                        }
                    }
                }
            }
        }
    }
}

0

ฉันทำรหัสของตัวเองไม่แน่ใจว่ามันเป็น "ผู้สัมภาษณ์" หรือเปล่า

private static final int MAX=100;
 PriorityQueue<Integer> queue = new PriorityQueue<>(MAX);
        queue.add(array[0]);
        for (int i=1;i<array.length;i++)
        {

            if(queue.peek()<array[i])
            {
                if(queue.size() >=MAX)
                {
                    queue.poll();
                }
                queue.add(array[i]);

            }

        }

0

การปรับปรุงที่เป็นไปได้

หากไฟล์มีหมายเลข 1 พันล้านการอ่านอาจเป็นจริงยาว ...

เพื่อปรับปรุงการทำงานนี้คุณสามารถ:

  • แบ่งไฟล์ออกเป็นส่วนต่าง ๆ , สร้าง n เธรด, สร้าง n เธรดค้นหาแต่ละหมายเลขที่ใหญ่ที่สุด 100 อันดับในส่วนของไฟล์ (โดยใช้ลำดับความสำคัญคิว) และสุดท้ายได้รับ 100 เธรดจำนวนมากที่สุดของเอาต์พุตทั้งหมด
  • ใช้คลัสเตอร์เพื่อทำงานดังกล่าวด้วยโซลูชันเช่น hadoop ที่นี่คุณสามารถแยกไฟล์ได้มากขึ้นและให้เอาต์พุตเร็วขึ้นสำหรับไฟล์หมายเลข 1 พันล้าน (หรือ 10 ^ 12)

0

เริ่มแรกใช้องค์ประกอบ 1,000 รายการและเพิ่มในจำนวนสูงสุด ตอนนี้ใช้องค์ประกอบสูงสุด 100 รายการแรกแล้วเก็บไว้ที่ใดที่หนึ่ง ตอนนี้เลือกองค์ประกอบอีก 900 รายการจากไฟล์และเพิ่มในฮีปพร้อมกับองค์ประกอบสูงสุด 100 รายการสุดท้าย

ทำขั้นตอนนี้ซ้ำเพื่อรับ 100 อิลิเมนต์จากฮีปและเพิ่ม 900 อิลิเมนต์จากไฟล์

การเลือกครั้งสุดท้ายของ 100 องค์ประกอบจะให้เราสูงสุด 100 องค์ประกอบจากตัวเลขหนึ่งพันล้าน


-1

ปัญหา: ค้นหาองค์ประกอบที่ใหญ่ที่สุดของ n รายการที่ n >>> m

ทางออกที่ง่ายที่สุดที่ทุกคนควรเห็นได้ชัดคือทำขั้นตอนวิธีการเรียงฟอง

จากนั้นพิมพ์องค์ประกอบสุดท้าย n ของอาร์เรย์

สิ่งนี้ไม่ต้องการโครงสร้างข้อมูลภายนอกและใช้อัลกอริทึมที่ทุกคนรู้

เวลาในการทำงานโดยประมาณคือ O (m * n) คำตอบที่ดีที่สุดคือ O (n log (m)) ดังนั้นการแก้ปัญหานี้จึงไม่แพงมากสำหรับ m ขนาดเล็ก

ฉันไม่ได้บอกว่าสิ่งนี้ไม่สามารถปรับปรุงได้ แต่นี่เป็นวิธีที่ง่ายที่สุด


1
ไม่มีโครงสร้างข้อมูลภายนอก? สิ่งที่เกี่ยวกับการเรียงลำดับจำนวนพันล้าน? อาเรย์ของขนาดนี้เป็นค่าใช้จ่ายจำนวนมากทั้งในการเติมและเวลาในการจัดเก็บ เกิดอะไรขึ้นถ้าตัวเลข "ใหญ่" ทั้งหมดอยู่ในแถวท้ายผิดของอาร์เรย์? คุณจะต้องใช้ลำดับ 100 พันล้านแลกเปลี่ยนในการ "วาง" พวกมันเข้าสู่ตำแหน่ง - เหนือศีรษะอันใหญ่อีก ... ในที่สุด M N = 100 พันล้านเทียบกับ M Log2 (N) = 6.64 พันล้านซึ่งเกือบสองคำสั่งของความแตกต่างของขนาด บางทีคิดใหม่อีกครั้ง การสแกนผ่านครั้งเดียวในขณะที่ยังคงรักษาโครงสร้างข้อมูลของตัวเลขที่มีค่ามากที่สุดนั้นกำลังดำเนินไปในแนวทางนี้
NealB
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.