📅  最后修改于: 2023-12-03 15:37:34.786000             🧑  作者: Mango
在计算机科学中,Trie,又称字典树,是一种树形数据结构,用于高效地存储和检索关联数组。本文介绍如何在C++中使用智能指针和面向对象编程(OOP)来实现Trie数据结构。
Trie数据结构(字典树)是一种树形结构,用于存储关联数组。Trie的每个节点表示一个字符串的字符,每个节点都有0或多个指向子节点的链接。通常,Trie的根节点表示空字符串。除根节点外,每个节点都表示一个非空字符串,从根节点到该节点的路径表示该字符串。每个节点还可以存储与其相关联的值(如出现次数),或者将值存储在它所代表的字符串中。
下面是一个Trie的示例:
root
/ | \
c t f
/ | \
a o a
/ | \
t p t
在上面的示例中,Trie存储了字符串 "cat"、"cot"、"coa"、"fat"、"fot" 和 "foa"。
C++中的智能指针是一种RAII(资源获取即初始化)机制,用于自动管理动态分配的内存。简单来说,智能指针允许程序员使用类似指针的语法来操作内存,而不用手动调用new和delete操作符。
C++中有三种常见的智能指针类型:unique_ptr、shared_ptr和weak_ptr。在本文中,我们将使用unique_ptr来管理Trie节点的内存。
unique_ptr是一种独占的智能指针,即同一时刻只能有一个unique_ptr指向一个对象。当unique_ptr被销毁时,它所指向的对象也会被自动销毁。
下面是unique_ptr的一个示例:
#include <memory>
int main() {
std::unique_ptr<int> ptr(new int(42));
std::cout << *ptr << std::endl; // 输出 42
return 0;
} // 此时,unique_ptr自动销毁,它所指向的int对象也会被销毁
我们可以通过一个TrieNode类来表示Trie中的每个节点。每个节点包含一个值(如出现次数)、0或多个子节点和一个单独的标志位,该标志位指示该节点是否存在一个与之关联的值。
下面是一个TrieNode类的简单实现:
class TrieNode {
public:
TrieNode(char c) : m_data(c), m_isEnd(false) {}
// 插入一个新的节点
TrieNode* insert(char c) {
m_children[c] = std::make_unique<TrieNode>(c);
return m_children[c].get();
}
// 获取某个子节点
TrieNode* get(char c) const {
const auto iter = m_children.find(c);
return iter == m_children.cend() ? nullptr : iter->second.get();
}
// 获取所有子节点
std::vector<TrieNode*> getAll() const {
std::vector<TrieNode*> res;
for (auto& child : m_children) {
res.push_back(child.second.get());
}
return res;
}
// 是否存在与之关联的值
bool isEnd() const { return m_isEnd; }
// 设置与之关联的值
void setEnd() { m_isEnd = true; }
private:
char m_data; // 节点代表的字符
bool m_isEnd; // 是否存在与之关联的值
std::unordered_map<char, std::unique_ptr<TrieNode>> m_children; // 子节点
};
在上面的代码中,我们使用std::unordered_map存储子节点,并使用unique_ptr来管理子节点的内存。当某个子节点不再需要时,它会自动被析构。
接下来,我们可以通过一个Trie类来包装TrieNode,以便在外部更方便地使用Trie数据结构。
下面是Trie类的简单实现:
class Trie {
public:
Trie() : m_root(std::make_unique<TrieNode>(' ')) {}
// 插入一个新的字符串
void insert(const std::string& word) {
auto node = m_root.get();
for (const char c : word) {
if (!node->get(c)) {
node = node->insert(c);
} else {
node = node->get(c);
}
}
node->setEnd();
}
// 查找某个字符串是否存在
bool search(const std::string& word) const {
const auto node = find(word);
return node && node->isEnd();
}
// 查找某个前缀是否存在
bool startsWith(const std::string& prefix) const {
return find(prefix) != nullptr;
}
private:
// 查找某个字符串
TrieNode* find(const std::string& word) const {
auto node = m_root.get();
for (const char c : word) {
if (!node->get(c)) {
return nullptr;
} else {
node = node->get(c);
}
}
return node;
}
private:
std::unique_ptr<TrieNode> m_root; // Trie树的根节点
};
在上面的代码中,我们定义了insert、search和startsWith三个用于操作Trie数据结构的方法。对于每个方法,我们都可以使用unique_ptr来自动管理内存。
本文介绍了如何在C++中使用智能指针和面向对象编程(OOP)来实现Trie数据结构。通过使用unique_ptr,我们可以简化内存管理并减少内存泄漏的风险,在编写大型程序时非常有用。