การบีบอัดที่มีประสิทธิภาพของต้นไม้ที่ไม่มีชื่อ


20

พิจารณาต้นไม้ไบนารีแบบไม่มีป้ายกำกับ เราสามารถบีบอัดต้นไม้ดังกล่าวเมื่อมีการชี้ไปยัง subtreesและกับ (การตีความความเท่าเทียมกันของโครงสร้าง) เราเก็บ (WLOG)และแทนที่คำแนะนำทุกคนที่จะกับตัวชี้ไปยังTดูคำตอบของ uliสำหรับตัวอย่างT T = T = T T TTTT=T=TTT

ให้อัลกอริทึมที่ใช้ต้นไม้ในความหมายข้างต้นเป็นอินพุตและคำนวณจำนวนโหนดที่เหลือหลังจากการบีบอัด อัลกอริทึมควรใช้ในเวลาที่ (ในรูปแบบค่าใช้จ่ายในเครื่องแบบ) กับจำนวนโหนดในการป้อนข้อมูลnO(nlogn)n

นี่เป็นคำถามสอบและฉันไม่สามารถหาวิธีแก้ปัญหาที่ดีและฉันไม่ได้เห็น


และ "ต้นทุน", "เวลา" คืออะไร, การดำเนินงานเบื้องต้นที่นี่? จำนวนโหนดที่เยี่ยมชม? จำนวนของการข้ามขอบ? และขนาดของอินพุตที่ระบุเป็นอย่างไร?
uli

การบีบอัดต้นไม้ต้นนี้เป็นตัวอย่างของกัญชา Consing ไม่แน่ใจว่าจะนำไปสู่วิธีการนับทั่วไปหรือไม่
Gilles 'หยุดความชั่วร้าย'

@uli ฉันชี้แจงว่าคืออะไร ฉันคิดว่า "เวลา" มีความเฉพาะเจาะจงมากพอ ในการตั้งค่าที่ไม่พร้อมกันนี้เทียบเท่ากับการนับการดำเนินการซึ่งอยู่ในเงื่อนไขรถม้าสี่ล้อเทียบเท่ากับการนับการดำเนินงานเบื้องต้นที่เกิดขึ้นบ่อยที่สุด n
Raphael

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

@uli เหล่านี้เป็นรายละเอียดที่ "รูปแบบค่าใช้จ่ายสม่ำเสมอ" ควรจะนำเสนอ มันเจ็บปวดที่จะกำหนดอย่างชัดเจนว่าการดำเนินงานระดับใด แต่ใน 99.99% ของกรณี (รวมถึงอันนี้) ไม่มีความกำกวม คลาสที่ซับซ้อนโดยพื้นฐานไม่มีหน่วยพวกเขาไม่ได้วัดเวลาที่ใช้ในการดำเนินการหนึ่งอินสแตนซ์ แต่วิธีนี้เวลาแตกต่างกันไปตามอินพุทที่ใหญ่ขึ้น
Gilles 'หยุดความชั่วร้าย'

คำตอบ:


10

ใช่คุณสามารถทำการบีบอัดนี้ในเวลาแต่มันไม่ง่ายเลย :) เราทำการสังเกตก่อนแล้วจึงนำเสนออัลกอริทึม เราถือว่าทรีไม่ได้ถูกบีบอัดในตอนแรก - มันไม่จำเป็นจริงๆ แต่ทำให้การวิเคราะห์ง่ายขึ้นO(nlogn)

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

ตัวอย่างเช่นโหนดใบไม้สองโหนดใด ๆ มีโครงสร้างเทียบเท่ากันเนื่องจากมีโหนดต้นไม้เป็นลูกทั้งสองซึ่งมีโครงสร้างเทียบเท่ากัน

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

คำจำกัดความข้างต้นให้คำแนะนำวิธีการบีบอัดข้อมูลทันที: หากเราทราบความเท่าเทียมกันเชิงโครงสร้างของ subtrees ทั้งหมดที่มีความลึกไม่เกินดังนั้นเราสามารถคำนวณความสมดุลเชิงโครงสร้างของ subtrees ที่มีความลึกได้อย่างง่ายดาย เราไม่ต้องทำการคำนวณนี้ในทางที่สมาร์ทเพื่อหลีกเลี่ยงการเวลาในการทำงานd + 1 O ( n 2 )dd+1O(n2)

