Hiệu quả nén cây không nhãn


20

Xem xét cây nhị phân không có nhãn, gốc. Chúng ta có thể nén cây như: bất cứ khi nào có con trỏ để subtrees và với (giải thích như bình đẳng cấu trúc), chúng tôi lưu trữ (wlog) và thay thế tất cả các con trỏ tới với con trỏ đến . Xem câu trả lời của uli cho một ví dụ.T ' T = T ' = T T ' TTTT=T=TTT

Đưa ra một thuật toán lấy một cây theo nghĩa trên làm đầu vào và tính toán số lượng nút (tối thiểu) còn lại sau khi nén. Thuật toán sẽ chạy trong thời gian (trong mô hình chi phí thống nhất) với số lượng nút trong đầu vào.nO(nlogn)n

Đây là một câu hỏi thi và tôi không thể đưa ra một giải pháp hay, tôi cũng không thấy.


Và những gì là chi phí, một thời gian, một hoạt động cơ bản ở đây là gì? Số lượng nút truy cập? Số lượng các cạnh đi qua? Và kích thước của đầu vào được chỉ định như thế nào?
uli

Nén cây này là một ví dụ của hàm băm . Không chắc chắn nếu điều đó dẫn đến một phương pháp đếm chung.
Gilles 'SO- ngừng trở thành ác quỷ'

@uli Tôi đã làm rõ là gì . Tôi nghĩ rằng "thời gian" là đủ cụ thể, mặc dù. Trong cài đặt không đồng thời, điều này tương đương với các hoạt động đếm theo thuật ngữ Landau tương đương với việc đếm hoạt động cơ bản xảy ra thường xuyên nhất. n
Raphael

@Raphael Tất nhiên tôi có thể đoán xem hoạt động cơ bản dự định sẽ là gì và có thể sẽ chọn giống như mọi người khác. Nhưng, và tôi biết rằng tôi là người phạm tội ở đây, bất cứ khi nào giới hạn thời gian của Giới tính được đưa ra, điều quan trọng là phải nêu rõ những gì đang được tính. Là nó hoán đổi, so sánh, bổ sung, truy cập bộ nhớ, các nút được kiểm tra, các cạnh đi qua, bạn đặt tên cho nó. Nó giống như bỏ qua đơn vị đo lường trong vật lý. Là hay ? Và tôi cho rằng truy cập bộ nhớ hầu như luôn luôn là hoạt động thường xuyên nhất. 1010kg10ms
uli

@uli Đây là những loại chi tiết mà mô hình chi phí thống nhất của Cameron được cho là truyền đạt. Thật khó để xác định chính xác các hoạt động là cơ bản, nhưng trong 99,99% trường hợp (bao gồm cả hoạt động này) không có sự mơ hồ. Các lớp phức tạp về cơ bản không có đơn vị, chúng không đo thời gian cần thiết để thực hiện một thể hiện nhưng cách thời gian này thay đổi khi đầu vào trở nên lớn hơn.
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


10

Có, bạn có thể thực hiện nén này trong thời gian , nhưng không dễ dàng :) Trước tiên chúng tôi thực hiện một số quan sát và sau đó trình bày thuật toán. Chúng tôi cho rằng cây ban đầu không bị nén - điều này không thực sự cần thiết nhưng giúp phân tích dễ dàng hơn.O(nlogn)

Đầu tiên, chúng tôi mô tả "sự bình đẳng về cấu trúc" theo quy nạp. Đặt và là hai (phụ) cây. Nếu và đều là cây null (hoàn toàn không có đỉnh), thì chúng tương đương về mặt cấu trúc. Nếu và đều không phải là cây rỗng, thì chúng tương đương về mặt cấu trúc nếu con trái của chúng có cấu trúc tương đương và con phải của chúng có cấu trúc tương đương. "Tương đương cấu trúc" là điểm cố định tối thiểu so với các định nghĩa này.T ' T T ' T T 'TTTTTT

Ví dụ, bất kỳ hai nút lá nào đều có cấu trúc tương đương nhau, vì cả hai đều có các cây null là cả hai con của chúng, tương đương về mặt cấu trúc.

Vì thật khó chịu khi nói 'những đứa con bên trái của chúng có cấu trúc tương đương nhau và những đứa con bên phải của chúng cũng vậy', chúng tôi thường sẽ nói 'những đứa trẻ của chúng có cấu trúc tương đương' và có cùng ý định. Cũng lưu ý đôi khi chúng ta nói 'đỉnh này' khi chúng ta muốn nói 'cây con bắt nguồn từ đỉnh này'.

Định nghĩa trên ngay lập tức cho chúng ta một gợi ý về cách thực hiện nén: nếu chúng ta biết tương đương cấu trúc của tất cả các cây con có độ sâu tối đa , thì chúng ta có thể dễ dàng tính toán tương đương cấu trúc của các cây con có độ sâu . Chúng ta phải thực hiện tính toán này một cách thông minh để tránh thời gian chạy .d + 1 O ( n 2 )dd+1O(n2)

