2010-09-08 30 views
8

Tôi lấy khái niệm đằng sau một số trie. Nhưng tôi hơi bối rối khi nói đến việc thực hiện.Điều gì sẽ là một cách hợp lý để thực hiện một Trie trong .NET?

Cách rõ ràng nhất tôi có thể nghĩ đến cấu trúc loại Trie sẽ là để có một Trie duy trì một nội bộ Dictionary<char, Trie>. Tôi có trong thực tế bằng văn bản một cách này, và nó hoạt động, nhưng ... điều này có vẻ như quá mức cần thiết. Ấn tượng của tôi là một bộ ba phải nhẹ và có một số Dictionary<char, Trie> riêng cho mỗi nút không có vẻ rất nhẹ đối với tôi.

Có cách nào thích hợp hơn để triển khai cấu trúc này mà tôi đang thiếu không?


CẬP NHẬT: OK! Dựa trên đầu vào rất hữu ích từ Jon và leppie, đây là những gì tôi đã đưa ra cho đến nay:

(1) Tôi có các loại Trie, trong đó có một tin _nodes viên của loại Trie.INodeCollection.

(2) Giao diện Trie.INodeCollection có các thành viên sau:

interface INodeCollection 
{ 
    bool TryGetNode(char key, out Trie node); 
    INodeCollection Add(char key, Trie node); 
    IEnumerable<Trie> GetNodes(); 
} 

(3) Có ba triển khai của giao diện này:

class SingleNode : INodeCollection 
{ 
    internal readonly char _key; 
    internal readonly Trie _trie; 

    public SingleNode(char key, Trie trie) 
    { /*...*/ } 

    // Add returns a SmallNodeCollection. 
} 

class SmallNodeCollection : INodeCollection 
{ 
    const int MaximumSize = 8; // ? 

    internal readonly List<KeyValuePair<char, Trie>> _nodes; 

    public SmallNodeCollection(SingleNode node, char key, Trie trie) 
    { /*...*/ } 

    // Add adds to the list and returns the current instance until MaximumSize, 
    // after which point it returns a LargeNodeCollection. 
} 

class LargeNodeCollection : INodeCollection 
{ 
    private readonly Dictionary<char, Trie> _nodes; 

    public LargeNodeCollection(SmallNodeCollection nodes, char key, Trie trie) 
    { /*...*/ } 

    // Add adds to the dictionary and returns the current instance. 
} 

(4) Khi một Trie được xây dựng đầu tiên, thành viên _nodes của mình là null. Cuộc gọi đầu tiên để Add tạo một SingleNode và các cuộc gọi tiếp theo tới Add sẽ chuyển từ đó, theo các bước được mô tả ở trên.

Điều này có hợp lý không? Điều này cảm thấy như một sự cải tiến theo nghĩa là nó hơi giảm "bulkiness" của một Trie (nút không còn full-blown Dictionary<char, Trie> đối tượng cho đến khi họ có đủ số lượng trẻ em). Tuy nhiên, nó cũng trở nên phức tạp hơn nhiều. Nó có quá phức tạp không? Tôi đã có một con đường phức tạp để đạt được một cái gì đó nên đã được đơn giản?

Trả lời

4

Vâng, bạn cần mỗi nút để có một cái gì đó mà hiệu quả thực hiện IDictionary<char, Trie>.Bạn có thể viết thực hiện tùy chỉnh của riêng bạn mà thay đổi cấu trúc bên trong của nó dựa trên bao nhiêu subnodes nó có:

  • Đối với một subnode duy nhất, sử dụng chỉ là một charTrie
  • Đối với một số nhỏ, sử dụng một List<Tuple<char, Trie>> hoặc một LinkedList<Tuple<char,Trie>>
  • Đối với một số lượng lớn, sử dụng một Dictionary<char, Trie>

(vừa nhìn thấy câu trả lời leppie, đây là loại phương pháp lai ông nói về, tôi tin.)

+0

Bạn cũng có thể nén đuôi, như trường hợp subnode đơn. – leppie

2

Có một vài cách, nhưng sử dụng danh sách liên kết đơn lẻ có lẽ là đơn giản nhất và nhẹ nhàng.

