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


33

คุณนึกถึงเหตุผลเฉพาะว่าทำไมการลบมักจะนำไปใช้อย่างมีนัยสำคัญยากกว่าการใส่โครงสร้างข้อมูลจำนวนมาก (ส่วนใหญ่?)

ตัวอย่างด่วน: รายการที่เชื่อมโยง การแทรกเป็นเรื่องเล็กน้อย แต่การลบมีบางกรณีที่ทำให้ยากขึ้นอย่างมาก ต้นไม้ค้นหาไบนารี่บาลานซ์เช่น AVL และ Red-black เป็นตัวอย่างคลาสสิกของการลบแบบเจ็บปวด

ฉันอยากจะบอกว่ามันเกี่ยวข้องกับวิธีการที่คนส่วนใหญ่คิดว่า: มันง่ายกว่าสำหรับเราที่จะกำหนดสิ่งต่าง ๆ อย่างสร้างสรรค์ซึ่งนำไปสู่การแทรกง่าย


4
สิ่งที่เกี่ยวกับpop, extract-min?
coredump

5
"ยากที่จะใช้งาน" เป็นเรื่องของจิตวิทยา (ความรู้ความเข้าใจและจุดแข็ง & จุดอ่อนของจิตใจมนุษย์) กว่าการเขียนโปรแกรม (คุณสมบัติของโครงสร้างข้อมูล & อัลกอริธึม)
outis

1
อย่างที่ฉันคิดว่า coredump พูดพาดพิงถึงอย่างน้อยสแต็คควรจะลบอย่างง่าย ๆ เหมือนเพิ่ม (สำหรับสแต็กที่ได้รับการสนับสนุนอาร์เรย์การ popping เป็นเพียงการลดลงของตัวชี้ [1] ในขณะที่การกดอาจต้องคัดลอกอาร์เรย์ทั้งหมด อาร์เรย์) นอกจากนี้ยังมีกรณีการใช้งานบางกรณีที่มีการสันนิษฐานว่าการแทรกจะเป็นประจำและการลบน้อยลงดังนั้นมันจะเป็นโครงสร้างข้อมูลที่มีมนต์ขลังมากซึ่งจำนวนการลบนั้นเกินกว่าการแทรก [1] คุณอาจจะโมฆะการอ้างอิงที่มองไม่เห็นในขณะนี้กับวัตถุที่ผุดเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำซึ่งฉันจำได้เพราะตำราเรียนของ Liskov ไม่ได้
Foon

43
"พนักงานเสิร์ฟคุณช่วยเพิ่ม mayo ให้กับแซนด์วิชนี้ได้ไหม" "แน่นอนไม่มีปัญหาเลย" "คุณสามารถเอามัสตาร์ดทั้งหมดได้หรือไม่" "เอ่อ ...... "
cobaltduck

3
ทำไมการลบจึงซับซ้อนกว่าการเติม การหาร (หรือการแยกตัวประกอบเฉพาะ) ซับซ้อนกว่าการคูณ? รากมีความซับซ้อนมากกว่าการยกกำลังหรือไม่
mu สั้นเกินไป

คำตอบ:


69

มันเป็นมากกว่าแค่ความคิด; มีเหตุผลทางกายภาพ (เช่นดิจิทัล) ว่าทำไมการลบจึงยาก

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

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


3
การกระจายตัวไม่ได้เป็นปัญหาที่ตัวชี้และทิศทางเข้ามาเล่นไม่ว่าจะเป็นโครงสร้างในหน่วยความจำหรือในไดอะแกรม ในหน่วยความจำมันไม่สำคัญว่าแต่ละโหนดมีอยู่เนื่องจากการอ้อม สำหรับรายการการลบโหนดภายใน (ซึ่งเป็นที่ที่คุณมีรูในแผนภาพ) เกี่ยวข้องกับการดำเนินการน้อยกว่าการแทรก (การกำหนดตัวชี้ 1 ครั้งและการจัดสรร 1 ฟรีเทียบกับ 1 และ 1 การกำหนดตัวชี้ 2) สำหรับต้นไม้การใส่โหนดสามารถทำให้ต้นไม้ไม่สมดุลได้เท่ากับการลบ เป็นกรณีขอบที่ก่อให้เกิดปัญหา brito หมายถึงการที่การกระจายตัวไม่สำคัญ
outis