Thuật toán sẽ gán các định danh cho mọi đỉnh trong quá trình thực thi. Mã định danh là một số trong tập . Mã định danh là duy nhất và không bao giờ thay đổi: do đó, chúng tôi giả sử chúng tôi đặt một số biến (toàn cầu) thành 1 khi bắt đầu thuật toán và mỗi khi chúng tôi gán một mã định danh cho một số đỉnh, chúng tôi gán giá trị hiện tại của biến đó cho đỉnh và gia tăng giá trị của biến đó.{1,2,3,,n}

Đầu tiên chúng ta chuyển đổi cây đầu vào thành (nhiều nhất ) danh sách chứa các đỉnh có độ sâu bằng nhau, cùng với một con trỏ tới cha mẹ của chúng. Điều này được thực hiện dễ dàng trong thời gian O ( n ) .nO(n)

Đầu tiên chúng ta nén tất cả các lá (chúng ta có thể tìm thấy các lá này trong danh sách với các đỉnh có độ sâu 0) thành một đỉnh duy nhất. Chúng tôi gán đỉnh này một định danh. Nén hai đỉnh được thực hiện bằng cách chuyển hướng cha mẹ của một trong hai đỉnh để trỏ đến đỉnh khác thay thế.

Chúng tôi thực hiện hai quan sát: thứ nhất, bất kỳ đỉnh nào cũng có con có độ sâu hoàn toàn nhỏ hơn và thứ hai, nếu chúng tôi thực hiện nén trên tất cả các đỉnh có độ sâu nhỏ hơn (và đã cho chúng định danh), thì hai đỉnh có độ sâu d tương đương về mặt cấu trúc và có thể được nén nếu các định danh của con cái họ trùng khớp. Quan sát cuối cùng này xuất phát từ lập luận sau: hai đỉnh tương đương về mặt cấu trúc nếu con cái của chúng tương đương về mặt cấu trúc và sau khi nén, điều này có nghĩa là con trỏ của chúng chỉ vào cùng một con, điều đó có nghĩa là các định danh của con cái chúng bằng nhau.dd

Chúng tôi lặp qua tất cả các danh sách với các nút có độ sâu bằng nhau từ độ sâu nhỏ đến độ sâu lớn. Đối với mỗi cấp độ, chúng tôi tạo một danh sách các cặp số nguyên, trong đó mỗi cặp tương ứng với các định danh của con của một số đỉnh ở cấp đó. Chúng ta có hai đỉnh ở mức đó tương đương về mặt cấu trúc nếu các cặp số nguyên tương ứng của chúng bằng nhau. Sử dụng thứ tự từ điển, chúng ta có thể sắp xếp chúng và thu được các cặp số nguyên bằng nhau. Chúng tôi nén các bộ này thành các đỉnh đơn như trên và cung cấp cho chúng định danh.

O(n)nO(nlogn)


Tôi chưa đọc câu trả lời của bạn một cách chi tiết, nhưng tôi nghĩ rằng bạn ít nhiều đã phát minh lại việc sử dụng hàm băm, với một cách tìm kiếm các nút cụ thể.
Gilles 'SO- đừng trở nên xấu xa'

@Alex Những đứa trẻ có bằng cấp nghiêm khắc nhỏ hơn degreecó lẽ nên depth? Và mặc dù các cây CS mọc xuống phía dưới, tôi thấy chiều cao của cây Một ít khó hiểu hơn chiều sâu của cây.
uli

Câu trả lời tốt đẹp. Tôi cảm thấy nên có một cách để sắp xếp xung quanh. Nhận xét thứ hai của tôi về câu trả lời @Gilles cũng hợp lệ ở đây.
Raphael

@uli: yup, bạn nói đúng, tôi đã sửa nó (không hiểu tại sao tôi lại nhầm lẫn hai từ đó). Chiều cao và chiều sâu là hai khái niệm khác nhau một cách tinh tế và tôi cần cái sau :) Tôi nghĩ rằng tôi sẽ gắn bó với 'độ sâu' thông thường hơn là nhầm lẫn mọi người bằng cách hoán đổi chúng.
Alex ten Brink

4

Nén một cấu trúc dữ liệu không thể thay đổi để nó không trùng lặp bất kỳ tập hợp con có cấu trúc bằng nhau nào được gọi là hàm băm . Đây là một kỹ thuật quan trọng trong quản lý bộ nhớ trong lập trình chức năng. Hash consing là một loại ghi nhớ có hệ thống cho các cấu trúc dữ liệu.

nO(nlg(n))

Tôi sẽ xem xét cây như có cấu trúc sau (được viết ở đây theo cú pháp Haskell):

data Tree = Leaf
          | Node Tree Tree

nodes:T×TNTNT=N{}

Chúng ta có thể sử dụng cấu trúc dữ liệu thời gian logarit nodes, chẳng hạn như cây tìm kiếm nhị phân cân bằng. Dưới đây tôi sẽ gọi lookup nodesthao tác tìm kiếm một khóa trong nodescấu trúc dữ liệu và insert nodesthao tác thêm một giá trị dưới một khóa mới và trả về khóa đó.

