diff --git a/project/LinXinHao/.keep b/project/LinXinHao/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/project/LinXinHao/BST.c b/project/LinXinHao/BST.c new file mode 100644 index 0000000000000000000000000000000000000000..a5db9d4ce5e3d8403822476184f48b28db92ab05 --- /dev/null +++ b/project/LinXinHao/BST.c @@ -0,0 +1,190 @@ +#include +#include + +#define T int //通过替换T获得不同类型的二叉搜索树 + + +typedef struct BST_Node // 二叉搜索树的节点的类型 +{ + T _value; // 该节点储存的值 + struct BST_Node* _left; // 该节点的左孩子 + struct BST_Node* _right; // 该节点的右孩子 +}BST_Node; + +BST_Node* creatRoot(T value) +{ + BST_Node* newNode = (BST_Node*)malloc(sizeof(BST_Node)); + newNode->_value = value; + newNode->_left = NULL; + newNode->_right = NULL; + return newNode; +} + +int find(BST_Node* root, const T value) +{ + BST_Node* current = root; + + while (current != NULL) // current不为空表示还有点未排除,可以继续寻找 + { + if (value < current->_value) + { + // 根据二叉搜索树的性质,该节点的左子树元素的每个值都应小于该节点,右子树则相反,于是应该向左子树寻找 + current = current->_left; + } + else if (value > current->_value) + { + // 同理,要寻找的值大于该节点的值,应向右寻找 + current = current->_right; + } + else + { + // 此时找到值相同的节点,那么返回true + return 1; + } + } + + // 此时找不到满足条件的值,那么返回false + return 0; +} + +void insert(BST_Node* root, const T value) +{ + BST_Node* newNode = (BST_Node*)malloc(sizeof(BST_Node)); + newNode->_left = NULL; + newNode->_right = NULL; + newNode->_value = value; + if (root == NULL) // 若没有根节点,则把生成一个值为value的节点作为根节点 + { + root = newNode; + } + else // 否则就寻找该值应该储存的位置并插入 + { + // 寻找的方法与find函数中类似,这里省略了一样的注释;而不调用find是为了确定变量current和变量parent + // 所以也可以使find函数能够获得变量current和变量parent,从而通过调用find函数减少代码量,这里不作演示 + + // 创建一个变量current用于存放要寻找的位置;而parent变量用于存储该位置的父节点,以便于插入 + BST_Node* current = root; + BST_Node* parent = NULL; + + while (current != NULL) // 若current为空则说明这就是要寻找的位置 + { + parent = current; + if (value < current->_value) + { + current = current->_left; + } + else if (value > current->_value) + { + current = current->_right; + } + else + { + // 此外,还有一种特殊情况,该节点存储的刚好就是要插入的值 + // 而二叉搜索树中同一个值只能出现一个,所以无需插入,直接返回 + return; + } + } + + // 此处是判断是父节点的左孩子还是右孩子,然后再插入一个储存改值的节点 + if (value < parent->_value) + { + parent->_left = newNode; + } + else + { + parent->_right = newNode; + } + } + +} + +void eraser(BST_Node* root, const T value) +{ + // 移除前要先找到该节点,方法也同上,省略类似注释 + BST_Node* current = root; + BST_Node* parent = root; + + while (current != NULL) + { + if (value < current->_value) + { + parent = current; + current = current->_left; + } + else if (value > current->_value) + { + parent = current; + current = current->_right; + } + else + { + // 此时已找到要删除的节点,但对于该节点的左右子树情况要作分类讨论 + if (current->_left == NULL) + { + // 若该点无左子树,那么让它的父节点跳过它而直接替换成它的右子树,此时不论该右子树是否非空都成立 + // 注意,若要删除的节点为父节点的左孩子,要把父节点的左孩子进行替换,反之则替换右孩子 + // (不要将父节点的左右孩子,与该节点的左右子树混淆,是要将该节点的位置替换上左右子树从而删除该节点) + if (value < parent->_value) + { + parent->_left = current->_right; + } + else + { + parent->_right = current->_right; + } + } + else if (current->_right == NULL) + { + // 若要删除的节点无右孩子也类似,同上 + if (value < parent->_value) + { + parent->_left = current->_left; + } + else + { + parent->_right = current->_left; + } + } + else + { + // 若要删除的节点左右子树都存在,该情况最为复杂 + // 思路是先替换上一个子树,然后将另一个子树接在次子树上 + // 若先替换为左子树,根据性质,右子树应该接在左子树的最右端(也就是一直向右孩子走直到到达空指针的位置) + // (因为右子树的每个元素全都大于左子树的每个元素,所以像查找右子树元素一样,会一直向右走) + // 也可以先替换为右子树,那么左子树应该接在右子树的最左端 + + // 这里以先替换为左子树为例子 + BST_Node* temp = current->_right; + if (value < parent->_value) + { + parent->_left = current->_left; + } + else + { + parent->_right = current->_left; + } + + current = current->_left; + while (current != NULL) + { + parent = current; + current = current->_right; + } + parent->_right = temp; + } + return; + } + } +} + +//int main() +//{ +// BST_Node* a = creatRoot(10); +// insert(a, 12); +// insert(a, 13); +// printf("%d\n", find(a, 12)); +// eraser(a, 12); +// printf("%d\n", find(a, 12)); +// +// return 0; +//} \ No newline at end of file diff --git a/project/LinXinHao/BST.cpp b/project/LinXinHao/BST.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1448fc6b682fe9478cbbee867416bf65b4226d04 --- /dev/null +++ b/project/LinXinHao/BST.cpp @@ -0,0 +1,212 @@ +#include + +using namespace std; + +template +class BST_Node // 二叉搜索树的节点的类型 +{ +public: + T _value; // 该节点储存的值 + BST_Node* _left; // 该节点的左孩子 + BST_Node* _right; // 该节点的右孩子 + + + BST_Node(const T& value) // 有参构造函数 + { + this->_value = value; + this->_left = nullptr; + this->_right = nullptr; + } +}; + +template +class BST // 该类型用于使用与维护二叉搜索树 +{ +public: + BST_Node* _root; // 该树的根节点 + + bool find(const T& value); // 该函数用于查询该数是否有节点的值为value,若是返回true,若否返回false + void insert(const T& value); // 该函数用于插入一个值为value的节点 + void remove(const T& value); // 该函数用于移除该数中值为value的节点 + + BST() // 无参构造函数 + { + _root = nullptr; + } + + /* + * 实际上,二叉搜索树还能增加更多功能,查询高度,该节点是否为左右孩子,最大/小值等, + * 甚至可以加上平衡的约束变为二叉平衡树,或加上红黑树的规则变成红黑树 + */ +}; + +template +bool BST::find(const T& value) +{ + BST_Node* current = _root; + + while (current != nullptr) // current不为空表示还有点未排除,可以继续寻找 + { + if (value < current->_value) + { + // 根据二叉搜索树的性质,该节点的左子树元素的每个值都应小于该节点,右子树则相反,于是应该向左子树寻找 + current = current->_left; + } + else if (value > current->_value) + { + // 同理,要寻找的值大于该节点的值,应向右寻找 + current = current->_right; + } + else + { + // 此时找到值相同的节点,那么返回true + return true; + } + } + + // 此时找不到满足条件的值,那么返回false + return false; +} + +template +void BST::insert(const T& value) +{ + if (_root == nullptr) // 若没有根节点,则把生成一个值为value的节点作为根节点 + { + _root = new BST_Node(value); + } + else // 否则就寻找该值应该储存的位置并插入 + { + // 寻找的方法与find函数中类似,这里省略了一样的注释;而不调用find是为了确定变量current和变量parent + // 所以也可以使find函数能够获得变量current和变量parent,从而通过调用find函数减少代码量,这里不作演示 + + // 创建一个变量current用于存放要寻找的位置;而parent变量用于存储该位置的父节点,以便于插入 + BST_Node* current = _root; + BST_Node* parent = nullptr; + + while (current != nullptr) // 若current为空则说明这就是要寻找的位置 + { + parent = current; + if (value < current->_value) + { + current = current->_left; + } + else if (value > current->_value) + { + current = current->_right; + } + else + { + // 此外,还有一种特殊情况,该节点存储的刚好就是要插入的值 + // 而二叉搜索树中同一个值只能出现一个,所以无需插入,直接返回 + return; + } + } + + // 此处是判断是父节点的左孩子还是右孩子,然后再插入一个储存改值的节点 + if (value < parent->_value) + { + parent->_left = new BST_Node(value); + } + else + { + parent->_right = new BST_Node(value); + } + } + +} + +template +void BST::remove(const T& value) +{ + // 移除前要先找到该节点,方法也同上,省略类似注释 + BST_Node* current = _root; + BST_Node* parent = _root; + + while (current != nullptr) + { + if (value < current->_value) + { + parent = current; + current = current->_left; + } + else if (value > current->_value) + { + parent = current; + current = current->_right; + } + else + { + // 此时已找到要删除的节点,但对于该节点的左右子树情况要作分类讨论 + if (current->_left == nullptr) + { + // 若该点无左子树,那么让它的父节点跳过它而直接替换成它的右子树,此时不论该右子树是否非空都成立 + // 注意,若要删除的节点为父节点的左孩子,要把父节点的左孩子进行替换,反之则替换右孩子 + // (不要将父节点的左右孩子,与该节点的左右子树混淆,是要将该节点的位置替换上左右子树从而删除该节点) + if (value < parent->_value) + { + parent->_left = current->_right; + } + else + { + parent->_right = current->_right; + } + } + else if (current->_right == nullptr) + { + // 若要删除的节点无右孩子也类似,同上 + if (value < parent->_value) + { + parent->_left = current->_left; + } + else + { + parent->_right = current->_left; + } + } + else + { + // 若要删除的节点左右子树都存在,该情况最为复杂 + // 思路是先替换上一个子树,然后将另一个子树接在次子树上 + // 若先替换为左子树,根据性质,右子树应该接在左子树的最右端(也就是一直向右孩子走直到到达空指针的位置) + // (因为右子树的每个元素全都大于左子树的每个元素,所以像查找右子树元素一样,会一直向右走) + // 也可以先替换为右子树,那么左子树应该接在右子树的最左端 + + // 这里以先替换为左子树为例子 + BST_Node* temp = current->_right; + if (value < parent->_value) + { + parent->_left = current->_left; + } + else + { + parent->_right = current->_left; + } + + current = current->_left; + while (current != nullptr) + { + parent = current; + current = current->_right; + } + parent->_right = temp; + } + return; + } + } +} + + +int main() +{ + /* + BST a; + a.insert(10); + a.insert(12); + a.insert(13); + cout << a.find(12) << endl; + a.remove(12); + cout << a.find(12) << endl; + */ + return 0; +} \ No newline at end of file