12
ฉันไม่เห็นด้วยว่าการแทรกและการลบแตกต่างกันในการคาดเดาได้ "การแก้ไขรอบ" โหนดรายการเป็นสิ่งที่เกิดขึ้นในสิ่งที่ตรงกันข้ามถ้าโหนดเดียวกันจะถูกแทรกแทน ไม่มีความไม่แน่นอนในทิศทางใดที่จุดใด ๆ และในภาชนะใด ๆ โดยไม่ต้องโครงสร้างที่แท้จริงกับองค์ประกอบของมัน (เช่นต้นไม้ไบนารีที่สมดุลอาร์เรย์ที่มีความสัมพันธ์ที่เข้มงวดระหว่างองค์ประกอบ offsets) ไม่มี "หลุม" เลย ดังนั้นฉันกลัวว่าไม่รู้ว่าคุณกำลังพูดถึงอะไรที่นี่
sqykly

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

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

36

ทำไมจึงมีแนวโน้มที่จะลบยากกว่าการแทรก? โครงสร้างข้อมูลได้รับการออกแบบมากขึ้นโดยคำนึงถึงการแทรกมากกว่าการลบและถูกต้อง

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

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

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


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

1
อืมฉันคิดว่าตัวอย่างอาจเป็นเวกเตอร์ที่เรียงลำดับแบบย้อนกลับ คุณสามารถเพิ่มชุดkขององค์ประกอบสวยอย่างรวดเร็ว: การป้อนข้อมูลการจัดเรียงย้อนกลับและผสานกับเวกเตอร์ที่มีอยู่ O(k log k + n)- ถ้าอย่างนั้นคุณมีโครงสร้างที่มีการแทรกที่ซับซ้อนพอสมควร แต่การบริโภคuองค์ประกอบยอดนิยมนั้นไม่สำคัญและรวดเร็ว ใช้เวลานานuและย้ายจุดสิ้นสุดของเวกเตอร์ แม้ว่าหากใครต้องการสิ่งนั้นฉันจะถูกสาป ฉันหวังว่าสิ่งนี้จะทำให้อาร์กิวเมนต์ของคุณแข็งแกร่งขึ้น
luk32

คุณไม่ควรต้องการปรับให้เหมาะสมสำหรับรูปแบบการใช้งานโดยเฉลี่ยมากกว่าสิ่งที่คุณทำมากที่สุด
ชีฟ

โดยทั่วไปคิวงานแบบ FIFO จะพยายามทำให้เวลาส่วนใหญ่ว่างเปล่า คิวที่ออกแบบมาอย่างดีจะได้รับการปรับให้เหมาะสม (เช่น O (1)) สำหรับการแทรกและการลบ (และอันที่ดีมากจะสนับสนุนการทำงานพร้อมกันอย่างรวดเร็วด้วย แต่นั่นเป็นปัญหาที่แตกต่างกัน)
เควิน

6

มันไม่ยาก

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

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

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

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


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

@Dennis: เป็นไปได้ว่าต้นไม้ AVL ปฏิบัติตามข้อยกเว้นมากกว่ากฎ
outis

@outis IIRC แผนผังการค้นหาที่สมดุลทั้งหมดมีรูทีนการลบที่ซับซ้อนกว่า (มากกว่าการแทรก)
Raphael

สิ่งที่เกี่ยวกับตารางแฮชปิด ? การแทรกคือ (ค่อนข้าง) ตรงไปตรงมาการลบอย่างน้อยยากที่จะกำหนดแนวคิดเนื่องจากคุณต้องแก้ไขทั้งหมด "สิ่งที่ควรจะเป็นดัชนี X คือที่ดัชนี Y ในปัจจุบันและเราต้องไปหามันและนำกลับมา" ปัญหา
เควิน

