diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 3a103a6b3..14c28b1d5 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -240,5 +240,9 @@ menu "test app" bool "Config test soft timer" default n + menuconfig USER_TEST_RBTREE + bool "Config test red black tree" + default n + endif endmenu diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 200e9e02a..37cb3d64b 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -99,6 +99,10 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) ifeq ($(CONFIG_USER_TEST_TIMER),y) SRC_FILES += test_timer.c + endif + + ifeq ($(CONFIG_USER_TEST_RBTREE),y) + SRC_FILES += test_rbtree/test_rbtree.c endif include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png b/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png new file mode 100644 index 000000000..04caae4d1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/CompileSuccess.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png b/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png new file mode 100644 index 000000000..b62acec61 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/ConfigOpen.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/README.md b/APP_Framework/Applications/app_test/test_hash/README.md new file mode 100644 index 000000000..876cd5d08 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/README.md @@ -0,0 +1,58 @@ +# 基于cortex-m3-emulator实现哈希表并测试验证## + +## 1. 简介 +利用c语言实现了哈希表(HashMap),包括添加键值对(Put),获取键对应的值(Get), 删除健(Delete),清空哈希表(Clear), 迭代遍历哈希表(hashMapIterator)等功能 +操作。 + +利用数组(Entry)作为存储空间,利用链表(*next)解决冲突。当哈希表的大小超过数组大小后,为避免发生冲突过多的情况,可以对哈希表扩容。 + +## 2. 数据结构设计说明 +键值对结构 +typedef struct entry { + void * key; // 键 + void * value; // 值 + struct entry * next; // 冲突链表 +}*Entry; + +哈希结构 +typedef struct hashMap { + int size; // 当前大小 + int listSize; // 有效空间大小 + HashCode hashCode; // 哈希函数 + Equal equal; // 判等函数 + Entry list; // 存储区域 + Put put; // 添加键的函数 + Get get; // 获取键对应值的函数 + Remove remove; // 删除键 + Clear clear; // 清空Map + Exists exists; // 判断键是否存在 + Boolean autoAssign; // 设定是否根据当前数据量动态调整内存大小,默认开启 +}*HashMap; + +包括以下函数功能,分别为: +`createHashMap`:创建一个哈希结构 +`defaultPut`:添加键值对 +`defaultGet`:获取键对应值 +`defaultRemove`:删除指定键的键值对 +`defaultExists`:判断键值是否存在 +`defaultClear`:清空Map的函数类型 +`resetHashMap`:重新构建哈希表 + + +## 3. 测试程序说明 +测试了哈希表的插入键值对(Put),判断键是否存在(Exist),获取键对应的值(Get), 删除健(Delete),迭代遍历哈希表(hashMapIterator),清空哈希表(Clear)等操作。 +并展示了利用链地址法解决哈希冲突的示例, 两个不同的人(Bob和Li Ming)的hashcode相同。 + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) +![image](ConfigOpen.png) +打开menuconfig之后,将test_hash_map开启(y),保存后退出 + +![image](CompileSuccess.png) +编译XiZi-cortex-m3-emulator.elf成功 + +![image](ShellCommand.png) +启动qemu模拟Xiuos操作系统,验证TestHash注册Shell命令 + +![image](TestHash.png) +执行TestHash命令,打印测试结果。 + diff --git a/APP_Framework/Applications/app_test/test_hash/ShellCommand.png b/APP_Framework/Applications/app_test/test_hash/ShellCommand.png new file mode 100644 index 000000000..f9338e7e1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/ShellCommand.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/TestHash.png b/APP_Framework/Applications/app_test/test_hash/TestHash.png new file mode 100644 index 000000000..3170edfef Binary files /dev/null and b/APP_Framework/Applications/app_test/test_hash/TestHash.png differ diff --git a/APP_Framework/Applications/app_test/test_hash/test_hash.c b/APP_Framework/Applications/app_test/test_hash/test_hash.c new file mode 100644 index 000000000..d6eabc4c7 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.c @@ -0,0 +1,323 @@ + +/** +* @file: test_hash.c +* @brief: a application of test hash function +* @version: 3.0 +* @author: Yao wenying +* @date: 2023/05/26 +*/ + +#include +#include"test_hash.h" + +int defaultHashCode(HashMap hashMap, let key) { + char * k = (char *)key; + unsigned long h = 0; + while (*k) { + h = (h << 4) + *k++; + unsigned long g = h & 0xF0000000L; + if (g) { + h ^= g >> 24; + } + h &= ~g; + } + return h % hashMap->listSize; +} + +Boolean defaultEqual(let key1, let key2) { + return strcmp((string)key1, (string)key2) ? False : True; +} + +void resetHashMap(HashMap hashMap, int listSize) { + + if (listSize < 8) return; + + // 键值对临时存储空间 + Entry tempList = newEntryList(hashMap->size); + + HashMapIterator iterator = createHashMapIterator(hashMap); + int length = hashMap->size; + for (int index = 0; hasNextHashMapIterator(iterator); index++) { + // 迭代取出所有键值对 + iterator = nextHashMapIterator(iterator); + tempList[index].key = iterator->entry->key; + tempList[index].value = iterator->entry->value; + tempList[index].next = NULL; + } + freeHashMapIterator(&iterator); + + // 清除原有键值对数据 + hashMap->size = 0; + for (int i = 0; i < hashMap->listSize; i++) { + Entry current = &hashMap->list[i]; + current->key = NULL; + current->value = NULL; + if (current->next != NULL) { + while (current->next != NULL) { + Entry temp = current->next->next; + free(current->next); + current->next = temp; + } + } + } + + // 更改内存大小 + hashMap->listSize = listSize; + Entry relist = (Entry)realloc(hashMap->list, hashMap->listSize * sizeof(struct entry)); + if (relist != NULL) { + hashMap->list = relist; + relist = NULL; + } + + // 初始化数据 + for (int i = 0; i < hashMap->listSize; i++) { + hashMap->list[i].key = NULL; + hashMap->list[i].value = NULL; + hashMap->list[i].next = NULL; + } + + // 将所有键值对重新写入内存 + for (int i = 0; i < length; i++) { + hashMap->put(hashMap, tempList[i].key, tempList[i].value); + } + free(tempList); +} + +void defaultPut(HashMap hashMap, let key, let value) { + // 获取哈希值 + int index = hashMap->hashCode(hashMap, key); + + if (hashMap->list[index].key == NULL) { + hashMap->size++; + // 该地址为空时直接存储 + hashMap->list[index].key = key; + hashMap->list[index].value = value; + } + else { + + Entry current = &hashMap->list[index]; + while (current != NULL) { + if (hashMap->equal(key, current->key)) { + // 对于键值已经存在的直接覆盖 + current->value = value; + return; + } + current = current->next; + }; + + // 发生冲突则创建节点挂到相应位置的next上 + Entry entry = newEntry(); + entry->key = key; + entry->value = value; + entry->next = hashMap->list[index].next; + hashMap->list[index].next = entry; + hashMap->size++; + } + + if (hashMap->autoAssign && hashMap->size >= hashMap->listSize) { + + // 内存扩充至原来的两倍 + // *注: 扩充时考虑的是当前存储元素数量与存储空间的大小关系,而不是存储空间是否已经存满, + // 例如: 存储空间为10,存入了10个键值对,但是全部冲突了,所以存储空间空着9个,其余的全部挂在一个上面, + // 这样检索的时候和遍历查询没有什么区别了,可以简单这样理解,当我存入第11个键值对的时候一定会发生冲突, + // 这是由哈希函数本身的特性(取模)决定的,冲突就会导致检索变慢,所以这时候扩充存储空间,对原有键值对进行 + // 再次散列,会把冲突的数据再次分散开,加快索引定位速度。 + resetHashMap(hashMap, hashMap->listSize * 2); + } +} + +let defaultGet(HashMap hashMap, let key) { + if (hashMap->exists(hashMap, key)) { + int index = hashMap->hashCode(hashMap, key); + Entry entry = &hashMap->list[index]; + while (entry != NULL) { + if (hashMap->equal(entry->key, key)) { + return entry->value; + } + entry = entry->next; + } + } + return NULL; +} + +let defaultRemove(HashMap hashMap, let key) { + int index = hashMap->hashCode(hashMap, key); + Entry entry = &hashMap->list[index]; + if (entry->key == NULL) { + return NULL; + } + let entryKey = entry->key; + Boolean result = False; + if (hashMap->equal(entry->key, key)) { + hashMap->size--; + if (entry->next != NULL) { + Entry temp = entry->next; + entry->key = temp->key; + entry->value = temp->value; + entry->next = temp->next; + free(temp); + } + else { + entry->key = NULL; + entry->value = NULL; + } + result = True; + } + else { + Entry p = entry; + entry = entry->next; + while (entry != NULL) { + if (hashMap->equal(entry->key, key)) { + hashMap->size--; + p->next = entry->next; + free(entry); + result = True; + break; + } + p = entry; + entry = entry->next; + }; + } + + // 如果空间占用不足一半,则释放多余内存 + if (result && hashMap->autoAssign && hashMap->size < hashMap->listSize / 2) { + resetHashMap(hashMap, hashMap->listSize / 2); + } + return entryKey; +} + +Boolean defaultExists(HashMap hashMap, let key) { + int index = hashMap->hashCode(hashMap, key); + Entry entry = &hashMap->list[index]; + if (entry->key == NULL) { + return False; + } + else { + while (entry != NULL) { + if (hashMap->equal(entry->key, key)) { + return True; + } + entry = entry->next; + } + return False; + } +} + +void defaultClear(HashMap hashMap) { + for (int i = 0; i < hashMap->listSize; i++) { + // 释放冲突值内存 + Entry entry = hashMap->list[i].next; + while (entry != NULL) { + Entry next = entry->next; + free(entry); + entry = next; + } + hashMap->list[i].next = NULL; + } + // 释放存储空间 + free(hashMap->list); + hashMap->list = NULL; + hashMap->size = -1; + hashMap->listSize = 0; +} + +HashMap createHashMap(HashCode hashCode, Equal equal) { + HashMap hashMap = newHashMap(); + if (hashMap == NULL) { + return NULL; + } + hashMap->size = 0; + hashMap->listSize = 8; + hashMap->hashCode = hashCode == NULL ? defaultHashCode : hashCode; + hashMap->equal = equal == NULL ? defaultEqual : equal; + hashMap->exists = defaultExists; + hashMap->get = defaultGet; + hashMap->put = defaultPut; + hashMap->remove = defaultRemove; + hashMap->clear = defaultClear; + hashMap->autoAssign = True; + + // 起始分配8个内存空间,溢出时会自动扩充 + hashMap->list = newEntryList(hashMap->listSize); + if (hashMap->list == NULL) { + return NULL; + } + Entry p = hashMap->list; + for (int i = 0; i < hashMap->listSize; i++) { + p[i].key = p[i].value = p[i].next = NULL; + } + return hashMap; +} + +HashMapIterator createHashMapIterator(HashMap hashMap) { + HashMapIterator iterator = newHashMapIterator(); + if (iterator == NULL) { + return NULL; + } + iterator->hashMap = hashMap; + iterator->count = 0; + iterator->hashCode = -1; + iterator->entry = NULL; + return iterator; +} + +Boolean hasNextHashMapIterator(HashMapIterator iterator) { + return iterator->count < iterator->hashMap->size ? True : False; +} + +HashMapIterator nextHashMapIterator(HashMapIterator iterator) { + if (hasNextHashMapIterator(iterator)) { + if (iterator->entry != NULL && iterator->entry->next != NULL) { + iterator->count++; + iterator->entry = iterator->entry->next; + return iterator; + } + while (++iterator->hashCode < iterator->hashMap->listSize) { + Entry entry = &iterator->hashMap->list[iterator->hashCode]; + if (entry->key != NULL) { + iterator->count++; + iterator->entry = entry; + break; + } + } + } + return iterator; +} + +void freeHashMapIterator(HashMapIterator * iterator) { + free(*iterator); + *iterator = NULL; +} + +#define Put(map, key, value) map->put(map, (void *)key, (void *)value); +#define Get(map, key) (char *)map->get(map, (void *)key) +#define Remove(map, key) map->remove(map, (void *)key) +#define Existe(map, key) map->exists(map, (void *)key) + +void TestHash() { + HashMap map = createHashMap(NULL, NULL); + Put(map, "000123", "Annie"); + Put(map, "000245", "Bob"); + Put(map, "000284", "Daniel"); + Put(map, "000281", "Luna"); + Put(map, "000587", "Yao"); + Put(map, "000985", "Li Ming"); + Put(map, "000852", "Janne"); + + printf("print the key-values in hashmap:\n"); + HashMapIterator iterator = createHashMapIterator(map); + while (hasNextHashMapIterator(iterator)) { + iterator = nextHashMapIterator(iterator); + printf("{ key: %s, key: %s, hashcode: %d }\n", + (char *)iterator->entry->key, (char *)iterator->entry->value, iterator->hashCode); + } + printf("key: 000852, exists: %s\n", Existe(map, "000852") ? "true" : "false"); + printf("000852: %s\n", Get(map, "000852")); + printf("remove 000852 %s\n", Remove(map, "000852") ? "true" : "false"); + printf("key: 000852, exists: %s\n", Existe(map, "000852") ? "true" : "false"); + + map->clear(map); + freeHashMapIterator(&iterator); +} + +PRIV_SHELL_CMD_FUNCTION(TestHash, Implement hash_map, PRIV_SHELL_CMD_MAIN_ATTR); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_hash/test_hash.h b/APP_Framework/Applications/app_test/test_hash/test_hash.h new file mode 100644 index 000000000..3f7358411 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_hash/test_hash.h @@ -0,0 +1,129 @@ + +/** +* @file: test_hash.h +* @brief: a application of test hash function +* @version: 3.0 +* @author: Yao wenying +* @date: 2023/05/26 +*/ + + +#ifndef __HASHMAP_H__ +#define __HASHMAP_H__ + +#include +#include +#include + + +// 实现数据的基本类型 +// 字符串类型 +#define string char * +#define newString(str) strcpy((char *)malloc(strlen(str) + 1), str) +#define NEW(type) (type *)malloc(sizeof(type)) + +// 布尔类型 +enum _Boolean { True = 1, False = 0 }; +typedef enum _Boolean Boolean; + +#define let void * + +typedef struct entry { + let key; // 键 + let value; // 值 + struct entry * next; // 冲突链表 +}*Entry; + +#define newEntry() NEW(struct entry) +#define newEntryList(length) (Entry)malloc(length * sizeof(struct entry)) + +// 哈希结构 +typedef struct hashMap *HashMap; + +#define newHashMap() NEW(struct hashMap) + +// 哈希函数类型 +typedef int(*HashCode)(HashMap, let key); + +// 判等函数类型 +typedef Boolean(*Equal)(let key1, let key2); + +// 添加键函数类型 +typedef void(*Put)(HashMap hashMap, let key, let value); + +// 获取键对应值的函数类型 +typedef let(*Get)(HashMap hashMap, let key); + +// 删除键的函数类型 +typedef let(*Remove)(HashMap hashMap, let key); + +// 清空Map的函数类型 +typedef void(*Clear)(HashMap hashMap); + +// 判断键值是否存在的函数类型 +typedef Boolean(*Exists)(HashMap hashMap, let key); + +typedef struct hashMap { + int size; // 当前大小 + int listSize; // 有效空间大小 + HashCode hashCode; // 哈希函数 + Equal equal; // 判等函数 + Entry list; // 存储区域 + Put put; // 添加键的函数 + Get get; // 获取键对应值的函数 + Remove remove; // 删除键 + Clear clear; // 清空Map + Exists exists; // 判断键是否存在 + Boolean autoAssign; // 设定是否根据当前数据量动态调整内存大小,默认开启 +}*HashMap; + +// 迭代器结构 +typedef struct hashMapIterator { + Entry entry; // 迭代器当前指向 + int count; // 迭代次数 + int hashCode; // 键值对的哈希值 + HashMap hashMap; +}*HashMapIterator; + +#define newHashMapIterator() NEW(struct hashMapIterator) + +// 默认哈希函数 +static int defaultHashCode(HashMap hashMap, let key); + +// 默认判断键值是否相等 +static Boolean defaultEqual(let key1, let key2); + +// 默认添加键值对 +static void defaultPut(HashMap hashMap, let key, let value); + +// 默认获取键对应值 +static let defaultGet(HashMap hashMap, let key); + +// 默认删除键 +static let defaultRemove(HashMap hashMap, let key); + +// 默认判断键是否存在 +static Boolean defaultExists(HashMap hashMap, let key); + +// 默认清空Map +static void defaultClear(HashMap hashMap); + +// 重新构建 +static void resetHashMap(HashMap hashMap, int listSize); + +// 创建一个哈希结构 +HashMap createHashMap(HashCode hashCode, Equal equal); + +// 创建哈希结构迭代器 +HashMapIterator createHashMapIterator(HashMap hashMap); + +// 迭代器是否有下一个 +Boolean hasNextHashMapIterator(HashMapIterator iterator); + +// 迭代到下一次 +HashMapIterator nextHashMapIterator(HashMapIterator iterator); + +// 释放迭代器内存 +void freeHashMapIterator(HashMapIterator * iterator); + +#endif // !__HASHMAP_H__ \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_radix_tree/README.md b/APP_Framework/Applications/app_test/test_radix_tree/README.md new file mode 100644 index 000000000..b2bfe9cb9 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_radix_tree/README.md @@ -0,0 +1,67 @@ +# 基于k210-emulator实现基数树并测试验证 + +## 1. 简介 + +基于矽璓模拟器k210-emulator,实现基数树,并编写测试程序在shell终端打印结果。 + +## 2. 数据结构设计说明 + +基数树节点设计为: + +```c +typedef struct _node { +​ void* value; +​ struct _node* next[NODE_SIZE]; +} node; +``` + +其中,节点在树中的路径即为键,`value` 存储值,`NODE_SIZE` 定义为 128,足以容纳所有 ASCII 值。 + +一共实现了 5 个函数,分别为: + +- `CreateNode`:创建一个基数树节点 +- `InsertNode`:将一对键值对插入基数树 +- `DeleteNode`:删除指定键的键值对 +- `FindNode`:查找指定键对应的值 +- `DestroyTree`:销毁整个基数树 + +## 3. 测试程序说明 + +测试程序 `TestRadix` 已经注册为 shell 命令,可以调用执行。 + +测试程序定义了以下键值对: + +```c +char keys[][MAX_WORD_LEN] = { + "what", + "where", + "why", + "how", + "hello!", + "apple", + "12345" +}; +int values[] = {1, 2, 3, 4, 5, 6, 7}; +``` + +1. 程序的第一部分创建了基数树,并且将定义的 7 个键值对的前 6 个插入了基数树,然后分别查找 7 个键,前 6 个均可以找到对应的值,最后一个未插入,因此无法找到 +2. 程序的第二部分从基数树中删除了 `where` 和 `how` 两个键,再次分别查找 7 个键,删除的键值对和未插入的键值对均无法找到 +3. 程序的第三部分重新插入了已删除的 `where` 和未插入过的 `12345` ,再次分别查找 7 个键,新插入的值可以检索到 +4. 程序的第四部分将基数树销毁,再次分别查找 7 个键,所有的键值对均无法找到 + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) + +1. 在工作区终端中输入命令:`make BOARD=k210-emulator menuconfig`,进入配置页面 +![fig1](fig1.png) +2. 依次进入 `APP_Framework` -> `Applications` -> `test app` 目录,将 `Enable application test function` 选项置为 `Y` +![fig2](fig2.png) +3. 进入 `Enable application test function` 将 `Config test radix tree` 选项置为 `Y` +![fig3](fig3.png) +4. 一直选择 `Exit` 退出配置,在最后需要确认的页面选择 `Yes` 保存配置 +![fig4](fig4.png) +5. 执行编译命令:`make BOARD=k210-emulator`,正常情况下应当编译无误 +![fig5](fig5.png) +6. 在 `qemu` 中运行:`qemu-system-riscv64 -nographic -machine sifive_u -bios build/XiZi-k210-emulator.elf` +![fig6](fig6.png) +7. 在 shell 中运行命令 `TestRadix`,执行结果与预期一致,验证完成。 +![fig7](fig7.png) \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig1.png b/APP_Framework/Applications/app_test/test_radix_tree/fig1.png new file mode 100644 index 000000000..ce51222cb Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig1.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig2.png b/APP_Framework/Applications/app_test/test_radix_tree/fig2.png new file mode 100644 index 000000000..b35f3ecb6 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig2.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig3.png b/APP_Framework/Applications/app_test/test_radix_tree/fig3.png new file mode 100644 index 000000000..5d5fa2276 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig3.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig4.png b/APP_Framework/Applications/app_test/test_radix_tree/fig4.png new file mode 100644 index 000000000..3df214d59 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig4.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig5.png b/APP_Framework/Applications/app_test/test_radix_tree/fig5.png new file mode 100644 index 000000000..99cd9ec2e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig5.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig6.png b/APP_Framework/Applications/app_test/test_radix_tree/fig6.png new file mode 100644 index 000000000..80bf28136 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig6.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig7.png b/APP_Framework/Applications/app_test/test_radix_tree/fig7.png new file mode 100644 index 000000000..7fad83c9e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_radix_tree/fig7.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c new file mode 100644 index 000000000..c54a49805 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c @@ -0,0 +1,187 @@ +/** +* @file: test_radix_tree.c +* @brief: Implement a simple radix tree +* @version: 1.0 +* @date: 2023/5/24 +*/ + +#include +#include "test_radix_tree.h" + +/** + * @description: Create a radix tree node + * @return node pointer + */ +node* CreateNode() +{ + node* n = (node*)malloc(sizeof(node)); + n->value = NULL; + for (int i = 0; i < NODE_SIZE; i++) { + n->next[i] = NULL; + } + return n; +} + +/** + * @description: Insert a new node to radix tree + * @param root - radix tree root + * @param key - new node key + * @param value - new node value + * @return void + */ +void InsertNode(node* root, const char* key, void* value) +{ + if (root == NULL) { + return; + } + node* cur = root; + size_t len = strlen(key); + for (size_t i = 0; i < len; i++) { + uint8_t b = (uint8_t)key[i]; + if (cur->next[b] == NULL) { + cur->next[b] = CreateNode(); + } + cur = cur->next[b]; + } + cur->value = value; +} + +/** + * @description: Delete a node from radix tree + * @param root - radix tree root + * @param key - key which is needed to delete + * @return void + */ +void DeleteNode(node* root, const char* key) +{ + if (root == NULL) { + return; + } + node** cur = &root; + size_t len = strlen(key); + for (size_t i = 0; i < len; i++) { + uint8_t b = (uint8_t)key[i]; + if ((*cur)->next[b] == NULL) { + return; + } + cur = &((*cur)->next[b]); + } + + if ((*cur)->value == NULL) { + return; + } + + (*cur)->value = NULL; + + int has_children = 0; + for (int i = 0; i < NODE_SIZE; i++) { + if ((*cur)->next[i] != NULL) { + has_children = 1; + break; + } + } + if (!has_children) { + free(*cur); + (*cur) = NULL; + } +} + +/** + * @description: find a node by key + * @param root - radix tree root + * @param key - key which is needed to find + * @return value pointer corresponding to key + */ +void* FindNode(node* root, const char* key) +{ + if (root == NULL) { + return NULL; + } + node* cur = root; + size_t len = strlen(key); + for (size_t i = 0; i < len; i++) { + uint8_t b = (uint8_t)key[i]; + if (cur->next[b] == NULL) { + return NULL; + } + cur = cur->next[b]; + } + return cur->value; +} + +/** + * @description: Destroy the radix tree + * @param root - radix tree root + * @return void + */ +void DestroyTree(node* root) +{ + if (root == NULL) { + return; + } + for (int i = 0; i < NODE_SIZE; i++) { + DestroyTree(root->next[i]); + } + free(root); +} + +void TestRadix() +{ + char keys[][MAX_WORD_LEN] = { + "what", + "where", + "why", + "how", + "hello!", + "apple", + "12345" + }; + int values[] = {1, 2, 3, 4, 5, 6, 7}; + + printf("\nCreate tree and add key & value:\n"); + node* root = CreateNode(); + if (!root) printf("Create node failed.\n"); + + int num = sizeof(keys) / sizeof(keys[0]); + for (int i = 0; i < num - 1; ++i) { + InsertNode(root, keys[i], &values[i]); + } + + for (int i = 0; i < num; ++i) { + int* v = (int*)FindNode(root, keys[i]); + if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); + else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + } + + printf("\nDelete \"where\" and \"how\":\n"); + DeleteNode(root, keys[1]); + DeleteNode(root, keys[3]); + + for (int i = 0; i < num; ++i) { + int* v = (int*)FindNode(root, keys[i]); + if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); + else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + } + + printf("\nInsert \"where\" and \"12345\":\n"); + InsertNode(root, keys[1], &values[1]); + InsertNode(root, keys[6], &values[6]); + + for (int i = 0; i < num; ++i) { + int* v = (int*)FindNode(root, keys[i]); + if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); + else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + } + + printf("\nDestroy tree:\n"); + DestroyTree(root); + root = NULL; + + for (int i = 0; i < num; ++i) { + int* v = (int*)FindNode(root, keys[i]); + if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); + else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + } +} + +PRIV_SHELL_CMD_FUNCTION(TestRadix, Implement a simple radix tree, PRIV_SHELL_CMD_MAIN_ATTR); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h new file mode 100644 index 000000000..ea9e7b7fa --- /dev/null +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h @@ -0,0 +1,20 @@ +/** +* @file: test_radix_tree.h +* @brief: Implement a simple radix tree +* @version: 1.0 +* @date: 2023/5/24 +*/ + +#define NODE_SIZE 128 +#define MAX_WORD_LEN 128 + +typedef struct _node { + void* value; + struct _node* next[NODE_SIZE]; +} node; + +node* CreateNode(); +void InsertNode(node* root, const char* key, void* value); +void DeleteNode(node* root, const char* key); +void* FindNode(node* root, const char* key); +void DestroyTree(node* root); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_rbtree/README.md b/APP_Framework/Applications/app_test/test_rbtree/README.md new file mode 100644 index 000000000..4841e4ed0 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_rbtree/README.md @@ -0,0 +1,69 @@ +# 基于cortex-m4-emulator实现红黑树并测试验证 + +## 1. 简介 +红黑树是一种自平衡的二叉查找树,具有良好的插入、删除和查找性能,本次提交结果为红黑树数据结构的简单实现。test_rbtree.h中定义了红黑树的数据结构和声明插入、删除、查找相关操作函数;test_rbtree.c中为操作函数的定义,并提供了测试函数TestRBTree用于验证红黑树的正确性。 + + +## 2. 数据结构设计说明 + +**RBNodeType 结构体** +用来存储一个红黑树结点的相关信息 +- key:节点的键值 +- left_child:指向左子节点的指针 +- right_child:指向右子节点的指针 +- parent:指向父节点的指针 +- is_red:表示节点的颜色,true表示红色,false表示黑色 + +**RBTreeType 结构体** +用来存储一个红黑树的相关信息 +- root:指向红黑树的根节点的指针 +- leaf:红黑树的叶节点,由于叶节点并不需要存储数据,故每棵树只分配一个叶节点 + + +**RBTreeTraversal 函数** +该函数用于遍历红黑树并打印节点的键值。采用中序遍历的方式,递归地遍历左子树、当前节点和右子树。 + +**RBTreeLeftRotate 函数** +该函数实现红黑树的左旋转操作。接受一个当前节点指针作为参数,并按照左旋转的规则调整节点和子树的位置。 + +**RBTreeRightRotate 函数** +该函数实现红黑树的右旋转操作。接受一个当前节点指针作为参数,并按照右旋转的规则调整节点和子树的位置。 + +**InsertFixup 函数** +该函数用于插入节点后修复红黑树的平衡性。接受一个当前节点指针作为参数,并根据红黑树的性质进行旋转和着色操作,以恢复平衡。 + +**RBTreeInsert 函数** +该函数用于向红黑树中插入一个新节点。接受一个新节点指针作为参数,并根据新节点的键值插入到适当的位置,然后调用 InsertFixup 进行修复。 + +**DeleteFixup 函数** +该函数用于删除节点后修复红黑树的平衡性。接受一个当前节点指针作为参数,并根据红黑树的性质进行旋转和着色操作,以恢复平衡。 + +**RBTreeDelete 函数** +该函数用于从红黑树中删除指定节点。接受一个目标节点指针作为参数,并根据不同的情况进行节点的替换和删除操作,然后调用 DeleteFixup 进行修复。 + +**FindSuccessor 函数** +该函数用于查找给定节点的后继节点。接受一个当前节点指针作为参数,并在红黑树中找到当前节点的后继节点。 + +**RBTreeSearch 函数** +该函数用于在红黑树中查找指定键值的节点。接受一个键值作为参数,并在红黑树中进行查找,返回找到的节点指针。 + +## 3. 测试程序说明 +TestRBTree用于验证红黑树的功能和正确性,下面是该程序的使用步骤和说明: +- 函数中定义一个默认关键字数组,其中包含了20个整数关键字,运行时自动遍历数组构建红黑树,构建完成后中序遍历输出结果,可以根据输出结果验证红黑树的节点顺序以及颜色是否符合预期。 +- 对关键字数组中的每个关键字,在红黑树中进行搜索,并输出找到节点的父节点、左子节点和右子节点的关键字,随后删除该节点。 +- 在每次删除操作后,程序会询问是否显示当前的红黑树。如果输入 "Y" 或 "y",将再次进行中序遍历,并输出当前红黑树中的结点,可以根据输出结果查看结点是否符合预期。当树空时结束程序。 + + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) + +根据默认关键字数组构建红黑树 + +![Alt text](image.png) + +根据键值查找结点,输出相关信息并删除,可以输入'Y'或'y'展示删除后的红黑树 + +![Alt text](image-1.png) + +到达空树,退出程序 + +![Alt text](image-2.png) diff --git a/APP_Framework/Applications/app_test/test_rbtree/image-1.png b/APP_Framework/Applications/app_test/test_rbtree/image-1.png new file mode 100644 index 000000000..48ed6fb1a Binary files /dev/null and b/APP_Framework/Applications/app_test/test_rbtree/image-1.png differ diff --git a/APP_Framework/Applications/app_test/test_rbtree/image-2.png b/APP_Framework/Applications/app_test/test_rbtree/image-2.png new file mode 100644 index 000000000..578024325 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_rbtree/image-2.png differ diff --git a/APP_Framework/Applications/app_test/test_rbtree/image.png b/APP_Framework/Applications/app_test/test_rbtree/image.png new file mode 100644 index 000000000..40d78288f Binary files /dev/null and b/APP_Framework/Applications/app_test/test_rbtree/image.png differ diff --git a/APP_Framework/Applications/app_test/test_rbtree/test_rbtree.c b/APP_Framework/Applications/app_test/test_rbtree/test_rbtree.c new file mode 100644 index 000000000..35b718da0 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_rbtree/test_rbtree.c @@ -0,0 +1,341 @@ +/* +* Copyright (c) 2023 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: test_rbtree.c +* @brief: a application of red-black tree function +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/6/23 +*/ +#include +#include +#include"test_rbtree.h" +#ifdef ADD_XIZI_FETURES + +void RBTreeTraversal(RBTreeType *tree, RBNodeType *node) +{ + if (node != tree->leaf) { + RBTreeTraversal(tree, node->left_child); + printf("key:%d, color:%s\n", node->key, (node->is_red ? "Red" : "Black")); + RBTreeTraversal(tree, node->right_child); + } +} + +RBNodeType* RBTreeSearch(RBTreeType *tree, int key) +{ + RBNodeType* current_node = tree->root; + while (current_node != tree->leaf){ + if (key < current_node->key) + current_node = current_node->left_child; + else if (key > current_node->key) + current_node = current_node->right_child; + else + return current_node; + } + + return tree->leaf; +} + +void RBTreeLeftRotate(RBTreeType *tree, RBNodeType *current_node) +{ + RBNodeType* child_node = current_node->right_child; + + current_node->right_child = child_node->left_child; + if (child_node->left_child != tree->leaf) + child_node->left_child->parent = current_node; + + child_node->parent = current_node->parent; + if (current_node->parent == tree->leaf) + tree->root = child_node; + else if (current_node == current_node->parent->left_child) + current_node->parent->left_child = child_node; + else + current_node->parent->right_child = child_node; + + child_node->left_child = current_node; + current_node->parent = child_node; +} + +void RBTreeRightRotate(RBTreeType *tree, RBNodeType* current_node) +{ + RBNodeType* child_node = current_node->left_child; + + current_node->left_child = child_node->right_child; + if (child_node->right_child != tree->leaf) + child_node->right_child->parent = current_node; + + child_node->parent = current_node->parent; + if (current_node->parent == tree->leaf) + tree->root = child_node; + else if (current_node == current_node->parent->right_child) + current_node->parent->right_child = child_node; + else + current_node->parent->left_child = child_node; + + child_node->right_child = current_node; + current_node->parent = child_node; +} + +void InsertFixup(RBTreeType *tree, RBNodeType* current_node) +{ + while (current_node->parent->is_red){ + /* The parent of current_node is the left subtree of the grandfather */ + if (current_node->parent == current_node->parent->parent->left_child){ + RBNodeType * uncle_node = current_node->parent->parent->right_child; + if (uncle_node->is_red){ /* case1:red uncle and red parent, change color */ + uncle_node->is_red = false; + current_node->parent->is_red = false; + current_node->parent->parent->is_red = true; + + current_node = current_node->parent->parent; + }else{ /* case2:black uncle and red parent, need rotation */ + if (current_node->parent->right_child == current_node){ + current_node = current_node->parent; + RBTreeLeftRotate(tree, current_node); + } + + current_node->parent->is_red = false; + current_node->parent->parent->is_red = true; + RBTreeRightRotate(tree, current_node->parent->parent); + } + /* The parent of current_node is the right subtree of the grandfather, same with left subtree */ + }else{ + RBNodeType * uncle_node = current_node->parent->parent->left_child; + if (uncle_node->is_red){ + uncle_node->is_red = false; + current_node->parent->is_red = false; + current_node->parent->parent->is_red = true; + + current_node = current_node->parent->parent; + }else{ + if (current_node->parent->left_child == current_node){ + current_node = current_node->parent; + RBTreeRightRotate(tree, current_node); + } + + current_node->parent->is_red = false; + current_node->parent->parent->is_red = true; + RBTreeLeftRotate(tree, current_node->parent->parent); + } + } + } + tree->root->is_red = false; +} + +void RBTreeInsert(RBTreeType *tree, RBNodeType* new_node) +{ + RBNodeType* previous_node = tree->root; + RBNodeType* current_node = tree->root; + + while (current_node != tree->leaf){ + previous_node = current_node; + if (new_node->key > current_node->key) + current_node = current_node->right_child; + else if (new_node->key < current_node->key) + current_node = current_node->left_child; + else + return; + } + + if (previous_node == tree->leaf){ + tree->root = new_node; + tree->root->parent = tree->leaf; + }else{ + new_node->parent = previous_node; + + if (previous_node->key > new_node->key) + previous_node->left_child = new_node; + else + previous_node->right_child = new_node; + } + + InsertFixup(tree, new_node); +} + +RBNodeType* FindSuccessor(RBTreeType *tree, RBNodeType* current_node) +{ + RBNodeType* parent_node = current_node->parent; + if (current_node->right_child != tree->leaf){ + current_node = current_node->right_child; + while (current_node->left_child != tree->leaf) + current_node = current_node->left_child; + return current_node; + } + + while ((parent_node != tree->leaf) && (current_node == parent_node->right_child)){ + current_node = parent_node; + parent_node = parent_node->parent; + } + return parent_node; +} + +void DeleteFixup(RBTreeType *tree, RBNodeType* current_node) +{ + while ((current_node != tree->root) && (current_node->is_red == false)){ + if (current_node == current_node->parent->left_child){ + + RBNodeType* brother_node = current_node->parent->right_child; + if (brother_node->is_red){ + brother_node->is_red = false; + current_node->parent->is_red = true; + RBTreeLeftRotate(tree, current_node->parent); + brother_node = current_node->parent->right_child; + } + + if ((brother_node->left_child->is_red == false) && (brother_node->right_child->is_red == false)){ + brother_node->is_red = true; + current_node = current_node->parent; + }else{ + if (brother_node->right_child->is_red == false){ + brother_node->left_child->is_red = false; + brother_node->is_red = true; + RBTreeRightRotate(tree, brother_node); + brother_node = current_node->parent->right_child; + } + + brother_node->is_red = current_node->parent->is_red; + current_node->parent->is_red = false; + brother_node->right_child->is_red = false; + RBTreeLeftRotate(tree, current_node->parent); + current_node = tree->root; + } + }else{ + RBNodeType* brother_node = current_node->parent->left_child; + if (brother_node->is_red){ + brother_node->is_red = false; + current_node->parent->is_red = true; + RBTreeRightRotate(tree, current_node->parent); + brother_node = current_node->parent->left_child; + } + + if ((brother_node->left_child->is_red == false) && (brother_node->right_child->is_red == false)){ + brother_node->is_red = true; + current_node = current_node->parent; + }else{ + if (brother_node->left_child->is_red == false){ + brother_node->right_child->is_red = false; + brother_node->is_red = true; + RBTreeLeftRotate(tree, brother_node); + brother_node = current_node->parent->left_child; + } + + brother_node->is_red = current_node->parent->is_red; + current_node->parent->is_red = false; + brother_node->left_child->is_red = false; + RBTreeRightRotate(tree, current_node->parent); + current_node = tree->root; + } + } + } + current_node->is_red = false; +} + +void RBTreeDelete(RBTreeType *tree, RBNodeType* target_node) +{ + RBNodeType* delete_node = tree->leaf; + RBNodeType* replace_node = tree->leaf; + + if ((target_node->left_child == tree->leaf) || (target_node->right_child == tree->leaf)) + delete_node = target_node; + else + delete_node = FindSuccessor(tree, target_node); + + if (delete_node->left_child != tree->leaf) /* successor still has subtree */ + replace_node = delete_node->left_child; + else if (delete_node->right_child != tree->leaf) + replace_node = delete_node->right_child; + + replace_node->parent = delete_node->parent; + + if (delete_node->parent == tree->leaf) /* delete a root node */ + tree->root = replace_node; + else if (delete_node == delete_node->parent->left_child) + delete_node->parent->left_child = replace_node; + else + delete_node->parent->right_child = replace_node; + + if (delete_node != target_node) + target_node->key = delete_node->key; + + if (delete_node->is_red == false) + DeleteFixup(tree, replace_node); + + free(delete_node); +} + + +void TestRBTree(void) +{ + int default_key[] = { 16, 25, 23, 5, 2, 6, 17, 37, 38, 98, 20, 19, 47, 49, 12, 21, 9, 18, 14, 15 }; + int array_size = sizeof(default_key) / sizeof(default_key[0]); + + printf("Test Red Black Tree\n"); + printf("default_key array: "); + for (int i = 0; i < array_size; i++) + printf("%d ", default_key[i]); + printf("\n%d elements\n", array_size); + + RBTreeType *tree = (RBTreeType *)malloc(sizeof(RBTreeType)); + if (tree == NULL) { + printf("malloc failed\n"); + return; + } + + tree->leaf = (RBNodeType*)malloc(sizeof(RBNodeType)); + tree->leaf->left_child = NULL; + tree->leaf->right_child = NULL; + tree->leaf->parent = NULL; + tree->leaf->is_red = false; + tree->leaf->key = -1; + tree->root = tree->leaf; + + RBNodeType *node = tree->leaf; + + for (int i = 0; i < array_size; i++) { + node = (RBNodeType*)malloc(sizeof(RBNodeType)); + node->left_child = tree->leaf; + node->right_child = tree->leaf; + node->parent = NULL; + node->is_red = true; + node->key = default_key[i]; + printf("insert key[%d]=%d\n",i,default_key[i]); + RBTreeInsert(tree, node); + } + + printf("------------------Inorder Traversal------------------\n"); + RBTreeTraversal(tree, tree->root); + + for (int i = 0; i < array_size; i++) { + printf("search key = %d\n", default_key[i]); + node = RBTreeSearch(tree, default_key[i]); + printf("search succeeded, parent node: %d, left-child: %d, right-child: %d\n", node->parent->key, node->left_child->key, node->right_child->key); + + printf("delete key = %d\n", node->key); + RBTreeDelete(tree, node); + + printf("Show current tree?Y/N \n"); + char ch; + scanf("%c", &ch); + if (ch == 'Y' || ch == 'y') { + printf("------------------Inorder Traversal Tree After Deletion------------------\n"); + if (tree->root != tree->leaf) + RBTreeTraversal(tree, tree->root); + else + printf("the tree is empty.\n"); + } + } +} + +PRIV_SHELL_CMD_FUNCTION(TestRBTree, a red-black tree test sample, PRIV_SHELL_CMD_MAIN_ATTR); + +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_rbtree/test_rbtree.h b/APP_Framework/Applications/app_test/test_rbtree/test_rbtree.h new file mode 100644 index 000000000..b9592ac82 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_rbtree/test_rbtree.h @@ -0,0 +1,60 @@ +/* +* Copyright (c) 2023 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: test_rbtree.h +* @brief: a head file of red-black tree structure +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/6/23 +*/ +#ifndef REDBLACKTREE_H_ +#define REDBLACKTREE_H_ +#include +#include + +typedef struct RedBlackNode +{ + int key; + struct RedBlackNode *left_child; + struct RedBlackNode *right_child; + struct RedBlackNode *parent; + bool is_red; +} RBNodeType; + +typedef struct RedBlackTree +{ + RBNodeType *root; + RBNodeType *leaf; +} RBTreeType; + +void TestRBTree(void); + +void RBTreeTraversal(RBTreeType *tree, RBNodeType *node); + +void RBTreeLeftRotate(RBTreeType *tree, RBNodeType *current_node); + +void RBTreeRightRotate(RBTreeType *tree, RBNodeType* current_node); + +void InsertFixup(RBTreeType *tree, RBNodeType* current_node); + +void RBTreeInsert(RBTreeType *tree, RBNodeType* new_node); + +void DeleteFixup(RBTreeType *tree, RBNodeType* current_node); + +void RBTreeDelete(RBTreeType *tree, RBNodeType* target_node); + +RBNodeType* FindSuccessor(RBTreeType *tree, RBNodeType* current_node); + +RBNodeType* RBTreeSearch(RBTreeType *tree, int key); + +#endif \ No newline at end of file