上篇文章我们讲了时间性能理论值彪悍,空间性能吃紧的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