首页 > 编程语言 ># Project #0 - C++ Primer

# Project #0 - C++ Primer

时间:2022-11-22 21:56:23浏览次数:122  
标签:node char TrieNode C++ Project trie value key Primer

https://15445.courses.cs.cmu.edu/fall2022/project0/

bustub 项目用 C++ 17 编写,但 C++ 11 已经够用。

C++ 相关教程:

关于GDB调试:

git 命令:

PROJECT SPECIFICATION

该项目中,实现一个由并发 Trie 支持的kv存储。Trie 是一种有效的 ordered-tree 数据结构,用于检索给定键的值。为了简化解释,我们假设键都是非空的可变长度字符串,但实际上它们可以是任意类型。

Trie 中的每个节点存储一个键的单个字符,并且可以有多个子节点,这些子节点表示不同的可能的下一个字符。当到达一个键的结尾时,将设置一个标志来指示其对应的节点是一个结束节点。

在下面的示例中,trie 存储键 HELLOHATHAVE

image-20221013195202758

你将实现的kv存储能存储 map 到任何类型的 value 的 string key。key 对应的 value 值存储在表示该 key 的最后一个字符的结点(又称 terminal node)中。

例如,考虑在 trie 中插入 kv 对(“ ab”,1)和(“ ac”,“ val”)。这两个键共享同一个父节点 “a”。在左边的子节点中,与键 “ab” 对应的值 1 存储在节点 “b” 中,与键 “ac” 对应的值 “val” 存储在节点 “c” 中。

IMPLEMENTATION

您只需要修改 BusTub 中的一个文件 p0_ trie.h (src/include/primer/p0 _ trie.h)。除了测试代码之外,您不需要修改存储库中的任何其他文件。

函数原型和成员变量在文件中指定。该项目要求您填写所有构造函数、析构函数和成员函数的实现。如果您认为合适,可以添加任何其他的辅助函数和成员变量,但不要修改现有的函数和变量。

Task #1 - Templated Trie

在这个头文件中,我们定义了三个必须实现的类。我们建议您首先实现 Trie 类的单线程版本,然后再转向并发版本。

TrieNode Class

TrieNode 类在 Trie 中定义单个节点。TrieNode 保存 key 的一个字符,is_end flag 指示它是否标记 key string 的结束。child nodes 的 key 字符从成员变量 children_ 这个map 访问得到,它存储charunique_ptr<TrieNode> 的映射。

注意:child node 为 unique_ptr,因此将它们分配给其他变量时需要小心。InsertChildNodeGetChildNode 都返回一个指向 unique_ptr 的指针,这样就可以在不制作 copy 或放弃所有权的情况下访问 unique_ptr 的数据。

最后,移动构造函数 TrieNode(TrieNode &&other_trie_node) 用于将旧 TrieNode 的 unique pointer 传输到新的 TrieNode。确保在传输数据时没有复制唯一指针。

TrieNodeWithValue Class

TrieNodeWithValue 类继承自 TrieNode,表示一个 terminal node,它的 key_char 是 key 的最后一个字符,该结点可保存任意类型 T的 value ,is_end_ flag 始终为 true。

当使用给定的 key 迭代一个 trie 树并到达最终字符时,你将根据不同场景调用 TrieNodeWithValue 的不同构造函数(详见 Trie Class 部分)。现在,你需要知道的是,TrieNodeWithValue (char key_char,T value) 构造函数根据给定的 key 的字符和 value 构建一个 TrieNodeWithValueTrieNodeWithValue (TrieNode &&trieNode,T value) 构造函数从给定的 trieNode 中获取 unique_ptr 的所有权,并将它自己的 value_ 设置为给定的值。

Trie Class

Trie 类定义支持 insert、search、remove 操作的实际 Trie 树。Trie 树的根节点是所有 key 的起始节点,它本身不应该存储任何键字符。

Insert

要在 Trie 树中插入一个kv,首先要使用给定的 key 遍历该 Trie 树,若不存在 TrieNode,则插入。⚠️ 不允许插入一个重复的 key,应该返回 false。一旦到达 key 的最后一个字符,会有三种情况:

  1. 具有该字符的 TrieNode 不存在。则需要调用TrieNodeWithValue (char key_char,T value) 构造函数来创建具有指定 key_char 和 value 的 trie node。利用多态性 —— TrieNode 类的 unique_ptr 也可存储 TrieNodeWithValue 类的 unique_ptr
  2. 具有该字符的 TrieNode 存在,但不是 terminal node(is_end_==false)。这意味着 unique_ptr 指向的是 TrieNode 类对象而不是 TrieNodeWithValue 类对象。需要调用 TrieNodeWithValue (TrieNode &&trieNode,T value) 构造函数将旧TrieNode 对象转为新的TrieNodeWithValue
  3. 具有该字符的 TrieNode 存在,且已是 terminal node(is_end_==true)这意味着 unique_ptr 已经指向一个 TrieNodeWithValue 类对象。应该立即返回 false,因为 key 不允许重复。
