อนุญาตให้ใช้คีย์ที่ซ้ำกันในคำจำกัดความของแผนภูมิการค้นหาแบบไบนารีหรือไม่


139

ฉันพยายามค้นหาคำจำกัดความของแผนภูมิการค้นหาแบบไบนารีและฉันค้นหาคำจำกัดความที่แตกต่างกันทุกที่

บางคนบอกว่าสำหรับทรีย่อยที่กำหนดคีย์ลูกด้านซ้ายจะน้อยกว่าหรือเท่ากับรูท

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

และหนังสือโครงสร้างข้อมูลวิทยาลัยเก่าของฉันบอกว่า "ทุกองค์ประกอบมีคีย์และไม่มีสององค์ประกอบที่มีคีย์เดียวกัน"

มีคำจำกัดความสากลของ bst หรือไม่? โดยเฉพาะอย่างยิ่งเกี่ยวกับสิ่งที่จะทำกับต้นไม้ที่มีหลายอินสแตนซ์ของคีย์เดียวกัน

แก้ไข: บางทีฉันไม่ชัดเจนคำจำกัดความที่ฉันเห็นคือ

1) left <= root <right

2) ซ้าย <root <= ขวา

3) ซ้าย <root <ขวาซึ่งไม่มีคีย์ที่ซ้ำกันอยู่

คำตอบ:


78

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

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

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

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

ดังนั้นไปกับสิ่งที่หนังสือโครงสร้างข้อมูลของคุณพูดว่า!

แก้ไข:

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

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


1
อะไรคือข้อเสียของการใช้รายการที่โหนด?
Pacerier

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

50

หากทรีค้นหาแบบไบนารี่ของคุณเป็นต้นไม้สีดำสีแดงหรือคุณตั้งใจที่จะดำเนินการ "การหมุนแบบทรี" ใด ๆ โหนดที่ซ้ำกันจะทำให้เกิดปัญหา ลองนึกภาพกฎต้นไม้ของคุณคือ:

left <root <= right

ทีนี้ลองนึกภาพต้นไม้ที่เรียบง่ายซึ่งมีรากเป็น 5 ลูกซ้ายไม่มีศูนย์และลูกด้านขวาคือ 5 ถ้าคุณหมุนซ้ายไปที่รากคุณจะจบลงด้วย 5 ในลูกซ้ายและ 5 ในรากที่มีลูกขวา เป็นศูนย์ ทีนี้บางอย่างในทรีด้านซ้ายเท่ากับรูท แต่กฎของคุณเหนือ <รูทด้านซ้าย

ฉันใช้เวลาหลายชั่วโมงในการคิดออกว่าทำไมต้นไม้สีแดง / ดำของฉันจะออกไปตามลำดับเป็นครั้งคราวปัญหาคือสิ่งที่ฉันอธิบายไว้ข้างต้น หวังว่าใครบางคนจะอ่านสิ่งนี้และประหยัดเวลาในการดีบั๊กในอนาคต!


18
อย่าหมุนเมื่อคุณมีโหนดเท่ากัน! ข้ามไปที่ระดับถัดไปและหมุนตัวนั้น
รวย

2
การแก้ปัญหาอื่น ๆ ที่มีการเปลี่ยนแปลงอย่างใดอย่างหนึ่งกฎต้นไม้ที่จะleft <= node <= rightหรือเพียงใส่ก่อนที่จะเป็นครั้งแรกที่เกิดขึ้นของค่า
paxdiablo

ปัญหานี้ทำให้เกิดอะไรในทางปฏิบัติ ดูเหมือนว่าถ้าคุณโอเคกับ left <= node <= ใช่แล้วการดำเนินการทรีสีแดง - ดำทั้งหมดจะได้ผล
Björn Lindqvist

39

นิยามทั้งสามนั้นเป็นที่ยอมรับและถูกต้อง พวกเขากำหนดรูปแบบที่แตกต่างกันของ BST

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