Tôi sẽ thực hiện một số kiểm tra để xem số lượng nút con mà mỗi nút có. Nếu không nhiều (nói 20 hoặc ít hơn), cách tiếp cận danh sách liên kết phải nhanh hơn một hashtable. Bạn cũng có thể làm một phương pháp lai tùy thuộc vào số lượng các nút con.

3

Thực hiện nó như là một từ điển, trong tâm trí của tôi, không phải là thực hiện một Trie - đó là thực hiện một từ điển từ điển.

Khi tôi đã thực hiện một Trie tôi đã thực hiện nó theo cách tương tự như đề nghị của Damien_The_Unbeliever (+1 có):

public class TrieNode 
{ 
    TrieNode[] Children = new TrieNode[no_of_chars]; 
} 

này lý tưởng đòi hỏi sau đó Trie của bạn sẽ chỉ hỗ trợ một tập hợp con hạn chế về các ký tự được chỉ định bởi no_of_chars và bạn có thể ánh xạ các ký tự nhập vào chỉ mục đầu ra. Ví dụ. nếu hỗ trợ AZ thì bạn tự nhiên sẽ lập bản đồ A đến 0 và từ Z đến 25.

Khi bạn sau đó cần thêm/xóa/kiểm tra sự tồn tại của một nút sau đó bạn làm điều gì đó như thế này:

public TrieNode GetNode(char c) 
{ 
    //mapping function - could be a lookup table, or simple arithmetic 
    int index = GetIndex(c); 
    //TODO: deal with the situation where 'c' is not supported by the map 
    return Children[index]; 
} 

Trong thực trường hợp tôi đã nhìn thấy điều này được tối ưu hóa để AddNode, ví dụ, sẽ có một ref TrieNode sao cho nút có thể được làm mới theo yêu cầu và tự động được đặt vào phụ huynh của TrieNode Children ở đúng vị trí.

Bạn cũng có thể sử dụng cây tìm kiếm thay vì phí trên bộ nhớ có thể khá điên rồ (đặc biệt nếu bạn định hỗ trợ tất cả 32k ký tự unicode!) Và hiệu suất TST khá ấn tượng (và cũng hỗ trợ tiền tố) & tìm kiếm ký tự đại diện cũng như tìm kiếm hamming). Tương tự, TST có thể hỗ trợ tất cả các ký tự unicode mà không cần phải thực hiện bất kỳ ánh xạ nào; kể từ khi họ làm việc trên một hoạt động lớn hơn/ít hơn/bằng thay vì một giá trị chỉ số tuyệt đối.

Tôi lấy mã số from here và điều chỉnh nó một chút (nó được viết trước khi Generics).

Tôi nghĩ bạn sẽ ngạc nhiên bởi TST; một khi tôi có một người thực hiện, tôi đã hoàn toàn rời khỏi Tries.

Điều khó khăn duy nhất là keeeping cân bằng TST; một vấn đề bạn không có với Tries.

+0

xin lỗi - Tôi đánh giá cao điều này không nhất thiết phải trả lời câu hỏi về cách triển khai - chỉ cung cấp giải pháp thay thế :) –

3

Nếu nhân vật của bạn là từ một tập hạn chế (ví dụ chỉ bảng chữ cái in hoa Latin), sau đó bạn có thể lưu trữ một mảng 26 phần tử, và mỗi tra cứu chỉ là

Trie next = store[c-'A'] 

trong đó c là nhân vật tra cứu hiện tại.

+0

các nút có mảng như cửa hàng là cách ưa thích của tôi để làm - không thể nghĩ ra cách nhẹ hơn làm điều đó –

+0

Tôi đang tìm một trường hợp tổng quát hơn.Điều đó nói rằng, tôi sẵn sàng chấp nhận rằng có lẽ một trie thực sự không thích hợp với cấu trúc dữ liệu "trường hợp chung", trong trường hợp đó, có thể chỉ có ý nghĩa trong các tình huống như thế này (nơi mà cấu trúc nút có thể được đơn giản hóa thành mảng đồng bằng). –

Các vấn đề liên quan