3

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

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


2
"โครงสร้างข้อมูลส่วนใหญ่ไม่ต้องการการค้นหาสำหรับการแทรก" - เช่น ฉันต้องการอ้างสิทธิ์ตรงกันข้าม (คุณ "ค้นหา" ตำแหน่งแทรกซึ่งเป็นเพียงราคาแพงเป็นการหาองค์ประกอบเดียวกันอีกครั้งในภายหลัง.)
ราฟาเอล

@Raphael: คำตอบนี้ควรอ่านในบริบทของตารางการเชื่อมโยงการดำเนินงานที่ซับซ้อนซึ่งไม่รวมการดำเนินการค้นหาเป็นส่วนหนึ่งของการลบ ในการตอบคำถามของคุณฉันจัดโครงสร้างตามชื่อสามัญ ของอาร์เรย์รายการต้นไม้ตารางแฮชกองคิวกองและชุดต้นไม้และชุดต้องค้นหาการแทรก ส่วนอื่น ๆ ใช้ดัชนีที่ไม่ได้เชื่อมต่อกับรายการ (สำหรับสแต็คขั้นพื้นฐานคิวและฮีปมีเพียง 1 ดัชนีที่เปิดเผยและไม่สนับสนุนการค้นหา) หรือคำนวณจากรายการ กราฟสามารถไปได้ทั้งสองทางขึ้นอยู่กับการใช้งาน
outis

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

ฉันจะยอมรับว่าฉันมีพจนานุกรม / ตั้งค่าอินเทอร์เฟซในใจ (เหมือนกันใน CS) อย่างไรก็ตามตารางนั้นทำให้เข้าใจผิดและ (iirc) ผิดแม้ในหลายสถานที่ - Wikipedia, หลุมข้อมูลผิด CS : /
Raphael

0

ด้านบนของปัญหาที่กล่าวถึงทั้งหมดมีความเกี่ยวข้องของข้อมูลอ้างอิง สำหรับการสร้างโครงสร้างข้อมูลอย่างถูกต้องที่สุดเช่นฐานข้อมูลใน SQL นั้น Oracle Referential Integrity นั้นสำคัญมาก
เพื่อให้แน่ใจว่าคุณไม่ได้ตั้งใจทำลายสิ่งประดิษฐ์ต่าง ๆ มากมาย
ยกตัวอย่างเช่นการลบที่ไม่เพียง แต่ลบสิ่งที่คุณพยายามที่จะลบ แต่ยังก่อให้เกิดการล้างข้อมูลที่เกี่ยวข้อง
ล้างข้อมูลฐานข้อมูลจากข้อมูลขยะตลอดจนรักษาความสมบูรณ์ของข้อมูลที่ไม่เปลี่ยนแปลง
ตัวอย่างเช่นคุณมีตารางที่มีผู้ปกครองและประเภทเป็นบันทึกที่เกี่ยวข้องในตารางที่สอง
โดยที่ parent เป็นตารางหลัก หากคุณไม่ได้เสริมความสมบูรณ์ Referential ในสถานที่คุณสามารถลบระเบียนใด ๆ ในตารางใด ๆ และในภายหลังคุณจะไม่ทราบวิธีการรับข้อมูลครอบครัวเต็มเพราะคุณมีข้อมูลในตารางเด็กและไม่มีอะไรในตารางผู้ปกครอง
นั่นคือเหตุผลที่การตรวจสอบความสมบูรณ์ของการอ้างอิงจะไม่ยอมให้คุณลบบันทึกออกจากตารางหลักจนกว่าจะมีการล้างระเบียนจากตารางลูก
และนี่คือสาเหตุที่แหล่งข้อมูลส่วนใหญ่ยากที่จะลบข้อมูล


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