แน่นอนว่าการอนุญาตการทำซ้ำเพิ่มความซับซ้อน หากคุณใช้คำนิยาม "left <= root <right" และคุณมีต้นไม้เช่น:

      3
    /   \
  2       4

จากนั้นการเพิ่มคีย์ที่ซ้ำกัน "3" ในทรีนี้จะส่งผลให้:

      3
    /   \
  2       4
    \
     3

โปรดทราบว่ารายการที่ซ้ำกันไม่ได้อยู่ในระดับที่ต่อเนื่องกัน

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

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

      3(1)
    /     \
  2(1)     4(1)

และหลังจากการแทรกคีย์ "3" ที่ซ้ำกันจะกลายเป็น:

      3(2)
    /     \
  2(1)     4(1)

วิธีนี้ช่วยให้การค้นหาการลบและการแทรกทำได้ง่ายขึ้นโดยมีค่าใช้จ่ายของไบต์พิเศษและการดำเนินการตัวนับ


ฉันประหลาดใจมากที่ไม่เคยเอ่ยถึงในตำราเรียนที่ฉันใช้ ศาสตราจารย์ไม่ได้พูดถึงมันเช่นกันหรือความจริงที่ว่ากุญแจซ้ำซ้อนยังเป็นปัญหา smh ...
Oloff Biermann

22

ใน BST ค่าทั้งหมดที่ลดลงทางด้านซ้ายของโหนดจะน้อยกว่า (หรือเท่ากับดูในภายหลัง) โหนดเอง ในทำนองเดียวกันค่าทั้งหมดลงมาทางด้านขวาของโหนดมากกว่า (หรือเท่ากับ) ค่าของโหนด(a) (ก)

BST บางตัวอาจเลือกที่จะอนุญาตให้มีค่าซ้ำกันดังนั้นตัวระบุ "หรือเท่ากับ" ด้านบน

ตัวอย่างต่อไปนี้อาจทำให้ชัดเจน:

            |
      +--- 14 ---+
      |          |
+--- 13    +--- 22 ---+
|          |          |
1         16    +--- 29 ---+
                |          |
               28         29

สิ่งนี้แสดง BST ที่อนุญาตการทำซ้ำคุณจะเห็นว่าการค้นหาค่าเริ่มต้นที่โหนดรูทและลงทรีย่อยทางซ้ายหรือขวาขึ้นอยู่กับว่าค่าการค้นหาของคุณน้อยกว่าหรือสูงกว่าค่าโหนด

สิ่งนี้สามารถเกิดขึ้นซ้ำ ๆ กับสิ่งที่ชอบ:

def hasVal (node, srchval):
    if node == NULL:
         return false
    if node.val == srchval:
        return true
    if node.val > srchval:
        return hasVal (node.left, srchval)
    return hasVal (node.right, srchval)

และเรียกมันด้วย:

foundIt = hasVal (rootNode, valToLookFor)

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


(a)คุณสามารถเรียงลำดับพวกเขาในทิศทางตรงกันข้ามได้หากคุณต้องการให้คุณปรับวิธีการค้นหาคีย์เฉพาะ BST ต้องการเพียงรักษาลำดับการเรียงบางอย่างไม่ว่าจะขึ้นหรือลงไม่เกี่ยวข้อง


สำหรับกรณีที่ซ้ำกันคุณสามารถตรวจสอบว่าเด็กที่ถูกต้องเหมือนกันกับโหนดปัจจุบันใน node.val == srchval: clause แล้วถ้าเป็นเช่นนั้นถูกต้องหรือไม่
bneil

9

ในหนังสือ "รู้เบื้องต้นเกี่ยวกับขั้นตอนวิธีการ" รุ่นที่สามโดย Cormen, Leiserson, Rivest และสไตน์ต้นไม้ค้นหาแบบทวิภาค (BST) ถูกกำหนดไว้อย่างชัดเจนว่าซ้ำกันช่วยให้ สามารถดูได้ในรูปที่ 12.1 และต่อไปนี้ (หน้า 287):

