预备知识
智能指针
std::unqiue_ptr std::shared_ptr
类型转化
const_cast dynamic_cast
task1
task2
task3
task4
task1
在此任务中,您将需要在trie.h和trie.cpp两个文件中修改和实现写入时复制尝试。在写入时复制 trie 中,操作不会直接修改原始 trie 的节点。相反,将为修改后的数据创建新节点,并为新修改的 trie 返回新的根。写入时复制使我们能够在每次操作后随时以最小的开销访问 trie。考虑在上面的示例中插入。我们通过重用原始树中的两个子节点并创建一个新的值节点 2 来创建一个新节点。(见下图)
Get
这里Get是最容易实现的,通过遍历std::string_view key,若能找到对应的TrieNodeWithValue 则返回其value,否则返回nullptr。需要注意的是遍历到最后一个点的时候需要通过dynamic_cast将父类指针转换为子类指针。
由于dynamic_cast不能对智能指针操作因此通过**.get()**的方式获取裸指针
auto *now = dynamic_cast<const TrieNodeWithValue<T> *>(cur_node.get());
return now ? now->value_.get() : nullptr;
这里还是通过遍历std::string_view key当该结点没有的时候就停下,同时需要将已有的结点存入栈中用于之后的复制
while (idx < key_size && cur_node) {
char ch = key[idx++];
node_stack.push_back(cur_node);
cur_node = cur_node->children_.find(ch) != cur_node->children_.end() ? cur_node->children_.at(ch) : nullptr;
}
if (idx != key_size || !cur_node || !cur_node->is_value_node_) {
return *this;
}
由于trie.h中声明了 TrieNode(std::map<char, std::shared_ptr<const TrieNode>> children) : children_(std::move(children)) {}
不难想到通过子节点的方式生成父节点。
最后的叶子节点leaf_node
为子节点
std::shared_ptr<const TrieNodeWithValue<T>>leaf_node =
cur_node ? std::make_shared<TrieNodeWithValue<T>>(cur_node->children_,value_node)
:std::make_shared<TrieNodeWithValue<T>>(value_node);
std::shared_ptr<const TrieNode>child_node = leaf_node;
最后只需要从后往前遍历stack_node
并返回Trie(cur_node)
Remove
Remove的思路和Put有些类似也是将出现的结点存入栈中,遍历到最后如果该点的children为空,则将该点释放,如果该点有value则删除value
task2
task2就是多个线程能读,一个线程能写,当读和写的时候都需要用到root_,这个时候root_lock_.lock();
上完锁之后再unlock就好了
写入的时候还需要write_lock_.lock();
task3
task主要就是一个调试,由于随机数因为环境不同需要修改Trie_debug_test.cpp
task4
task4是实现一个大小写的转换