Remove

从 Trie 树中删除一个kv:

  1. 根据给定的 key 遍历该 Trie 树,若该 key 不存在,立即 return。
  2. 将 terminal node 的 is_end_ flag 变为 false。
  3. 若 terminal node 没有任何child结点,将它从父结点的children_ map 中移除。
  4. 遍历 Trie 树并递归删除没有子结点的结点,当遇到有子结点的结点时就停止。
GetValue

对于给定的 key,返回对应的 value。若找不到 key 或提供的 type 与结点 value 的 type 不匹配,则将 success 设为 false。要检查这两种 type 是否相同,dynamic_cast raw TrieNode pointer to TrieNodeWithValue<T> pointer. 若转换结果为 nullptr 则 type T 与结点存储的 value 的类型不匹配。

Task #2 - Concurrent Trie

Trie 树需要保证 insert、search、remove 操作在多线程环境中工作。可使用 RwLatch(BusTub 对 readers-writer lock 的实现)或 C++ STL 的std::shared_mutex 来实现。

这个项目只需要你通过获取根节点的 read/write lock 来实现简单的并发控制。

  • GetValue 函数应该从根结点获取一个 read lock(通过调用 RwLatchRLock 方法)
  • InsertRemove 操作应该从根结点获取一个 write lock(通过调用 RwLatchWLock 方法)

若使用 RWLatch,请确保在函数返回之前解锁所有获取的锁,以避免死锁。

Testing

可以使用我们的测试框架来测试此任务的各个组件。我们对单元测试用例使用 GTest。

test/primer/starter_trie_test.cpp 文件包含了对以上三个类的测试。

测试前要删掉测试文件中各函数前的 DISABLED_ 前缀。

$ cd build
$ make starter_trie_test
$ ./test/starter_trie_test

提交

您将把您的实现提交给 Grescope:https://www.gradescope.com/courses/424375/

您只需在提交的 zip 文件中包含以下文件及其完整路径:

  • src/include/primer/p0_trie.h

或者,在你的工作目录 (aka bustub, bustub-private, etc.) 运行

标签:node,char,TrieNode,C++,Project,trie,value,key,Primer
From: https://www.cnblogs.com/angelia-wang/p/16916598.html

相关文章

  • C++ 复习
    第一章C++的初步认识类是C++新增加的重要数据类型,可以体现数据的封装性和信息隐蔽。封装:把有关数据与操作组成一个单位,与外界相对隔离。大多情况下,将类中所有数......
  • c语言的钩子与C++的策略模式
    1.c语言钩子:特性模块:功能函数,调用注册函数主线模块:注册函数,定义钩子(通常是全局变量),调用钩子 2.c++策略模式:特性模块:从策略基类派生一个新特性类,实例化对象并调用se......
  • C++——vector
    vector(向量):C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的.用......
  • C++ 动态规划
    C++动态规划动态规划的定义动态规划(DynamicProgramming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。动态规划是一种在数学、管理科学、计算机科学、经济学和生......
  • c++成绩分析(2020蓝桥杯F题)
    题目描述小蓝给学生们组织了一场考试,卷面总分为100分,每个学生的得分都是一个0到100的整数。请计算这次考试的最高分、最低分和平均分。输入描述输入的第一......
  • 使用cmake构建C/C++项目和动态库
    编译C/C++文件时,很多时候都是直接使用像gccmain.c或者g++main.cpp这样的命令编译的。但是代码文件多了后,这样编译就很困难了。这时候就出现了MakeFile这个工具。......
  • C++ Builder使用FMX多平台框架(FireMonkey)开发安卓APP应用,底层是基于什么?
    【DelphiGuy】:底层是基于AndroidNDK的,JDK也可以调用。Delphi、C++Builder目前编译生成的安卓应用是基于ARM机器码的共享库.so(相当于DLL,在.APK中有一个java写的启动代码......
  • C++——map
      Map是c++的一个标准容器,她提供了很好一对一的关系,在一些程序中建立一个map可以起到事半功倍的效果,总结了一些map基本简单实用的操作!1.map最基本的构造函数;  map<s......
  • 2. pycharm终端提示无法加载文件 F:\Users\Administrator\PycharmProjects\python
    问题如下:终端(terminnal)遇到下面红色问题。   怎么解决??pycharm终端提示无法加载文件F:\Users\Administrator\PycharmProjects\pythonProject\venv\Scripts\activa......
  • (C++) atomic_flag的使用
    使用示例#include<atomic>#include<iostream>#include<thread>#include<vector>intmain(intargc,char**argv){constexprsize_tkLoopNum=10;std::......