"กุญแจในต้นไม้ค้นหาแบบทวิภาคจะถูกเก็บไว้เสมอในลักษณะเช่นเพื่อตอบสนองคุณสมบัติไบนารีค้นหาต้นไม้: Let x. เป็นโหนดในต้นไม้ค้นหาแบบทวิภาคถ้าyเป็นโหนดในทรีย่อยทางซ้ายxแล้วy:key <= x:key. ถ้าyเป็น โหนดในแผนผังย่อยที่เหมาะสมของxจากนั้นy:key >= x:key"

นอกจากนี้จะมีการกำหนดทรีสีแดง - ดำในหน้า 308 เป็น:

"ต้นไม้สีแดงดำเป็นต้นไม้ค้นหาแบบไบนารีที่มีพื้นที่เก็บข้อมูลเพิ่มอีกหนึ่งบิตต่อโหนด: สีของมัน"

ดังนั้นต้นไม้สีแดงดำที่กำหนดไว้ในหนังสือเล่มนี้จึงสนับสนุนการทำซ้ำ


4

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


1
หากคุณมีชุดของข้อมูลที่มีคีย์ที่ซ้ำกันรายการเหล่านั้นควรจะถูกเก็บไว้ใน 1 โหนดบนต้นไม้ผ่านวิธีการอื่น (รายการที่เชื่อมโยง ฯลฯ ) ต้นไม้ควรมีคีย์ที่ไม่ซ้ำกันเท่านั้น
nickf

1
นอกจากนี้โปรดทราบจากวิกิว่าทรีย่อยด้านขวามีค่า "มากกว่าหรือเท่ากับ" รูท ดังนั้นนิยามของ wiki จึงขัดแย้งกับตนเอง
SoapBox

1
+1: คนต่างใช้นิยามที่ต่างกัน หากคุณใช้ BST ใหม่คุณต้องตรวจสอบให้แน่ชัดว่าคุณใช้สมมติฐานเกี่ยวกับรายการที่ซ้ำกัน
Mr Fooz

1
ดูเหมือนว่าฉันทามติคือ (ซ้าย <= ราก <= ขวา) เมื่ออนุญาตการทำซ้ำ แต่คำนิยามบางอย่างของ BST ไม่อนุญาตให้ใช้งานซ้ำ หรือบางคนอาจสอนวิธีนั้นเพื่อหลีกเลี่ยงความซับซ้อนเพิ่มเติม
Tim Merrifield

1
! ไม่ถูกต้อง มันทั้งซ้าย <= ราก <ขวาหรือซ้าย <ราก <= ขวาหรือซ้าย> ราก> = ขวาหรือซ้าย> = ราก> ขวา
ข้าวสาลีมิทช์

3

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

left <= root <= right

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


2

สามสิ่งที่คุณพูดนั้นเป็นจริงทั้งหมด

  • กุญแจเป็นเอกลักษณ์
  • ด้านซ้ายมีกุญแจน้อยกว่าอันนี้
  • ด้านขวาเป็นกุญแจที่มีขนาดใหญ่กว่ากุญแจนี้

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


1

1. ) left <= root <right

2. ) left <root <= right

3. ) ซ้าย <root <ขวาซึ่งไม่มีคีย์ที่ซ้ำกันอยู่

ฉันอาจต้องไปขุดหนังสืออัลกอริทึมของฉัน แต่ส่วนบนของหัว (3) เป็นรูปแบบบัญญัติ

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


คุณช่วยอธิบายได้ไหมว่าทำไมเหลือ <= รูท <= ถูกต้องไม่เหมาะ?
Helin Wang

ดูคำตอบที่ได้รับการยอมรับโดย @paxdiablo - คุณสามารถดูค่าที่ซ้ำกันได้ด้วย >=คุณสามารถดูค่าที่ซ้ำกันสามารถอยู่ด้วย อุดมคติขึ้นอยู่กับความต้องการของคุณ แต่ถ้าคุณมีค่าซ้ำซ้อนจำนวนมากและคุณอนุญาตให้สำเนาซ้ำมีอยู่ในโครงสร้าง bst ของคุณอาจกลายเป็นเส้นตรง - เช่น O (n)
Robert Paulson

