diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 45df5f5d5..05f9b68d2 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 1cf919846..116d5d12c 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -99,6 +99,10 @@ ifeq ($(CONFIG_ADD_XIZI_FETURES),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_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