Bây giờ chúng ta đi qua cây và thêm các nút khi chúng ta đi cùng. Mặc dù tôi đang viết bằng mã giả giống Haskell, tôi sẽ coi nodesnhư một biến có thể thay đổi toàn cầu; chúng ta sẽ chỉ thêm vào nó, nhưng các phần chèn thêm cần được xâu chuỗi xuyên suốt. Các addchức năng recurses trên một cây, thêm subtrees của mình cho các nodesbản đồ, và trả về nhận dạng của rễ.

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

Số lượng insertcuộc gọi, cũng là kích thước cuối cùng của nodescấu trúc dữ liệu, là số lượng nút sau khi nén tối đa. (Thêm một cho cây trống nếu cần.)


nO(nlg(n))nodes

Tôi chỉ xem xét các cấu trúc băm cho các số theo cách có cấu trúc để tính toán băm độc lập cho cùng một cây sẽ luôn mang lại kết quả tương tự. Giải pháp của bạn cũng tốt, miễn là chúng tôi có các cơ sở dữ liệu có thể thay đổi trên tay. Tôi nghĩ rằng nó có thể được làm sạch một chút, mặc dù; sự xen kẽ của insertaddnên được làm rõ ràng và một chức năng thực sự giải quyết vấn đề nên được đưa ra, imho.
Raphael

1
@Raphael Hash consing dựa trên cấu trúc bản đồ hữu hạn qua các bộ con trỏ / định danh, bạn có thể thực hiện điều đó với thời gian logarit để tra cứu và thêm (ví dụ với cây tìm kiếm nhị phân cân bằng). Giải pháp của tôi không yêu cầu khả năng biến đổi; Tôi tạo nodesmột biến có thể thay đổi để thuận tiện, nhưng bạn có thể xâu chuỗi nó xuyên suốt. Tôi sẽ không cung cấp mã đầy đủ, đây không phải là SO.
Gilles 'SO- đừng trở nên xấu xa'

1
Các cấu trúc băm @Raphael , trái ngược với việc gán cho chúng các số tùy ý, là một chút tinh ranh. Trong mô hình chi phí thống nhất, bạn có thể mã hóa bất cứ thứ gì thành một số nguyên lớn và thực hiện các hoạt động thời gian không đổi trên nó, điều này không thực tế. Trong thế giới thực, bạn có thể sử dụng băm mật mã để có ánh xạ một-một trên thực tế từ các tập hợp vô hạn đến một phạm vi số nguyên hữu hạn, nhưng chúng chậm. Nếu bạn sử dụng tổng kiểm tra không phải là tiền điện tử làm hàm băm, bạn cần suy nghĩ về các va chạm.
Gilles 'SO- đừng trở nên xấu xa'

3

Đây là một ý tưởng khác nhằm mục đích (mã hóa) mã hóa cấu trúc của cây thành số, thay vì chỉ dán nhãn tùy ý. Vì vậy, chúng tôi sử dụng hệ số nguyên tố của bất kỳ số nào là duy nhất.

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

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

f

Giả định cuối cùng này là một sự kéo dài trên các máy thật; trong trường hợp này, người ta thích sử dụng một cái gì đó tương tự như chức năng ghép nối của Cantor thay vì lũy thừa.

O(nlogn)O(nlogn)


1

Như hình ảnh không được phép trong ý kiến:

nhập mô tả hình ảnh ở đây

trên cùng bên trái: một cây đầu vào

trên cùng bên phải: các cây con bắt nguồn từ các nút 5 và 7 cũng là đẳng cấu.

phía dưới bên trái và bên phải: các cây nén không được xác định duy nhất.

7+5|T|6+|T|


Đây thực sự là một ví dụ về hoạt động mong muốn, cảm ơn. Lưu ý rằng các ví dụ cuối cùng của bạn giống hệt nhau nếu bạn không phân biệt giữa các tài liệu tham khảo gốc và bổ sung.
Raphael

-1

Chỉnh sửa: Tôi đọc câu hỏi vì T và T là con của cùng cha mẹ. Tôi cũng đã định nghĩa nén là đệ quy, nghĩa là bạn có thể nén hai cây con đã nén trước đó. Nếu đó không phải là câu hỏi thực tế, thì câu trả lời của tôi có thể không hoạt động.

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    

Trường hợp hasSameStructure()một hàm so sánh hai cây con đã được nén trong thời gian tuyến tính để xem liệu chúng có cùng cấu trúc không. Viết một hàm đệ quy thời gian tuyến tính đi ngang qua nhau và kiểm tra xem một cây con có con trái mỗi khi con kia không, v.v. không nên khó.

nnr

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

Nếu cây con không phải là anh em ruột thì sao? Chăm sóc cho ((T1, T1), (T2, T1)) T1 có thể được lưu hai lần bằng cách sử dụng một con trỏ hai lần xuất hiện thứ ba.
uli

TT

Các câu hỏi công bằng nói rằng hai phần phụ được xác định là đẳng cấu. Không có gì được nói về họ có cùng cha mẹ. Nếu một cây con T1 xuất hiện ba lần trong một cây, như trong ví dụ trước của tôi ((T1, T1), (T1, T2)), hai lần xuất hiện có thể được nén bằng cách chỉ vào orccurence thứ ba.
uli
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.