อัลกอริทึมจะกำหนดตัวระบุให้ทุกจุดสุดยอดในระหว่างการดำเนินการ ตัวระบุเป็นตัวเลขที่อยู่ในชุด\} ตัวระบุมีความเป็นเอกลักษณ์และไม่เคยเปลี่ยนแปลง: ดังนั้นเราถือว่าเราตั้งค่าตัวแปร (ทั่วโลก) เป็น 1 เมื่อเริ่มต้นอัลกอริทึมและทุกครั้งที่เรากำหนดตัวระบุให้กับจุดสุดยอดเรากำหนดค่าปัจจุบันของตัวแปรนั้นให้กับจุดสุดยอด ค่าของตัวแปรนั้น{1,2,3,,n}

อันดับแรกเราจะแปลงทรีอินพุทเป็นรายการ (ไม่เกิน ) ที่มีจุดยอดที่มีความลึกเท่ากันพร้อมกับตัวชี้ไปยังพาเรนต์ นี้จะกระทำได้อย่างง่ายดายในเวลาO ( n )nO(n)

ก่อนอื่นเราอัดใบทั้งหมด (เราสามารถหาใบเหล่านี้ในรายการที่มีจุดยอด 0 ความลึก) ลงในจุดยอดเดียว เรากำหนดตัวระบุจุดสุดยอดนี้ การบีบอัดของสองจุดยอดจะกระทำโดยการเปลี่ยนเส้นทางพาเรนต์ของจุดยอดทั้งสองไปที่จุดยอดอื่นแทน

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

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

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


ฉันยังไม่ได้อ่านคำตอบของคุณในรายละเอียด แต่ฉันคิดว่าคุณมีแฮ็ค consing ที่คิดค้นใหม่ได้มากขึ้นหรือน้อยลงพร้อมกับวิธีการค้นหาปัญหาแบบแปลก ๆ โดยเฉพาะในการค้นหาโหนด
Gilles 'หยุดความชั่วร้าย'

@Alex“ เด็กที่เล็กกว่าdegree” ดีกรีดีน่าจะเป็นdepthอย่างไร และแม้ว่าต้นไม้ CS จะเติบโตต่ำฉันก็พบว่า "ความสูงของต้นไม้" ทำให้เกิดความสับสนน้อยกว่า
uli

คำตอบที่ดี ฉันรู้สึกว่าควรมีวิธีการเรียงลำดับ ความคิดเห็นที่สองของฉันในคำตอบ @Gilles นั้นใช้ได้ที่นี่เช่นกัน
Raphael

@uli: ใช่คุณพูดถูกฉันได้แก้ไขแล้ว (ไม่แน่ใจว่าทำไมฉันจึงสับสนทั้งสองคำ) ความสูงและความลึกเป็นแนวคิดที่แตกต่างกันสองอย่างและฉันต้องการสิ่งต่อไปนี้ :) ฉันคิดว่าฉันยึดติดกับ 'ความลึก' แบบดั้งเดิมมากกว่าที่จะทำให้ทุกคนสับสนโดยการเปลี่ยนพวกเขา
อเล็กซ์สิบ Brink

4

การบีบอัดโครงสร้างข้อมูลที่ไม่แน่นอนเพื่อที่จะไม่ซ้ำกันใด ๆ subterm เท่ากับการก่อสร้างเป็นที่รู้จักกันกัญชา Consing นี่เป็นเทคนิคสำคัญในการจัดการหน่วยความจำในการเขียนโปรแกรมการทำงาน Hash consing เป็นระบบบันทึกความจำสำหรับโครงสร้างข้อมูล

