ความถี่ของคำที่มีการสั่งซื้อในความซับซ้อน O (n)


11

ในระหว่างการสัมภาษณ์ตำแหน่งนักพัฒนา Java ฉันถูกถามต่อไปนี้:

เขียนฟังก์ชั่นที่ใช้สองพารามิเตอร์:

  1. String แสดงเอกสารข้อความและ
  2. จำนวนเต็มซึ่งระบุจำนวนรายการที่จะส่งคืน

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

ต่อไปนี้เป็นสิ่งที่ฉันตอบ (ใน pseudocode) ไม่ใช่แต่ค่อนข้างเวลาเนื่องจากการเรียงลำดับ ฉันไม่สามารถคิดออกว่าจะทำมันเวลา O(n)O(nเข้าสู่ระบบn)O(n)

wordFrequencyMap = new HashMap<String, Integer>();
words = inputString.split(' ');

for (String word : words) {
  count = wordFrequencyMap.get(word);
  count = (count == null) ? 1 : ++count;
  wordFrequencyMap.put(word, count);
}

return wordFrequencyMap.sortByValue.keys

มีใครรู้บ้างหรือบางคนให้คำแนะนำแก่ฉันบ้าง


1
ใช้ตารางแฮช
Yuval Filmus

การใช้ hashtable ไม่สามารถแก้ปัญหาได้ นอกจากนี้ hashtable เป็น Java รุ่นเก่า
user2712937

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

@YuvalFilmus ขอขอบคุณ แต่ตารางแฮชค่อนข้างคล้ายกับแผนที่แฮชซึ่งฉันใช้อยู่แล้ว (ความแตกต่างที่สำคัญระหว่างโครงสร้างข้อมูล 2 แบบคือการซิงโครไนซ์ซึ่งไม่ได้ใช้ที่นี่) บันทึก (n) ในเหมืองมาจากการเรียงลำดับค่าในแผนที่แฮช
2712937

3
อย่างไรก็ตามเว็บไซต์นี้เน้นแนวคิดและอัลกอริธึมไม่ใช่ในรหัส ดังนั้นโดยปกติเราจะขอให้คุณลบโค้ด Java และให้คำอธิบายเชิงแนวคิดเกี่ยวกับวิธีการของคุณ (อาจเป็นรหัสเทียมที่รัดกุมในระดับสูงหากจำเป็น) นอกจากนี้ในเว็บไซต์นี้คำถามที่เกี่ยวข้องคือโครงสร้างข้อมูลและอัลกอริทึมที่จะใช้ Java API เฉพาะนั้นไม่ใช่หัวข้อสำหรับเว็บไซต์นี้ (แต่คุณสามารถถามได้ใน StackOverflow) และในทำนองเดียวกันไม่ว่าจะHashtableเป็น Java รุ่นเก่าหรือไม่เกี่ยวข้องกับวัตถุประสงค์ของเว็บไซต์นี้จริงๆ
DW

คำตอบ:


10

ฉันขอแนะนำรูปแบบของการนับการกระจาย:

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

คุณสามารถแทนที่ trie ด้วยโครงสร้างข้อมูลอื่น ๆ ในระยะแรก


+1 แม้ว่าฉันไม่แน่ใจเกี่ยวกับเรื่องนี้ เป็น O (n) เนื่องจากจำนวนคำที่จะส่งคืนถูก จำกัด ด้วย n จำนวนอักขระ แต่นี่คือสิ่งที่คำถามถามหรือไม่ หรือผลลัพธ์ที่เป็นอิสระจากจำนวนคำที่ส่งคืน?
Nikos M.

@NikosM มันคือ ;nเป็นกรณีทั่วไปที่เลวร้ายที่สุดบนขอบเขตของจำนวนคำที่ส่งคืนไม่จำเป็นต้องมีข้อสมมติฐาน
Raphael

@Raphael, yeap แก้ไขผมคิดเกี่ยวกับเรื่องนี้เพราะมันถูกถามในการสัมภาษณ์, เทคนิคที่เป็นไปได้ในคำถาม ..
Nikos เมตร

ฉันสงสัยว่ามีขั้นตอนวิธีเชิงเส้นตรงเวลาที่มีประสิทธิภาพในอวกาศหรือไม่
saadtaame

