文章目录
环境配置中遇到的问题 WSL2 Ubuntu18.04
- clang更新:
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo vi /etc/apt/sources.list
加入下面内容:
deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main
deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic main
# Needs 'sudo add-apt-repository ppa:ubuntu-toolchain-r/test' for libstdc++ with C++20 support
# 16
deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-16 main
deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-16 main
# 17
deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-17 main
deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-17 main
然后更新:
sudo apt update
sudo apt-get install clang-format clang-tidy clang-tools clang
sudo apt-get purge clang-10 # 卸载老版本,按你自己的实际情况选择参数
sudo apt-get purge libclang-10-dev
Task#1 Copy-On-Write Trie
- 注意GetRoot是一个不能用的函数
- Get(key):
- 正常的前缀树思路
- 算法流程:
- 从key中取一个字符,如果当前节点不存在对应的儿子节点,返回nullptr;否则,移动到对应的儿子节点,并重复该过程,直到取完key中所有字符,到第2步
- 判断当前节点是不是值节点,如果不是,返回nullptr;否则到第3步
- 将该节点转化成TrieNodeWithValue* 类型,如果转换成功,返回该指针;否则返回nullptr
- Put(key, value):
- 注意写时复制
- 算法流程:
- 新建一棵树new_trie,一个新的根节点new_root
- 如果当前前缀树根节点不为空,更新new_root为当前前缀树根节点的Clone
- 特殊处理key为空的情况,新建一个以value作为值,以new_root的儿子节点作为儿子节点的值节点,将其作为new_trie的根节点,并返回* new_trie
- 从key中取一个字符,如果当前节点不存在对应的儿子节点,到第5步;否则,到第6步,当key中只剩最后一个字符时,到第7步
- 该节点不能被复用,创建一个新节点,并将对应的字符ch和新节点new_node加到当前节点的儿子节点中,移动到new_node,再次执行第4步
- 该节点可以复用,生成一个该节点的Clone,并将其更新到当前节点的儿子节点中,移动到该新节点,再次执行第4步
- 根据key中最后一个字符,找对应的儿子节点,如果该儿子节点存在,以其为基础创建一个新的值节点;否则,创建一个新的值节点
- 将new_trie的根节点设置成new_root,并返回* new_trie
- Delete(key):
- 对应代码中是Remove函数
- 算法流程:
- 新建一棵树new_trie,一个新的根节点new_root,用一个map来记录各节点的父节点,其形式是pair<std::shared_ptr, std::pair<std::shared_ptr, char>>,(cur, (fa, ch))表明cur节点是fa节点遇到ch字符转移过来的
- 如果当前前缀树根节点不为空,更新new_root为当前前缀树根节点的Clone
- 特殊处理key为空的情况,如果当前节点是个值节点,以其为基础新建一个非值节点,并将其设置为新前缀树的树根,返回新树;否则,返回旧树
- 从key中取一个字符,如果当前节点不存在对应的儿子节点,返回旧树;否则,到第5步,当key中只剩最后一个字符时,到第6步
- 该节点可以复用,生成一个该节点的Clone,并将其更新到当前节点的儿子节点中,记录其父节点情况,移动到该新节点,再次执行第4步
- 根据key中最后一个字符,找对应的儿子节点,如果该儿子节点存在并且是个值节点,以其为基础创建一个非值节点,到第7步;否则,返回旧树
- 删除节点之后,需要清空无用的节点,无用的节点满足几个条件:不是值节点 + 没有儿子节点。从当前被删除节点向上找,借助记录父节点的map,清理无用节点。如果所有节点都被清空了,将新树的根节点设置成空,否则设置成new_root,返回新树
Task#2 Concurrent Key-Value Store
-
以Task1中实现的三个函数为基础,实现多线程版本
-
Get(key):
- 算法流程:
- 给根节点上锁,取根节点,释放根节点的锁
- 调用单线程版本的Get函数,如果返回值是nullptr,返回std::nullopt;否则构建并返回std::optional<ValueGuard>
- 算法流程:
-
Put(key, value):
- 算法流程:
- 请求写锁
- 给根节点上锁,取根节点,释放根节点的锁
- 调用单线程版本的Put函数
- 给根节点上锁,更新根节点,释放根节点的锁
- 释放写锁
- 算法流程:
-
Delete(key):
- 算法流程:
- 请求写锁
- 给根节点上锁,取根节点,释放根节点的锁
- 调用单线程版本的Delete函数
- 给根节点上锁,更新根节点,释放根节点的锁
- 释放写锁
- 算法流程:
Task#3 Debugging
- 在对应的位置使用cout打印出对应的数值就可以了
Task#4 SQL String Functions
- 修改string_expression.h文件中的Compute函数
- 算法流程:判断操作类型,根据操作类型将字符串中的字符全改成大写或小写