นี่เป็นคำถามสัมภาษณ์ที่ฉันวิ่งข้ามไปสองสามครั้งและฉันไม่แน่ใจว่าจะแก้ปัญหาอย่างไรเพราะตัวเลขสี่ตัวนั้นหายไป ฉันคุ้นเคยกับอัลกอริธึมในการค้นหาตัวเลขหนึ่งหรือสองตัวที่หายไป แต่ฉันไม่เห็นวิธีที่จะทำให้เป็นหนึ่งในสี่
นี่เป็นคำถามสัมภาษณ์ที่ฉันวิ่งข้ามไปสองสามครั้งและฉันไม่แน่ใจว่าจะแก้ปัญหาอย่างไรเพราะตัวเลขสี่ตัวนั้นหายไป ฉันคุ้นเคยกับอัลกอริธึมในการค้นหาตัวเลขหนึ่งหรือสองตัวที่หายไป แต่ฉันไม่เห็นวิธีที่จะทำให้เป็นหนึ่งในสี่
คำตอบ:
ไม่ว่าจะเป็นสำหรับการสัมภาษณ์หรือทำงานจริงอันดับแรกของคุณจะต้องมีวิธีการแก้ปัญหาการทำงานที่ทำให้รู้สึกถึงคุณ ซึ่งมักจะหมายความว่าคุณควรเสนอวิธีแก้ปัญหาแรกที่คุณสามารถคิดได้ว่าง่ายและสะดวกสำหรับคุณที่จะอธิบาย
สำหรับฉันนั่นหมายถึงการเรียงลำดับตัวเลขและสแกนหาช่องว่าง แต่ฉันทำงานกับระบบธุรกิจและเว็บแอพ ฉันไม่ได้เล่นกับบิตและฉันไม่ต้องการให้ทีมของฉัน!
หากคุณสัมภาษณ์งานที่มีระดับต่ำและใกล้ชิดกับโลหะมากขึ้น "การเรียงลำดับ" อาจพบกับดาวว่างเปล่า พวกเขาต้องการให้คุณคิดอย่างสบายใจเกี่ยวกับบิตและอื่น ๆ คำตอบแรกของคุณควรมี "โอ้ฉันจะใช้บิตแมป" (หรืออาร์เรย์บิตหรือชุดบิต)
จากนั้นไม่ว่าจะด้วยวิธีใด - แม้ว่าคุณจะให้วิธีการ "ผิด" หากผู้สัมภาษณ์ (หรือเจ้านาย!) กดมันคุณสามารถแนะนำการปรับปรุงหรือทางเลือกอื่น ๆ โดยมุ่งเน้นไปที่ประเด็นเฉพาะของผู้จัดการ
O(n*log(n))
เรียงลำดับอยู่แล้ว (หรือ O (n) สำหรับการจัดเรียงจำนวนเต็ม!)BitSet
/ BitMap
/ BitArray
)BitArray
เพื่อตั้งค่าสถานะ "หมายเลขที่พบ" และแล้วสแกนหา0
'sBitArray
/BitSet
(เพื่อค้นหาไฟล์0
) นั่นคือO(n)
ฉันคิดว่า!หรืออะไรก็ตาม
แก้ไขข้อกังวลที่คุณมี เพียงแก้ไขปัญหาก่อนโดยใช้วิธีแก้ปัญหาไร้เดียงสาหากจำเป็น อย่าเสียเวลากับความกังวลของทุกคนที่ยังไม่มี
เนื่องจากเป็นไฟล์ฉันถือว่าคุณได้รับอนุญาตให้ผ่านหลายครั้งได้ ขั้นแรกสร้างอาร์เรย์จาก 256 เคาน์เตอร์วนซ้ำไฟล์และสำหรับแต่ละหมายเลขจะเพิ่มตัวนับที่ทำดัชนีเป็นไบต์แรกของตัวเลข เมื่อเสร็จแล้วเคาน์เตอร์ส่วนใหญ่ควรอยู่ที่ 2 ^ 24 แต่เคาน์เตอร์ 1 ถึง 4 ตัวควรมีค่าต่ำกว่า แต่ละดัชนีเหล่านี้แสดงถึงไบต์แรกของหนึ่งในตัวเลขที่ขาดหายไป (ถ้ามีน้อยกว่า 4 นั่นก็เพราะตัวเลขที่ขาดหายไปหลายตัวใช้ร่วมกันเป็นไบต์แรก)
สำหรับแต่ละดัชนีเหล่านี้ให้สร้างอาร์เรย์อีกจำนวน 256 ตัวนับและทำการส่งครั้งที่สองในไฟล์ เวลานี้หากไบต์แรกเป็นหนึ่งในค่าจากก่อนหน้าให้เพิ่มตัวนับในอาร์เรย์โดยยึดตามไบต์ที่สอง เมื่อเสร็จแล้วให้ค้นหาตัวนับที่ต่ำกว่า 2 ^ 16 อีกครั้งและคุณจะมีจำนวนไบต์ที่สองของตัวเลขที่หายไปโดยแต่ละคู่จะตรงกับไบต์แรก
ทำอีกครั้งสำหรับไบต์ที่สาม (โปรดสังเกตว่าคุณต้องการสูงสุด 4 อาร์เรย์ในแต่ละรอบแม้ว่าแต่ละไบต์จะสามารถตามได้สูงสุด 4 ไบต์ที่แตกต่างกัน) และสำหรับไบต์ที่สี่และคุณพบตัวเลขที่หายไปทั้งหมด
ความซับซ้อนของเวลา - ความซับซ้อนของO(n * log n)
อวกาศ - คงที่ !
ที่จริงแล้วฉันถือว่าn=2^32
เป็นพารามิเตอร์ แต่จำนวนตัวเลขที่ขาดหายไปk=4
ก็เป็นพารามิเตอร์เช่นกัน สมมติว่านี้หมายถึงความซับซ้อนของพื้นที่คือk<<n
O(k)
เพียงเพื่อความสนุกสนาน (และเพราะฉันกำลังพยายามที่จะเรียนรู้สนิม) ฉันนำมาใช้มันใน Rust: https://gist.github.com/idanarye/90a925ebb2ea57de18f03f570f70ea1f ฉันเลือกที่จะเป็นตัวแทนข้อความเนื่องจากหนึ่งในนั้นจะทำงานด้วยตัวเลข ~ 2 ^ 32 ...
หากนี่คือ Java คุณสามารถใช้ BitSet ได้ สองคนเพราะพวกเขาไม่สามารถถือตัวเลข 32 บิตได้ทั้งหมด รหัสโครงร่างอาจเป็นรถ:
BitSet bitsetForPositives = new Bitset(2^31); // obviously not 2^31 but you get the idea
BitSet bitsetForNegatives = new Bitset(2^31);
for (int value: valuesTheyPassInSomehow) {
if ((value & 0x80000000) == 0)
bitsetForPositives.set(value );
else
bitsetForNegatives.set(value & ~0x80000000);
}
จากนั้นใช้ BitSet.nextClearBit()
เพื่อค้นหาผู้ที่หายไป
เพิ่มหมายเหตุมากในภายหลัง:
โปรดทราบว่ามีขั้นตอนวิธีการนี้มันค่อนข้างง่ายที่จะเรียกใช้เวลานานเป็นส่วนหนึ่งในแบบคู่ขนาน สมมติว่าไฟล์ต้นฉบับถูกแบ่งออกเป็นสี่ส่วนเท่า ๆ กันโดยประมาณ จัดสรร BitSets 4 คู่ (2GB, ยังจัดการได้)
ฉันคาดหวังว่า I / O จะยังคงเป็นขั้นตอน จำกัด อัตรา แต่ถ้าตัวเลขทั้งหมดอยู่ในหน่วยความจำอย่างน่าอัศจรรย์คุณสามารถเร่งความเร็วได้อย่างแท้จริง
Integer.MIN_VALUE
ถูกต้อง คุณสามารถปกปิด bit sign แทนการปฏิเสธเพื่อแก้ไขได้
bool GetBit(byte[] byteArray, uint index) { var byteIndex = index >> 3; var bitInByte = index & 7; return (byteArray[byteIndex] >> bitInByte) & 1 != 0; }
คำถามนี้สามารถแก้ไขได้โดยใช้อาร์เรย์ของบิต (จริง / เท็จ) นี่ควรเป็นโครงสร้างที่มีประสิทธิภาพที่สุดในการเก็บคำตอบสำหรับตัวเลขทั้งหมดโดยใช้ดัชนีของอาร์เรย์เพื่อเก็บว่าพบจำนวนนั้นหรือไม่
C #
var bArray = new BitArray(Int32.MaxValue);
//Assume the file has 1 number per line
using (StreamReader sr = File.OpenText(fileName))
{
string s = String.Empty;
while ((s = sr.ReadLine()) != null)
{
var n = int32.Parse(s);
bArray[n] = true;
}
}
จากนั้นก็วนซ้ำอาร์เรย์และสำหรับค่าที่ยังคงเป็นเท็จพวกเขาไม่ได้อยู่ในไฟล์
คุณสามารถแบ่งไฟล์ออกเป็นชิ้นเล็ก ๆ แต่ฉันก็สามารถจัดสรรอาร์เรย์ขนาดสูงสุด int32 (2147483647) บนแล็ปท็อป 16.0 GB ที่ใช้ Windows 7 (64 บิต)
แม้ว่าฉันจะไม่ได้รัน 64 บิตก็ตามฉันก็สามารถจัดสรรอาร์เรย์บิตที่เล็กลงได้ ฉันจะประมวลผลไฟล์ล่วงหน้าโดยสร้างไฟล์เล็ก ๆ แต่ละไฟล์ด้วยช่วง [0-64000] [64001-128000] และหมายเลขอื่น ๆ ในนั้นที่จะเหมาะสมกับทรัพยากรด้านสิ่งแวดล้อมที่มีอยู่ ผ่านไฟล์ขนาดใหญ่และเขียนแต่ละหมายเลขไปยังไฟล์ชุดที่สอดคล้องกัน จากนั้นประมวลผลแต่ละไฟล์ที่เล็กกว่า อาจใช้เวลานานขึ้นเล็กน้อยเนื่องจากขั้นตอนการประมวลผลล่วงหน้า แต่จะได้รับการ จำกัด ทรัพยากรหากมีทรัพยากรที่ จำกัด
เนื่องจากนี่เป็นคำถามสัมภาษณ์ฉันจะแสดงให้ผู้สัมภาษณ์เข้าใจถึงข้อ จำกัด จากนั้น "ตัวเลขที่เป็นไปได้ทั้งหมด" หมายความว่าอย่างไร จริง ๆ แล้วมันคือ 0 ... 2 <(32-1) ตามที่ทุกคนเดา? สถาปัตยกรรมแบบ 32 บิตโดยปกติสามารถทำงานกับจำนวนมากกว่า 32 บิตเท่านั้น มันเป็นเพียงเรื่องของการเป็นตัวแทนแน่นอน
ต้องมีการแก้ไขในระบบ 32 บิตหรือว่าเป็นส่วนหนึ่งของการ จำกัด จำนวนหรือไม่ ตัวอย่างเช่นระบบทั่วไป 32 บิตจะไม่สามารถโหลดไฟล์ลงใน RAM ได้ในครั้งเดียว ฉันยังพูดถึงว่าระบบ 32 บิตมักจะไม่สามารถมีไฟล์ที่มีตัวเลขทั้งหมดเนื่องจากข้อ จำกัด ขนาดไฟล์ ดีเว้นแต่มันจะมีการเข้ารหัสที่ฉลาดเช่น "ตัวเลขทั้งหมดยกเว้นที่สี่" ซึ่งในกรณีนี้ปัญหาจะได้รับการแก้ไขเล็กน้อย
แต่ถ้าคุณต้องการที่จะเข้าใจคำถามจริงๆว่า "ให้ไฟล์ที่มีตัวเลขทั้งหมดตั้งแต่ 0 ... 2 ^ (32-1) ยกเว้นบางอันให้ฉันหายไป" (และนี่ก็ใหญ่ถ้า !) จากนั้น มีหลายวิธีในการแก้ปัญหา
ไม่สำคัญ แต่ไม่เป็นไปได้: สำหรับแต่ละหมายเลขที่เป็นไปได้ให้สแกนไฟล์และดูว่ามีอยู่ในนั้นหรือไม่
ด้วย RAM ขนาด 512 MB และการส่งผ่านไฟล์ครั้งเดียว: ทำเครื่องหมายทุกหมายเลข (= ตั้งค่าบิตที่ดัชนีนั้น) อ่านจากไฟล์และหลังจากนั้นส่งผ่าน RAM หนึ่งครั้งและดูสิ่งที่ขาดหายไป
วิธีหนึ่งที่ง่ายต่อการจดจำและง่ายต่อการสื่อสารในการสัมภาษณ์คือการใช้ความจริงที่ว่าถ้าคุณดูตัวเลขทั้งหมดในบิต N บิตแต่ละบิตจะถูกตั้งค่าเป็นครึ่งหนึ่งของค่าเหล่านั้นและไม่ได้ตั้งค่าในอีกครึ่งหนึ่ง .
หากคุณวนซ้ำค่าทั้งหมดในไฟล์และเก็บค่า 32 จำนวนท้ายที่สุดคุณจะได้ค่า 32 ค่าที่แน่นอน (2 ^ 32/2) หรือน้อยกว่าค่านั้นเล็กน้อย ความแตกต่างที่มากที่สุด (2 ^ 32/2) และผลรวมจะให้บิตทั้งหมดที่กำหนดในแต่ละตำแหน่งของค่าที่หายไป
เมื่อคุณได้รับแล้วคุณสามารถกำหนดชุดที่เป็นไปได้ทั้งหมด 4 ค่าที่สามารถให้ผลรวมเหล่านั้น จากนั้นคุณสามารถผ่านค่าต่างๆในไฟล์อีกครั้งเพื่อตรวจสอบค่าใด ๆ ที่เป็นส่วนหนึ่งของชุดค่าผสมเหล่านั้น เมื่อคุณพบชุดค่าผสมที่มีค่านั้นจะถูกกำจัดออกไป เมื่อคุณมีชุดค่าผสมที่เป็นไปได้เพียงชุดเดียวเท่านั้นคุณจะได้คำตอบ
ตัวอย่างเช่นการใช้ nibble คุณมีค่าต่อไปนี้:
1010
0110
1111
0111
1101
1001
0100
0101
0001
1011
1100
1110
จำนวนบิตทั้งหมดที่ตั้งค่าในแต่ละตำแหน่งคือ:
7867
ลบออกจาก 8 (4 ^ 2/2) เราได้:
1021
ซึ่งหมายความว่ามีชุดค่าที่เป็นไปได้ 4 ค่าต่อไปนี้:
1000
0000
0011
0010
1010
0001
0010
0000
(ยกโทษให้ฉันถ้าฉันพลาดใด ๆ ฉันแค่ทำสิ่งนี้ด้วยสายตา)
แล้วดูตัวเลขเดิมอีกครั้งเราพบ 1,010 ทันทีความหมายชุดแรกคือคำตอบ
determine all the possible sets of 4 values that could give those totals
แต่คุณปัดสวะ ฉันคิดว่านี่เป็นส่วนสำคัญของวิธีแก้ปัญหาที่ขาดหายไปจากคำตอบของคุณ นอกจากนี้ยังสามารถส่งผลกระทบต่อความซับซ้อนของเวลาและพื้นที่
สมมติว่าไฟล์ถูกเรียงลำดับตามจำนวนที่เพิ่มขึ้น:
ตรวจสอบให้แน่ใจว่ามันมีตัวเลข (2³²-4)
ตอนนี้ถ้าไฟล์เสร็จสมบูรณ์ (หรือถ้าตัวเลขที่หายไป 4 ตัวเป็น 4 ตัวสุดท้าย) การอ่านคำใด ๆ ในไฟล์ที่ตำแหน่ง N จะคืนค่าที่ตรงกัน N
ใช้การค้นหาแบบแบ่งขั้วบนตำแหน่ง [0..2³²-4-1) เพื่อค้นหาเพื่อหาหมายเลข X1 ที่ไม่ได้คาดหวังครั้งแรก
เมื่อพบว่าหมายเลขที่ขาดหายไปเป็นครั้งแรกให้ทำการค้นหา dichtotomy อีกครั้งในตำแหน่ง [X1 .. (2³²-4-1)] เพื่อค้นหาตำแหน่งที่สองที่ขาดหายไป X2: ครั้งนี้การอ่านคำที่ตำแหน่ง N ควรกลับค่าการจับคู่ N-1 หากไม่มีหมายเลขที่หายไปอีกต่อไป (เนื่องจากคุณผ่านหมายเลขที่ขาดหายไปหนึ่งรายการ)
ทำซ้ำเช่นเดียวกันสำหรับตัวเลขที่เหลือทั้งสอง ในการทำซ้ำครั้งที่สามการอ่านคำที่ตำแหน่ง N ควรกลับ N-2 และที่สี่มันควรกลับ N-3
Caveat: ฉันไม่ได้ทดสอบสิ่งนี้ แต่ฉันคิดว่ามันควรจะทำงาน :)
ตอนนี้ในชีวิตจริงฉันเห็นด้วยกับคำตอบอื่น ๆ : คำถามแรกเกี่ยวกับสภาพแวดล้อม เรามีหน่วยความจำ RAM (เท่าไหร่) เป็นไฟล์บนอุปกรณ์จัดเก็บข้อมูลการเข้าถึงโดยตรงหรือไม่นี่คือการดำเนินการครั้งเดียว (ไม่จำเป็นต้องปรับให้เหมาะสม) หรือสำคัญอย่างยิ่ง (สำคัญสำหรับแต่ละวงจร ฯลฯ
จากนั้นหาการประนีประนอมที่ยอมรับได้สำหรับบริบท อย่างน้อยนี้แสดงว่าคุณเริ่มวิเคราะห์ปัญหาก่อนค้นหาอัลกอริทึม
เช่นเดียวกับคำถามมาตรฐานทั้งหมดการแก้ปัญหาคือ google พวกเขาก่อนการสัมภาษณ์
คำถามและรูปแบบนี้มีคำตอบที่ 'ถูกต้อง' ที่ชัดเจนมากซึ่งเกี่ยวข้องกับ XORing ตัวเลขทั้งหมด มันควรจะแสดงให้คุณเข้าใจดัชนีในฐานข้อมูลหรือบางสิ่งบางอย่าง ดังนั้นศูนย์คะแนนสำหรับ 'อาจทำงานได้ แต่ไม่ใช่สิ่งที่มันบอกว่าบนกระดาษ' คำตอบฉัน afriad
ในทางบวกมีชุดคำถามที่ จำกัด เหล่านี้ไม่กี่ชั่วโมงการแก้ไขจะทำให้คุณดูเหมือนอัจฉริยะ เพียงจำไว้ว่าคุณแกล้งทำมันออกมาในหัวของคุณ
แก้ไข อาห์ดูเหมือนว่า 4 มีวิธีการที่แตกต่างจากแฮคเกอร์
แก้ไข Downvoters: นี่คือวิธีแก้ไขปัญหาตำราเรียน O (n) ที่ตีพิมพ์ในปัญหาที่แน่นอนที่ระบุไว้ใน OP