3
@ saadtaame, yup, นั่นเป็นคำถามที่น่าสนใจ อาจจะมีมูลค่าการโพสต์แยกเป็นคำถามแยกต่างหาก มันไม่ใช่แค่ประสิทธิภาพของพื้นที่ โซลูชัน trie ยังใช้ตัวชี้มากซึ่งอาจทำให้ช้าลงในทางปฏิบัติ (เนื่องจากลำดับชั้นของหน่วยความจำทำงานในเครื่องจริง) "ประสิทธิภาพ" แตกต่างจากเวลาทำงานที่เลวร้ายที่สุด ไม่ใช่เรื่องแปลกสำหรับการทำความสะอาดO(nLGn) อัลกอริทึมเวลาในการเอาชนะตัวชี้อย่างเข้มข้น O(n)อัลกอริธึมเวลาดังนั้นคำถามนี้ดูเหมือนจะเป็นการตัดทอนอัลกอริธึมที่อาจเป็นทางเลือกที่ดีกว่าในทางปฏิบัติแล้ว
DW

3

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

heap เป็นวิธีทั่วไปในการรวมค่า k สูงสุดแม้ว่าจะสามารถใช้วิธีอื่นได้ (ดูhttps://en.wikipedia.org/wiki/Partial_sorting )

สมมติว่า k เป็นพารามิเตอร์ที่สองด้านบนและเป็นค่าคงที่ในคำแถลงปัญหา (ดูเหมือนจะเป็น):

  1. สร้าง trie ของคำที่มีจำนวนที่เกิดขึ้นในแต่ละโหนด
  2. เริ่มต้นกองขนาด k
  3. ข้าม trie และ min-probe / แทรกแต่ละคู่ (ลีฟ, การเกิดขึ้นนับ) ใน heap -k
  4. เอาท์พุท k ใบไม้และจำนวนสูงสุด (ซึ่งเป็นความเจ็บปวดจริง ๆ เพราะคุณต้องการพอยน์เตอร์หลักเพื่อแมปใบไม้แต่ละใบกลับเป็นคำ)

เนื่องจากขนาดฮีปเป็นค่าคงที่การดำเนินการฮีปคือ O (1) ดังนั้นขั้นตอนที่ 3 คือ O (n)

กองยังสามารถรักษาแบบไดนามิกในขณะที่สร้างคู่ชีวิต


2

อัลกอริทึมของคุณไม่ได้ทำงานในเวลา O(nlogn); แทรกΘ(n) สิ่งต่าง ๆ ในเวลา hashtable ต้นทุน Ω(n2) แล้ว (กรณีที่เลวร้ายที่สุด)


สิ่งที่ตามมาคือผิด ; ฉันจะทิ้งไว้ที่นี่เพื่อดูตัวอย่าง

อัลกอริทึมต่อไปนี้ทำงานในเวลาที่เลวร้ายที่สุด O(n) (สมมติว่าตัวอักษร Σ ขนาดคงที่) n จำนวนตัวอักษรในข้อความ

  1. สร้างต้นไม้ต่อท้ายของข้อความเช่นกับอัลกอริทึมของ Ukkonen

    หากการก่อสร้างไม่ได้ทำเช่นนี้ให้เพิ่มจำนวนของใบไม้ที่สามารถเข้าถึงได้ไปยังโหนด (ภายใน) ทุกโหนด

  2. สำรวจต้นไม้จากรากและตัดกิ่งไม้ทั้งหมดออกจากพื้นที่แรก (สีขาว)

  3. สำรวจต้นไม้และเรียงลำดับรายการลูก ๆ ของทุกโหนดด้วยการนับใบไม้

  4. ผลผลิตของต้นไม้ (ใบจากซ้ายไปขวา) ตอนนี้เป็นรายการของคำทั้งหมดเรียงลำดับตามความถี่

เกี่ยวกับรันไทม์:

  1. อัลกอริทึมของ Ukkonen (ในรูปแบบที่ปรับปรุงแล้ว) ทำงานได้ทันเวลา O(n); การรักษาจำนวนใบไม้ไม่เพิ่มΘ- ค่าใช้จ่ายของอัลกอริทึม
  2. เราต้องสำรวจหนึ่งโหนดต่อตัวละครของทุกคำที่เกิดขึ้นในข้อความ เนื่องจากมีมากที่สุดn คู่คำศัพท์ที่แตกต่างกันเราไปเยี่ยมชมมากที่สุด n โหนด
  3. เราไปเยี่ยมชมมากที่สุด n โหนด (cf 2. ) และใช้เวลา O(|Σ|เข้าสู่ระบบ|Σ|)=O(1) ต่อโหนด
  4. เราสามารถรับผลผลิต (ซึ่งมีขนาดของหลักสูตร O(n)) โดยการสำรวจเส้นทางที่เรียบง่ายในเวลา O(n) (cf 2. )

ขอบเขตที่แม่นยำยิ่งขึ้นสามารถรับได้โดยการเพิ่มจำนวนของคำที่แตกต่างกัน; หากมีน้อยต้นไม้จะเล็กหลังจาก 2


อัลกอริทึมไม่ถูกต้อง (ไม่เรียงลำดับ) ฉันไม่แน่ใจว่าเวลาเชิงเส้นจะเป็นไปได้อีกต่อไปแล้ว
กราฟิลส์

1

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

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

หรือถ้าคุณต้องการเล่นให้ปลอดภัยกว่านี้ก่อนที่จะให้คำตอบก่อนอื่นให้ถามว่า "คุณสนใจความแตกต่างระหว่างที่คาดหวังหรือไม่ O(n) เวลาทำงานและกรณีที่เลวร้ายที่สุด O(n)ใช้เวลา? "จากนั้นปรับคำตอบของคุณให้พร้อมสำหรับผู้สัมภาษณ์เพื่อถามคุณว่าคุณจะเลือกในทางปฏิบัติอย่างไร (ถ้าใช่ให้คะแนน!


การเก็บรักษา Θ(n) สิ่งต่าง ๆ ใน hashtable ใช้ Ω(n2)เวลาในกรณีที่เลวร้ายที่สุดแล้ว
กราฟิลส์

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

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

0

โซลูชันที่ใช้ Hashtable

ไม่แน่ใจว่าทำไม hashtable สร้างความซับซ้อน Ω(n2) ถ้า nคือจำนวนอักขระ (ไม่ใช่คำ)

หากคุณวนซ้ำทุกตัวละครในเอกสารและในขณะที่คุณกำลังวนซ้ำคำนวณรหัสแฮชโค้ดของคำคุณจะต้องผ่าน nตัวละคร นั่นคือทันทีที่พบจดหมายคำนั้นจะเริ่มขึ้นดังนั้นให้เริ่มคำนวณแฮชจนกว่าคำจะจบลง (มีบางกรณีพิเศษสำหรับเครื่องหมายวรรคตอน แต่สิ่งเหล่านั้นไม่ส่งผลกระทบต่อความซับซ้อน) สำหรับทุกคำเมื่อคำนวณ hash แล้วให้เพิ่มลงใน hashtable นี่คือการหลีกเลี่ยงการไปทุกคำสองครั้งคือก่อนอื่นให้วนซ้ำผ่านเอกสารเพื่อค้นหาคำแล้วใส่คำเหล่านั้นลงใน hashtable แม้ว่าความซับซ้อนในกรณีนั้นก็อาจจะเป็นΩ(n).

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

สมมติฐานคืออัลกอริทึมการแปลงแป้นพิมพ์เป็นเส้นตรงเวลาตามจำนวนตัวอักษร

วิธีการจัดเรียงตาม Radix

อีกทางหนึ่งคือสมมติว่าภาษาอังกฤษเนื่องจากความยาวของคำศัพท์เป็นที่รู้จักกันดีฉันจึงควรสร้างกริดและใช้การเรียงลำดับแบบ Radix ซึ่งเป็น O(kยังไม่มีข้อความ) ที่ไหน k จะเป็นความยาวสูงสุดของคำในภาษาอังกฤษและ ยังไม่มีข้อความคือจำนวนคำทั้งหมด ป.ร. ให้ไว้n คือจำนวนอักขระในเอกสารและ k เป็นค่าคงที่, ไม่ต้องบอกจำนวนเงินนี้ O(n).

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

คำที่ยาวที่สุดสองสามอันดับแรกในภาษาอังกฤษนั้นมีความยาวน่าขันแต่ก็สามารถเพิ่มความยาวของคำด้วยจำนวนที่เหมาะสม (เช่น 30 หรือน้อยกว่า) และตัดคำที่ยอมรับขอบเขตของข้อผิดพลาดที่อาจมาพร้อมกับมัน


(1) เนื่องจากในตำราส่วนใหญ่ความยาวสูงสุดของคำถูกล้อมรอบด้วยค่าคงที่จำนวนคำคือ Θ(n)เช่นกัน (2) ขึ้นอยู่กับฟังก์ชั่นแฮชอาจไม่สามารถคำนวณแฮชได้ทันทีขณะอ่านคำ (3) ในกรณีที่เลวร้ายที่สุดคำทั้งหมดแฮชไปยังตำแหน่งเดียวกันในตารางโดยทำการแทรกและค้นหาΘ(n).
FrankW

สวัสดี FrankW (2) ฉันระบุว่าเราสามารถเลือกฟังก์ชั่น (เช่นแฮชกลิ้ง) ที่เราสามารถคำนวณได้ทันที แม้ว่าจะไม่เป็นเช่นนั้นความซับซ้อนโดยรวมจะไม่เปลี่ยนแปลงตราบใดที่การแฮชเป็นเวลาเชิงเส้นเพราะการอ่านและการแฮชจะเป็นเช่นนั้นO(n+n)การดำเนินงาน (3) แน่นอน แต่ขึ้นอยู่กับการเลือกอัลกอริทึมอีกครั้ง มีอัลกอริทึมมากมายที่ทำได้ดีกว่าอย่างมากหากคำต่างกัน สำหรับคำเดียวกันคุณเพียงเพิ่มจำนวนในรายการเดียว ในการเปรียบเทียบเมื่อฉันต้องเลือกอัลกอริธึมการเรียงลำดับO(n2)แต่โดยทั่วไปแล้วฉันจะเลือก :-) ดีกว่า
Omer Iqbal

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

ทำไมตารางแฮชนำไปสู่ O(n2)ความซับซ้อนของกรณีที่เลวร้ายที่สุด? เป็นเพราะในหลักการเวลาที่เลวร้ายที่สุดในการใช้งานของแฮชเทเบิลนั้นแย่มาก ในทางปฏิบัติกรณีที่เลวร้ายที่สุดนี้แทบจะไม่เคยดูเหมือนว่าจะเกิดขึ้น (โดยเฉพาะถ้าคุณเลือกฟังก์ชันแฮชอย่างถูกต้องด้วยการสุ่มและเทคนิคอื่น ๆ ) และคุณสามารถพิสูจน์ทฤษฎีบทเพื่อพิสูจน์ว่าทำไมถึงเป็นเช่นนั้น แต่ถ้านี่เป็นคำถาม ข้อควรพิจารณาในทางปฏิบัติเช่นนั้นออกไปนอกหน้าต่าง (หรืออย่างน้อยนั่นก็เป็นข้อโต้แย้งที่คุณอาจได้ยิน)
DW

แทรกตารางแฮชธรรมดา O(n2)เพราะการชนต้องมีรายการที่จะวางไว้ที่อื่น ที่นี่เราไม่จำเป็นต้องใส่ข้อมูลที่ซ้ำกัน 1) คำเดียวกันซ้ำซ้ำ: จากนั้นนับการนับสิ่งนี้รับประกันได้ว่าจะเป็นO(1)บวกเวลาคร่ำครวญ 2) คำที่แตกต่างกันแฮชเดียวกัน: นั่นคือที่คำถามเกี่ยวกับวิธีแฮชดี / ไม่ดีและถ้าขนาดของตารางมีขนาดเล็กเกินไป ฉันเห็นด้วยΩ(1)แต่ขึ้นอยู่กับตัวเลือกฉันยังระบุด้วยว่า "เราสามารถเข้าใกล้ได้O(1) สำหรับการแทรกและการนับ "เราสามารถพูดคุยเกี่ยวกับขนาดและฟังก์ชั่นของตารางที่จะทำให้เราเข้าใกล้ O(1).
โอเมอิกอิกบัล
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.