เราจะแฮชข้อเสียของต้นไม้และนับจำนวนโหนดหลังจากแฮช consing Hash consing โครงสร้างข้อมูลขนาดสามารถทำได้ในO ( nnการดำเนินงาน การนับจำนวนโหนดที่ส่วนท้ายจะเป็นแบบเชิงเส้นในจำนวนโหนดO(nlg(n))

ฉันจะถือว่าต้นไม้มีโครงสร้างดังต่อไปนี้ (เขียนไว้ที่นี่ในไวยากรณ์ Haskell):

data Tree = Leaf
          | Node Tree Tree

nodes:T×TNTNT=N{}

เราสามารถใช้โครงสร้างข้อมูลแบบลอการิทึมเวลาnodesเช่นแผนผังการค้นหาแบบทวิภาคที่สมดุล ด้านล่างฉันจะเรียกlookup nodesการดำเนินการที่ค้นหาคีย์ในnodesโครงสร้างข้อมูลและinsert nodesการดำเนินการที่เพิ่มค่าภายใต้คีย์ใหม่และส่งคืนคีย์นั้น

ตอนนี้เราสำรวจต้นไม้และเพิ่มโหนดตามที่เราไป แม้ว่าฉันจะเขียนในรหัสเทียมเหมือน Haskell แต่ฉันจะถือว่าnodesเป็นตัวแปรที่ไม่แน่นอนในระดับโลก เราจะเพิ่มมันเข้าไป แต่การแทรกจะต้องถูกสอดเข้าไปตลอด addฟังก์ชั่น recurses บนต้นไม้เพิ่ม subtrees ไปยังnodesแผนที่และส่งกลับระบุของราก

insert (p1,p2) =
add Leaf = $\ell$
add (Node t1 t2) =
    let p1 = add t1
    let p2 = add t2
    case lookup nodes (p1,p2) of
      Nothing -> insert nodes (p1,p2)
      Just p -> p

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


nO(nlg(n))nodes

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

1
@Raphael Hash consing อาศัยโครงสร้างแผนที่ที่ จำกัด เหนือ tuples ของตัวชี้ / ตัวระบุคุณสามารถใช้สิ่งนั้นด้วยเวลาลอการิทึมสำหรับการค้นหาและเพิ่ม (เช่นด้วยแผนภูมิการค้นหาแบบไบนารีที่สมดุล) โซลูชันของฉันไม่ต้องการความไม่แน่นอน ฉันสร้างnodesตัวแปรที่ผันแปรได้เพื่อความสะดวก แต่คุณสามารถทำมันได้ตลอด ฉันจะไม่ให้รหัสเต็มซึ่งไม่เป็นเช่นนั้น
Gilles 'SO- หยุดความชั่วร้าย'

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

3

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

EN(l,r)lrN(E,E)

f(E)=0f(N(l,r))=2f(l)3f(r)

f

ข้อสันนิษฐานสุดท้ายนี้เป็นการยืดอายุเครื่องจักรจริง ในกรณีนี้เราต้องการใช้สิ่งที่คล้ายกับฟังก์ชั่นการจับคู่ของคันทอร์แทนการยกกำลัง

O(nlogn)O(nlogn)


1

ในฐานะที่เป็นรูปภาพไม่ได้รับอนุญาตในความคิดเห็น:

ป้อนคำอธิบายรูปภาพที่นี่

ด้านบนซ้าย: ต้นไม้อินพุต

ด้านบนขวา: ทรีย่อยที่ถูกรูตในโหนด 5 และ 7 นั้นก็มี isomorphic เช่นกัน

ด้านล่างซ้ายและขวา: ต้นไม้ที่ถูกบีบอัดไม่ได้กำหนดไว้โดยเฉพาะ

7+5|T|6+|T|


นี่เป็นตัวอย่างของการปฏิบัติการที่ต้องการขอบคุณ โปรดทราบว่าตัวอย่างสุดท้ายของคุณเหมือนกันถ้าคุณไม่แยกความแตกต่างระหว่างการอ้างอิงดั้งเดิมและที่เพิ่มเข้ามา
Raphael

-1

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

O(nlogn)T(n)=2T(n/2)+cn

def Comp(T):
   if T == null:
     return 0
   leftCount = Comp(T.left)
   rightCount = Comp(T.right)
   if leftCount == rightCount:
     if hasSameStructure(T.left, T.right):
       T.right = T.left
       return leftCount + 1
     else
       return leftCount + rightCount + 1    

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

nnr

T(n)=T(n1)+T(n2)+O(1) if nnr
2T(n/2)+O(n) otherwise

เกิดอะไรขึ้นถ้าทรีย่อยไม่ใช่พี่น้อง? การดูแล ((T1, T1), (T2, T1)) T1 สามารถบันทึกได้สองครั้งโดยใช้ตัวชี้สองเหตุการณ์ที่สาม
uli

TT

คำถามระบุว่ามีการระบุซับเซ็ตสองตัวเป็น isomorphic ไม่มีอะไรพูดถึงพวกเขาที่มีพ่อแม่คนเดียวกัน หากทรีย่อย T1 ปรากฏขึ้นสามครั้งในทรีเช่นเดียวกับในตัวอย่างก่อนหน้าของฉัน ((T1, T1), (T1, T2)) เหตุการณ์ที่สองสามารถถูกบีบอัดได้โดยการชี้ไปที่ orccurence ที่สาม
uli
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.