2022-04-30 三向字符查找树--性能和空间平衡的最优解(C++)

上篇文章我们讲了时间性能理论值彪悍,空间性能吃紧的R向单词查找树。为了解决空间问题,我们进行妥协,建立一个类二叉树的字符串专有树,三向切分字符查找树。

当把字符串的每个字符当成二叉树的节点,等于本节点的下一个字符节点在中间节点,大于本节点的在右节点,小于本节点的在左节点,通常一般的单词不会很深,时间效率为O(1.39 log( N ) ),其中N为字符串数量,由于英文字母至多26个,不像单词本身可能成千上万,所以一般来讲会优于单词二叉树。

三向切分的最差效率与二叉树是一样的,就是输入有序的字符串,比如:abc,bcd,cde,会退化成链表。这是需要注意的。

代码如下:

// 下面我们用she sells sea shells by the shore
// 这几个键作为示例描述以下3个新方法。
// longestPrefixOf()接受一个字符串参数并返回符号表中该字符串的前缀中最长的键。
// 对于以上所有键,longestPrefixOf("shell")的结果是she,
// longestPrefixOf("shellsort")的结果是shells。
// keysWithPrefix()接受一个字符串参数并返回符号表中所有以该字符串作为前缀的键。
// 对于以上所有键,keysWithPrefix("she")的结果是she和shells
// keysWithPrefix("se")的结果是sells和sea。
// keysThatMatch()接受一个字符串参数并返回符号表中所有和该字符串匹配的键,
// 其中参数字符串中的点(“.”)可以匹配任何字符。
// 对于以上所有键,keysThatMatch(".he")的结果是she和the,
// keysThatMatch("s..")的结果是she和sea。

// template  struct StringST
// {
//     StringST();
//
//     //向表中插入键值对(如果值为null则删除键key)
//     void put(const std::string &key, V val);
//
//     //键key所对应的值(如果键不存在则返回null)
//     auto get(const std::string &key) const -> V;
//
//     //删除键key和它的值
//     void del(const std::string &key);
//
//     //表中是否包含key的值
//     auto contains(const std::string &key) const -> bool;
//
//     //符号表是否为空
//     auto isEmpty() const -> bool;
//
//     // str 的前缀中最长的键
//     auto longestPrefixOf(const std::string &str) const -> std::string;
//
//     //所有以 str 为前缀的键
//     auto keysWithPrefix(const std::string &str) const
//         -> std::vector;
//
//     //所有和 str 匹配的键(其中 "." 能够匹配任意字符
//     auto keysThatMatch(const std::string &str) const
//         -> std::vector;
//
//     //键值对的数量
//     auto size() const -> int;
//
//     //符号表中的所有键
//     auto keys() const -> std::vector;
// };

#ifndef STRST
#define STRST

#include 
#include 
#include 