1

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

หนึ่งสามารถสร้างรายการที่เชื่อมโยงสำหรับแต่ละโหนดของต้นไม้ที่มีคีย์ที่ซ้ำกันและเก็บข้อมูลในรายการ


1

ฉันแค่ต้องการเพิ่มข้อมูลเพิ่มเติมลงในสิ่งที่ @Robert Paulson ตอบ

สมมติว่าโหนดนั้นมีข้อมูล & ที่สำคัญ ดังนั้นโหนดที่มีรหัสเดียวกันอาจมีข้อมูลต่างกัน
(ดังนั้นการค้นหาจะต้องค้นหาโหนดทั้งหมดด้วยคีย์เดียวกัน)

1) left <= cur <ขวา

2) ซ้าย <cur <= ขวา

3) left <= cur <= ขวา

4) left <cur <right && cur มีโหนด siblingมีคีย์เดียวกัน

5) ซ้าย <cur <ขวาซึ่งไม่มีคีย์ที่ซ้ำกันอยู่

1) & 2) ทำงานได้ดีถ้าต้นไม้ไม่มีฟังก์ชั่นที่เกี่ยวข้องกับการหมุนเพื่อป้องกันความเบ้
แต่รูปแบบนี้ใช้ไม่ได้กับต้นไม้ AVLหรือต้นไม้สีแดงดำเนื่องจากการหมุนจะทำให้ตัวการสำคัญ
และแม้ว่าการค้นหา () จะค้นหาโหนดที่มีคีย์มันจะต้องข้ามไปที่โหนดใบสำหรับโหนดที่มีคีย์ซ้ำกัน
ทำให้เวลามีความซับซ้อนสำหรับการค้นหา = theta (logN)

3) จะทำงานได้ดีกับ BST ทุกรูปแบบพร้อมฟังก์ชั่นที่เกี่ยวข้องกับการหมุน
แต่การค้นหาจะใช้O (n)ทำลายวัตถุประสงค์ของการใช้ BST
สมมติว่าเรามีต้นไม้ดังต่อไปนี้โดยมี 3) ตัวการ

         12
       /    \
     10     20
    /  \    /
   9   11  12 
      /      \
    10       12

หากเราค้นหา (12)บนต้นไม้นี้แม้แต่ที่เราพบ 12 ที่รูทเราจะต้องค้นหาทั้งเด็กซ้ายและขวาเพื่อค้นหาคีย์ที่ซ้ำกัน
ใช้เวลา O (n) ตามที่ฉันบอก

4) ฉันชอบส่วนตัว สมมุติว่าพี่น้องหมายถึงโหนดที่มีคีย์เดียวกัน
เราสามารถเปลี่ยนต้นไม้ด้านบนเป็นด้านล่าง

         12 - 12 - 12
       /    \
10 - 10     20
    /  \    /
   9   11  12

ตอนนี้การค้นหาใด ๆ จะใช้ O (logN) เพราะเราไม่จำเป็นต้องข้ามลูกสำหรับคีย์ที่ซ้ำกัน
และเงินต้นนี้ยังทำงานได้ดีกับAVLหรือRB ต้นไม้


0

องค์ประกอบการจัดลำดับความสัมพันธ์ <= คือการสั่งซื้อทั้งหมดดังนั้นความสัมพันธ์ต้องสะท้อนกลับ แต่โดยทั่วไปแล้วแผนภูมิการค้นหาแบบไบนารี (aka BST) เป็นต้นไม้ที่ไม่มีการซ้ำซ้อน

มิฉะนั้นหากมีรายการซ้ำกันคุณต้องเรียกใช้ฟังก์ชันลบซ้ำสองครั้งหรือมากกว่า

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