ทั้งสองดูเหมือนกันมากและมีโครงสร้างเกือบเหมือนกัน ความแตกต่างคืออะไร? ความซับซ้อนของเวลาสำหรับการดำเนินงานที่แตกต่างกันของแต่ละคนคืออะไร
ทั้งสองดูเหมือนกันมากและมีโครงสร้างเกือบเหมือนกัน ความแตกต่างคืออะไร? ความซับซ้อนของเวลาสำหรับการดำเนินงานที่แตกต่างกันของแต่ละคนคืออะไร
คำตอบ:
ฮีปจะรับประกันว่าองค์ประกอบในระดับที่สูงกว่านั้นจะใหญ่กว่า (สำหรับแม็กซ์ฮีป) หรือเล็กกว่า (สำหรับมิน - ฮีป) กว่าองค์ประกอบในระดับที่ต่ำกว่าในขณะที่ BST รับประกันคำสั่งซื้อ (จาก "ซ้าย" ถึง "ขวา") ถ้าคุณต้องการเรียงองค์ประกอบไปด้วย BST โดย Dante ไม่ได้เกินบรรยาย
ฮีปจะดีกว่าที่ findMin / findMax (O (1)) ในขณะที่ BST นั้นดีในการค้นหาทั้งหมด (O (logN)) ส่วนแทรกคือ O (logN) สำหรับโครงสร้างทั้งสอง หากคุณสนใจเกี่ยวกับ findMin / findMax (เช่นลำดับความสำคัญที่เกี่ยวข้อง) ให้ไปกับ heap ถ้าคุณต้องการเรียงทุกอย่างไปด้วย BST
ทั้งแผนผังการค้นหาแบบทวิตและฮีปแบบไบนารีเป็นโครงสร้างข้อมูลแบบทรี
ฮีปจำเป็นต้องใช้โหนดเพื่อให้ความสำคัญกับลูก ๆ ใน max heap ลูกของแต่ละโหนดต้องน้อยกว่าตัวเอง นี่คือสิ่งที่ตรงกันข้ามสำหรับนาทีกอง:
แผนผังการค้นหาแบบไบนารี (BST) จะติดตามการสั่งซื้อเฉพาะ (การสั่งซื้อล่วงหน้าการสั่งซื้อภายหลังการสั่งซื้อ) ระหว่างโหนดพี่น้อง ต้นไม้จะต้องเรียงลำดับเหมือนกอง:
BST มีค่าเฉลี่ยสำหรับการแทรกการลบและการค้นหา
Binary Heaps มีค่าเฉลี่ยสำหรับ findMin / findMax และสำหรับการแทรกและการลบO ( 1 ) O ( บันทึกn )
สรุป
Type BST (*) Heap
Insert average log(n) 1
Insert worst log(n) log(n) or n (***)
Find any worst log(n) n
Find max worst 1 (**) 1
Create worst n log(n) n
Delete worst log(n) log(n)
เวลาเฉลี่ยทั้งหมดในตารางนี้เท่ากับเวลาที่แย่ที่สุดยกเว้นการแทรก
*
: ทุกที่ในคำตอบนี้ BST == สมดุล BST เนื่องจากความไม่สมดุลจะดูด asymptotically**
: การใช้การดัดแปลงเล็กน้อยอธิบายในคำตอบนี้***
: log(n)
สำหรับทรีของตัวชี้ฮีปn
สำหรับฮีปอาร์เรย์แบบไดนามิกข้อดีของไบนารีฮีปผ่าน BST
แทรกเวลาเฉลี่ยเป็นกองไบนารีO(1)
สำหรับ BST O(log(n))
เป็น นี่คือคุณสมบัตินักฆ่าของกอง
นอกจากนี้ยังมีฮีปอื่น ๆ ที่มีค่าเสื่อมราคาO(1)
(แข็งแกร่ง) เช่นFibonacci Heapและแม้แต่กรณีที่เลวร้ายที่สุดเช่นคิว Brodalถึงแม้ว่ามันอาจไม่สามารถใช้งานได้จริงเนื่องจากประสิทธิภาพที่ไม่ใช่เชิงเส้น: https://stackoverflow.com/questions/30782636 / มี-fibonacci-กองหรือ brodal-คิวที่ใช้ในการปฏิบัติงานได้ทุกที่
สามารถใช้ไบนารีฮีปได้อย่างมีประสิทธิภาพที่ด้านบนของอาร์เรย์แบบไดนามิกหรือแผนภูมิแบบอิงพอยน์เตอร์, ต้นไม้ BST เท่านั้น ดังนั้นสำหรับฮีปเราสามารถเลือกการใช้อาเรย์ที่มีประสิทธิภาพมากขึ้นถ้าเราสามารถจ่ายค่าเวลาในการปรับขนาดได้เป็นครั้งคราว
สร้างกองไบนารีเป็นO(n)
กรณีที่เลวร้ายที่สุด , O(n log(n))
สำหรับ BST
ข้อได้เปรียบของ BST มากกว่าไบนารีฮีป
O(log(n))
ค้นหาสำหรับองค์ประกอบโดยพลการเป็น นี่คือคุณสมบัตินักฆ่าของ BST
สำหรับกองมันเป็นโดยทั่วไปยกเว้นสำหรับองค์ประกอบที่ใหญ่ที่สุดซึ่งเป็นO(n)
O(1)
ข้อได้เปรียบ "เท็จ" ของกองมากกว่า BST
กองคือO(1)
จะหา max, O(log(n))
BST
นี่เป็นความเข้าใจผิดที่พบบ่อยเพราะมันเป็นเรื่องเล็กน้อยที่จะแก้ไข BST เพื่อติดตามองค์ประกอบที่ใหญ่ที่สุดและอัปเดตเมื่อใดก็ตามที่องค์ประกอบนั้นสามารถเปลี่ยนแปลงได้: ในการแทรกของ swap ที่ใหญ่กว่าในการลบค้นหาที่ใหญ่เป็นอันดับสอง https://stackoverflow.com/questions/7878622/can-we-use-binary-search-tree-to-simulate-heap-operation (กล่าวถึงโดย Yeo )
จริงๆแล้วนี่เป็นข้อ จำกัดของฮีปเมื่อเปรียบเทียบกับ BST: การค้นหาที่มีประสิทธิภาพเพียงอย่างเดียวคือสำหรับองค์ประกอบที่ใหญ่ที่สุด
ค่าเฉลี่ยของการแทรกฮีปไบนารีคือ O(1)
แหล่งที่มา:
อาร์กิวเมนต์ที่ใช้งานง่าย:
ใน binary heap การเพิ่มค่าที่ดัชนีที่กำหนดนั้นก็O(1)
เป็นเช่นเดียวกัน แต่ถ้าคุณต้องการที่จะทำว่ามันเป็นโอกาสที่คุณจะต้องการให้ดัชนีพิเศษ up-to-date เกี่ยวกับการดำเนินงานของกองhttps://stackoverflow.com/questions/17009056/how-to-implement-ologn-decrease- key-operation-for-min-heap-based-priority-queuเช่นสำหรับ Dijkstra เป็นไปได้โดยไม่มีค่าใช้จ่ายเพิ่มเติม
ไลบรารีมาตรฐาน GCC C ++ แทรกมาตรฐานในฮาร์ดแวร์จริง
ฉันเปรียบเทียบ C + + std::set
( ต้นไม้สีแดงดำ BST ) และstd::priority_queue
( ไดนามิกอาร์เรย์ฮีป ) แทรกเพื่อดูว่าฉันถูกเกี่ยวกับเวลาแทรกและนี่คือสิ่งที่ฉันได้รับ:
ชัดเจนมาก:
เวลาแทรกฮีปเป็นค่าคงที่โดยทั่วไป
เราสามารถเห็นคะแนนการปรับขนาดอาร์เรย์แบบไดนามิกได้อย่างชัดเจน เนื่องจากเรากำลังเฉลี่ยเม็ดมีดทุก ๆ 10k ที่สามารถมองเห็นสิ่งต่าง ๆ ได้จากเสียงรบกวนของระบบทั้งหมดยอดเขาเหล่านั้นจึงมีขนาดใหญ่กว่าที่แสดงจริงประมาณ 10k เท่า!
กราฟที่ซูมไม่รวมถึงการปรับขนาดของอาเรย์เท่านั้นและแสดงให้เห็นว่าเม็ดมีดเกือบทั้งหมดอยู่ในระดับต่ำกว่า 25 นาโนวินาที
BST เป็นลอการิทึม เม็ดมีดทั้งหมดนั้นช้ากว่าเม็ดมีดทั่วไปมาก
การวิเคราะห์รายละเอียด BST vs hashmap ที่: https://stackoverflow.com/questions/18414579/what-data-structure-is-inside-stdmap-in-c/51945119#51945119
ไลบรารีมาตรฐาน GCC C ++ จะแทรกเกณฑ์มาตรฐานบน gem5
gem5m5 dumpstats
เป็นโปรแกรมจำลองระบบเต็มรูปแบบและดังนั้นจึงยังมีนาฬิกาที่ถูกต้องเพียบด้วยกับ ดังนั้นฉันจึงลองใช้มันเพื่อประเมินการกำหนดเวลาสำหรับเม็ดมีดแต่ละใบ
การตีความ:
กองยังคงคงที่ แต่ตอนนี้เราเห็นรายละเอียดเพิ่มเติมว่ามีเพียงไม่กี่บรรทัดและแต่ละบรรทัดที่สูงขึ้นจะกระจัดกระจายมากขึ้น
สิ่งนี้จะต้องสอดคล้องกับเวลาแฝงในการเข้าถึงหน่วยความจำที่ทำเพื่อแทรกสูงขึ้นและสูงขึ้น
สิ่งที่ต้องทำฉันไม่สามารถตีความ BST อย่างเต็มที่เพราะมันไม่ได้ดูลอการิทึมและค่อนข้างคงที่
ด้วยรายละเอียดที่มากขึ้นนี้ แต่เราสามารถเห็นได้ว่ามีเส้นที่แตกต่างกันสองสามอัน แต่ฉันไม่แน่ใจว่ามันหมายถึงอะไร: ฉันคาดว่าบรรทัดล่างจะบางกว่าเนื่องจากเราแทรกด้านบนสุด
เทียบกับเรื่องนี้ติดตั้ง Buildrootบน aarch64 HPI CPU
BST ไม่สามารถนำไปใช้อย่างมีประสิทธิภาพในอาเรย์
การดำเนินการฮีปจำเป็นต้องทำให้ฟองต้นไม้ขึ้นหรือลงเพียงครั้งเดียวดังนั้นค่าเฉลี่ยของO(log(n))
กรณีที่แย่ที่สุดO(1)
การรักษาความสมดุล BST นั้นต้องใช้การหมุนต้นไม้ซึ่งสามารถเปลี่ยนองค์ประกอบยอดนิยมสำหรับอีกอันหนึ่งได้และจะต้องย้ายอาร์เรย์ทั้งหมดไปรอบ ๆ ( O(n)
)
สามารถใช้ Heaps ได้อย่างมีประสิทธิภาพในอาเรย์
ผู้ปกครองและเด็กดัชนีสามารถคำนวณได้จากดัชนีปัจจุบันเป็นที่แสดงที่นี่
ไม่มีการดำเนินการที่สมดุลเช่น BST
ลบนาทีเป็นการดำเนินการที่น่าเป็นห่วงที่สุดเนื่องจากจะต้องเลื่อนจากบนลงล่าง แต่มันก็สามารถทำได้โดยการ "แทรกซึมลง" สาขาเดียวของกองตามที่อธิบายไว้ที่นี่ สิ่งนี้นำไปสู่กรณีที่เลวร้ายที่สุด O (บันทึก (n)) เนื่องจากฮีปมีความสมดุลที่ดีเสมอ
หากคุณกำลังแทรกโหนดเดียวสำหรับทุก ๆ โหนดที่คุณลบคุณจะเสียความได้เปรียบของการแทรกเฉลี่ยแบบ asymptotic O (1) ที่ฮีปมีให้ซึ่งการลบจะมีอิทธิพลเหนือและคุณอาจใช้ BST อย่างไรก็ตาม Dijkstra อัพเดตโหนดหลายครั้งสำหรับการลบแต่ละครั้งดังนั้นเราจึงใช้ได้
ฮีปอาร์เรย์แบบไดนามิกกับฮีปต้นไม้ของพอยเตอร์
สามารถดำเนินการฮีปได้อย่างมีประสิทธิภาพที่ด้านบนของฮีปพอยน์เตอร์: https://stackoverflow.com/questions/19720438/is-it-possible-to-make-efficient-pointer-binary-heap-implementations
การใช้อาเรย์แบบไดนามิกนั้นมีพื้นที่มากขึ้น สมมติว่าแต่ละองค์ประกอบของฮีปประกอบด้วยเพียงตัวชี้ไปที่struct
:
การใช้งานทรีต้องเก็บพอยน์เตอร์สามตัวสำหรับแต่ละองค์ประกอบ: parent, child child และ right child ดังนั้นการใช้หน่วยความจำจึงเป็นตลอดเวลา4n
(3 พอยน์เตอร์พอยน์เตอร์ + 1 พอยน์เตอร์struct
)
Tree BSTs จะต้องการข้อมูลเพิ่มเติมเช่นสมดุลสีดำ - แดง - เนส
การใช้งานอาร์เรย์แบบไดนามิกอาจมีขนาด2n
หลังจากเพิ่มขึ้นสองเท่า 1.5n
ดังนั้นโดยเฉลี่ยจะเป็นไปได้
ในทางตรงกันข้ามต้นไม้กองมีแทรกกรณีที่เลวร้ายที่สุดที่ดีกว่าเนื่องจากการคัดลอกอาร์เรย์แบบไดนามิกการสำรองเพื่อสองเท่าของขนาดใช้O(n)
กรณีที่เลวร้ายที่สุดในขณะที่ต้นไม้กองเพียงแค่การจัดสรรใหม่ขนาดเล็กสำหรับแต่ละโหนด
อย่างไรก็ตามอาร์เรย์สำรองสองเท่าจะถูกO(1)
ตัดจำหน่ายดังนั้นจึงพิจารณาถึงความล่าช้าสูงสุด กล่าวถึงที่นี่
ปรัชญา
BSTs รักษาทรัพย์สินส่วนกลางระหว่างผู้ปกครองและผู้สืบทอดทั้งหมด (เหลือน้อยกว่าใหญ่กว่า)
โหนดบนสุดของ BST เป็นองค์ประกอบกลางซึ่งต้องใช้ความรู้ระดับโลกในการบำรุงรักษา (รู้จำนวนองค์ประกอบที่เล็กและใหญ่กว่า)
สถานที่ให้บริการส่วนกลางนี้มีราคาแพงกว่าในการรักษา (แทรก log n) แต่ให้การค้นหาที่มีประสิทธิภาพมากขึ้น (การค้นหาบันทึก n)
กองรักษาทรัพย์สินในท้องถิ่นระหว่างผู้ปกครองและเด็กโดยตรง (ผู้ปกครอง> เด็ก)
สิ่งสำคัญอันดับต้น ๆ ของฮีปคือองค์ประกอบขนาดใหญ่ซึ่งต้องการเพียงความรู้ในท้องถิ่นในการบำรุงรักษา (การรู้จักผู้ปกครองของคุณ)
รายการที่ลิงก์ทวีคูณ
รายการที่เชื่อมโยงเป็นทวีคูณสามารถเห็นได้เป็นชุดย่อยของฮีปที่รายการแรกมีลำดับความสำคัญมากที่สุดดังนั้นให้เปรียบเทียบที่นี่ด้วย:
O(1)
กรณีที่แย่ที่สุดเนื่องจากเรามีตัวชี้ไปยังรายการต่างๆและการอัปเดตนั้นง่ายมากO(1)
เฉลี่ยจึงแย่กว่ารายการที่เชื่อมโยง แลกเปลี่ยนสิ่งที่มีตำแหน่งแทรกทั่วไปเพิ่มเติมO(n)
สำหรับทั้งคู่กรณีการใช้งานสำหรับกรณีนี้คือเมื่อคีย์ของฮีปคือการประทับเวลาปัจจุบัน: ในกรณีนั้นรายการใหม่จะไปที่จุดเริ่มต้นของรายการเสมอ ดังนั้นเราสามารถลืมการประทับเวลาที่แน่นอนทั้งหมดและเพียงแค่รักษาตำแหน่งในรายการเป็นลำดับความสำคัญ
สิ่งนี้สามารถใช้เพื่อนำแคช LRUไปใช้ เช่นเดียวกับแอปพลิเคชันฮีปเช่น Dijkstraคุณจะต้องเก็บ hashmap เพิ่มเติมจากคีย์ไปยังโหนดที่เกี่ยวข้องของรายการเพื่อค้นหาว่าโหนดใดที่ต้องอัปเดตอย่างรวดเร็ว
เปรียบเทียบ BST ที่สมดุลต่างกัน
ถึงแม้ว่าการแทรกเชิงเส้นกำกับและค้นหาเวลาสำหรับโครงสร้างข้อมูลทั้งหมดที่จัดอยู่ในประเภท "สมดุล BSTs" ที่ฉันเคยเห็นมาแล้วนั้นเหมือนกัน แต่ BBSTs ที่แตกต่างกันมีการแลกเปลี่ยนที่แตกต่างกัน ฉันยังไม่ได้ศึกษาอย่างเต็มที่ แต่ก็เป็นการดีที่จะสรุปการแลกเปลี่ยนเหล่านี้ที่นี่:
ดูสิ่งนี้ด้วย
คำถามที่คล้ายกันเกี่ยวกับ CS: อะไรคือความแตกต่างระหว่างแผนภูมิการค้นหาแบบไบนารีและแบบไบนารีของฮีป
ด้วยโครงสร้างข้อมูลหนึ่งจะต้องแยกระดับของความกังวล
นามธรรมโครงสร้างข้อมูล (วัตถุที่เก็บไว้, การดำเนินงานของพวกเขา) ในคำถามนี้จะแตกต่างกัน หนึ่งใช้คิวลำดับความสำคัญอีกชุดหนึ่ง คิวลำดับความสำคัญไม่สนใจในการค้นหาองค์ประกอบตามอำเภอใจโดยเฉพาะคิวที่มีลำดับความสำคัญสูงสุด
การใช้โครงสร้างอย่างเป็นรูปธรรม ตั้งแต่แรกเห็นทั้งคู่เป็นต้นไม้ (binary) ที่มีคุณสมบัติโครงสร้างต่างกัน ทั้งลำดับที่สัมพันธ์ของคีย์และโครงสร้างส่วนกลางที่เป็นไปได้นั้นแตกต่างกัน (ค่อนข้างไม่แน่ชัดในBST
คีย์ถูกสั่งจากซ้ายไปขวาในฮีปจะถูกสั่งจากบนลงล่าง) เนื่องจาก IPlant พูดอย่างถูกต้องฮีปควรเป็น "สมบูรณ์"
มีความแตกต่างสุดท้ายในคือการดำเนินงานในระดับต่ำ แผนภูมิการค้นหาแบบไบนารี (ไม่สมดุล) มีการนำมาตรฐานไปปฏิบัติโดยใช้พอยน์เตอร์ ฮีปไบนารีเป็นทางตรงกันข้ามมีการนำไปใช้อย่างมีประสิทธิภาพโดยใช้อาร์เรย์ (แม่นยำเนื่องจากโครงสร้างที่ จำกัด )
ด้านบนของคำตอบก่อนหน้าฮีปต้องมีคุณสมบัติโครงสร้างฮีป ต้นไม้จะต้องเต็มและชั้นล่างสุดซึ่งไม่สามารถเต็มได้เสมอต้องเต็มไปทางซ้ายสุดไปทางขวาสุดโดยไม่มีช่องว่าง