namespace strST
{
// R单词查找树
template <typename V> struct TrieST;

// R单词查找树节点
template <typename V> struct Node
{
    Node() = default;
    ~Node()
    {
        for (int i = 0; i != R; ++i)
        {
            delete next[i];
        }
        delete[] next;
        delete val;
    }

  private:
    static const int R = 256;
    V *val = nullptr;
    Node **next = nullptr;
    friend struct TrieST<V>;
};

// R单词查找树
template <typename V> struct TrieST
{

    TrieST() = default;
    ~TrieST()
    {
        delete root;
        root = nullptr;
        cnt = 0;
    }

    // 向表中插入键值对(如果值为null则删除键key)
    void put(const std::string &key, const V &val)
    {
        root = put(root, key, val, 0);
    }

    // 键key所对应的值(如果键不存在则返回null)
    auto get(const std::string &key) const -> std::pair<bool, V>
    {
        Node<V> *nodePtr = get(root, key, 0);
        if (nodePtr != nullptr && nodePtr->val != nullptr)
        {
            return {true, *nodePtr->val};
        }
        return {false, V()};
    }

    // 删除键key和它的值
    void del(const std::string &key)
    {
        root = del(root, key, 0);
    }

    //只删除键对应的值
    void deljustval(const std::string &key)
    {
        Node<V> *nodePtr = get(root, key, 0);
        if (nodePtr != nullptr && nodePtr->val != nullptr)
        {
            delete nodePtr->val;
            nodePtr->val = nullptr;
            --cnt;
        }
    }

    // 表中是否包含key的值
    auto contains(const std::string &key) const -> bool
    {
        Node<V> *nodePtr = get(root, key, 0);
        return nodePtr != nullptr && nodePtr->val != nullptr;
    }

    // 符号表是否为空
    auto isEmpty() const -> bool
    {
        return cnt == 0;
    }

    // str 的前缀中最长的键
    auto longestPrefixOf(const std::string &str) const -> std::string
    {
        int length = search(root, str, 0, 0);
        return str.substr(0, length);
    }

    // 所有以 pre 为前缀的键
    auto keysWithPrefix(const std::string &pre) const
        -> std::vector<std::string>
    {
        std::vector<std::string> strVec;
        collect(get(root, pre, 0), pre, strVec);
        return strVec;
    }

    // 所有和 pat 匹配的键(其中 "." 能够匹配任意字符
    auto keysThatMatch(const std::string &pat) const -> std::vector<std::string>
    {
        std::vector<std::string> strVec;
        collect(root, "", pat, strVec);
        return strVec;
    }

    // 键值对的数量
    auto size() const -> int
    {
        return cnt;
    }

    // 符号表中的所有键
    auto keys() const -> std::vector<std::string>
    {
        return keysWithPrefix("");
    }

  private:
    // 递归获取 key 对应的 Node 指针
    auto get(Node<V> *nodePtr, const std::string &key, int chOrder) const
        -> Node<V> *
    {
        if (nodePtr == nullptr)
        {
            return nullptr;
        }
        if (chOrder == key.size())
        {
            return nodePtr;
        }
        unsigned char chr = key.at(chOrder);
        return get(nodePtr->next[chr], key, chOrder + 1);
    }

    // 递归将 val 放入 Node 指针中
    auto put(Node<V> *nodePtr, const std::string &key, const V &val,
             int chOrder) -> Node<V> *
    {
        if (nodePtr == nullptr)
        {
            nodePtr = new Node<V>();
            nodePtr->next = new Node<V> *[R]();
        }
        if (chOrder == key.size())
        {
            if (nodePtr->val == nullptr)
            {
                ++cnt;
                nodePtr->val = new V(val);
            }
            else
            {
                *nodePtr->val = val;
            }
            return nodePtr;
        }
        unsigned char chr = key.at(chOrder);
        nodePtr->next[chr] = put(nodePtr->next[chr], key, val, chOrder + 1);
        return nodePtr;
    }

    // 收集 pre 为前缀的键,加入 strVec
    void collect(Node<V> *nodePtr, const std::string &pre,
                 std::vector<std::string> &strVec) const
    {
        if (nodePtr == nullptr)
        {
            return;
        }
        if (nodePtr->val != nullptr)
        {
            strVec.push_back(pre);
        }
        for (int i = 0; i != R; ++i)
        {
            if (nodePtr->next[i])
            {
                collect(nodePtr->next[i], pre + static_cast<char>(i), strVec);
            }
        }
    }

    // 收集符合 pat 模式的键,加入 strVec
    void collect(Node<V> *nodePtr, const std::string &pre,
                 const std::string &pat, std::vector<std::string> &strVec) const
    {
        unsigned chOrder = pre.size();
        if (nodePtr == nullptr)
        {
            return;
        }
        if (chOrder == pat.size() && nodePtr->val != nullptr)
        {
            strVec.push_back(pre);
        }
        if (chOrder == pat.size())
        {
            return;
        }
        char ch = pat.at(chOrder);
        for (int i = 0; i != R; ++i)
        {
            if ((ch == '.' || ch == i) && (nodePtr->next[i]))
            {
                collect(nodePtr->next[i], pre + static_cast<char>(i), pat,
                        strVec);
            }
        }
    }

    //查找作为 str 前缀最长键的长度
    auto search(Node<V> *nodePtr, const std::string &str, int chOrder,
                int length) const -> int
    {
        if (nodePtr == nullptr)
        {
            return length;
        }
        if (nodePtr->val != nullptr)
        {
            length = chOrder;
        }
        if (chOrder == str.size())
        {
            return length;
        }
        unsigned char ch = str.at(chOrder);
        return search(nodePtr->next[ch], str, chOrder + 1, length);
    }

    //递归删除键和值
    auto del(Node<V> *nodePtr, const std::string &key, int chOrder) -> Node<V> *
    {
        if (nodePtr == nullptr)
        {
            return nullptr;
        }
        if (chOrder == key.size() && nodePtr->val)
        {
            delete nodePtr->val;
            nodePtr->val = nullptr;
            --cnt;
        }
        else
        {
            unsigned char c = key.at(chOrder);
            nodePtr->next[c] = del(nodePtr->next[c], key, chOrder + 1);
        }
        if (nodePtr->val != nullptr)
        {
            return nodePtr;
        }
        for (int i = 0; i != R; ++i)
        {
            if (nodePtr->next[i] != nullptr)
            {
                return nodePtr;
            }
        }
        delete nodePtr;
        return nullptr;
    }

    // 根节点
    Node<V> *root = nullptr;
    // 字母表大小
    static const int R = 256;
    // 含有键值对数量
    unsigned cnt = 0;
};

// 基于三向单词查找树符号表
template <typename V> struct TST;

// 三向字符查找树链表节点
template <typename V> struct NodeT
{
    NodeT() = default;
    ~NodeT()
    {
        delete lft;
        delete mid;
        delete rht;
        delete val;
    }
    friend struct TST<V>;

  private:
    unsigned char ch = 0;
    NodeT<V> *lft = nullptr;
    NodeT<V> *mid = nullptr;
    NodeT<V> *rht = nullptr;
    V *val = nullptr;
};

// 基于三向单词查找树符号表
template <typename V> struct TST
{

    TST() = default;
    ~TST()
    {
        delete root;
        root = nullptr;
        cnt = 0;
    }

    // 键key所对应的值(如果键不存在则返回null)
    auto get(const std::string &key) const -> std::pair<bool, V>
    {
        NodeT<V> *nodePtr = get(root, key, 0);
        if (nodePtr != nullptr && nodePtr->val != nullptr)
        {
            return {true, *nodePtr->val};
        }
        return {false, V()};
    }

    // 向表中插入键值对(如果值为null则删除键key)
    void put(const std::string &key, const V &val)
    {
        root = put(root, key, val, 0);
    }

    //删除键key和它的值
    void del(const std::string &key)
    {
        if (key.empty())
        {
            return;
        }
        root = del(root, key, 1);
    }

    //表中是否包含key的值
    auto contains(const std::string &key) const -> bool
    {
        NodeT<V> *nodePtr = get(root, key, 0);
        return static_cast<bool>(nodePtr != nullptr && nodePtr->val != nullptr);
    }

    //符号表是否为空
    auto isEmpty() const -> bool
    {
        return cnt == 0;
    }

    // str 的前缀中最长的键
    auto longestPrefixOf(const std::string &str) const -> std::string
    {
        int length = search(root, str, 0, 0);
        return str.substr(0, length);
    }

    // 所有以 pre 为前缀的键
    auto keysWithPrefix(const std::string &pre) const
        -> std::vector<std::string>
    {
        std::vector<std::string> strVec;
        if (pre.empty())
        {
            collect(root, pre, strVec);
            return strVec;
        }
        NodeT<V> *nodePtr = get(root, pre, 0);
        if (nodePtr == nullptr)
        {
            return strVec;
        }
        if (nodePtr->val != nullptr)
        {
            strVec.push_back(pre);
        }
        collect(nodePtr->mid, pre, strVec);
        return strVec;
    }

    //所有和 str 匹配的键(其中 "." 能够匹配任意字符
    auto keysThatMatch(const std::string &pat) const -> std::vector<std::string>
    {
        std::vector<std::string> strVec;
        collect(root, "", pat, strVec);
        return strVec;
    }

    //键值对的数量
    auto size() const -> int
    {
        return cnt;
    }

    //符号表中的所有键
    auto keys() const -> std::vector<std::string>
    {
        std::vector<std::string> strVec;
        collect(root, "", strVec);
        return strVec;
    }

  private:
    // 递归获取 key 对应的 NodeT 指针
    auto get(NodeT<V> *nodePtr, const std::string &key, int chOrder) const
        -> NodeT<V> *
    {
        if (nodePtr == nullptr)
        {
            return nullptr;
        }
        unsigned char chr = key.at(chOrder);
        if (chr < nodePtr->ch)
        {
            return get(nodePtr->lft, key, chOrder);
        }
        if (chr > nodePtr->ch)
        {
            return get(nodePtr->rht, key, chOrder);
        }
        if (chOrder < key.size() - 1)
        {
            return get(nodePtr->mid, key, chOrder + 1);
        }
        return nodePtr;
    }

    auto put(NodeT<V> *NodeTptr, const std::string &key, const V &val,
             int chOrder) -> NodeT<V> *
    {
        unsigned char c = key.at(chOrder);
        if (NodeTptr == nullptr)
        {
            NodeTptr = new NodeT<V>();
            NodeTptr->ch = c;
        }
        if (c < NodeTptr->ch)
        {
            NodeTptr->lft = put(NodeTptr->lft, key, val, chOrder);
        }
        else if (c > NodeTptr->ch)
        {
            NodeTptr->rht = put(NodeTptr->rht, key, val, chOrder);
        }
        else if (chOrder < key.size() - 1)
        {
            NodeTptr->mid = put(NodeTptr->mid, key, val, chOrder + 1);
        }
        else
        {
            if (NodeTptr->val == nullptr)
            {
                ++cnt;
                NodeTptr->val = new V(val);
            }
            else
            {
                *NodeTptr->val = val;
            }
        }
        return NodeTptr;
    }

    auto search(NodeT<V> *nodePtr, const std::string &str, int chOrder,
                int length) const -> int
    {
        if (nodePtr == nullptr)
        {
            return length;
        }
        if (nodePtr->val != nullptr)
        {
            length = chOrder;
        }
        if (chOrder == str.size())
        {
            return length;
        }
        unsigned char ch = str.at(chOrder);
        if (ch > nodePtr->ch)
        {
            return search(nodePtr->rht, str, chOrder + 1, length);
        }
        if (ch < nodePtr->ch)
        {
            return search(nodePtr->lft, str, chOrder + 1, length);
        }
        return search(nodePtr->mid, str, chOrder + 1, length);
    }

    // 收集 pre 为前缀的键,加入 strVec
    void collect(NodeT<V> *nodePtr, const std::string &pre,
                 std::vector<std::string> &strVec) const
    {
        if (nodePtr == nullptr)
        {
            return;
        }
        if (nodePtr->val != nullptr)
        {
            strVec.push_back(pre + static_cast<char>(nodePtr->ch));
        }
        if (nodePtr->lft)
        {
            collect(nodePtr->lft, pre, strVec);
        }
        if (nodePtr->mid)
        {
            collect(nodePtr->mid, pre + static_cast<char>(nodePtr->ch), strVec);
        }
        if (nodePtr->rht)
        {
            collect(nodePtr->rht, pre, strVec);
        }
    }

    // 收集符合 pat 模式的键,加入 strVec
    void collect(NodeT<V> *nodePtr, const std::string &pre,
                 const std::string &pat, std::vector<std::string> &strVec) const
    {
        if (nodePtr == nullptr)
        {
            return;
        }
        const std::string preStr = pre + static_cast<char>(nodePtr->ch);
        unsigned chOrder = preStr.size();
        if (chOrder == pat.size() && nodePtr->val != nullptr &&
            (pat.at(chOrder - 1) == '.' ||
             pat.at(chOrder - 1) == static_cast<char>(nodePtr->ch)))
        {
            strVec.push_back(preStr);
        }
        if (chOrder > pat.size())
        {
            return;
        }
        char ch = pat.at(chOrder - 1);
        if ((ch == '.' || static_cast<char>(ch) < nodePtr->ch) &&
            (nodePtr->lft))
        {
            collect(nodePtr->lft, pre, pat, strVec);
        }
        if ((ch == '.' || static_cast<char>(ch) > nodePtr->ch) &&
            (nodePtr->rht))
        {
            collect(nodePtr->rht, pre, pat, strVec);
        }
        if ((ch == '.' || static_cast<unsigned char>(ch) == nodePtr->ch) &&
            (nodePtr->mid))
        {
            collect(nodePtr->mid, preStr, pat, strVec);
        }
    }

    //递归删除键和值
    auto del(NodeT<V> *nodePtr, const std::string &key, int chOrder)
        -> NodeT<V> *
    {
        if (nodePtr == nullptr)
        {
            return nullptr;
        }
        if (chOrder == key.size() && nodePtr->val &&
            key.back() == static_cast<char>(nodePtr->ch))
        {
            delete nodePtr->val;
            nodePtr->val = nullptr;
            --cnt;
        }
        else
        {
            unsigned char c = key.at(chOrder - 1);
            if (c < nodePtr->ch)
            {
                nodePtr->lft = del(nodePtr->lft, key, chOrder);
            }
            if (c == nodePtr->ch)
            {
                nodePtr->mid = del(nodePtr->mid, key, chOrder + 1);
            }
            if (c > nodePtr->ch)
            {
                nodePtr->rht = del(nodePtr->rht, key, chOrder);
            }
        }
        if (nodePtr->val != nullptr)
        {
            return nodePtr;
        }
        if (nodePtr->lft != nullptr || nodePtr->mid != nullptr ||
            nodePtr->rht != nullptr)
        {
            return nodePtr;
        }
        delete nodePtr;
        return nullptr;
    }

    NodeT<V> *root = nullptr;
    unsigned cnt = 0;
};

} // namespace strST

#endif

你可能感兴趣的:(笔记,c++,数据结构,单词查找树)