C++一步一步实现二叉搜索树
来源:互联网 发布:小鑫优化 编辑:程序博客网 时间:2024/06/08 04:00
学习《STL源码剖析》关联容器的章节,map,set等关联容器的底层实现都是红黑树,本着循序渐进的思想,先实现二叉搜索树。读懂了二叉搜索树的定义后,实现了二叉搜索树的基本功能。文章属原创,代码全手打,欢迎指正讨论。首先给出类的定义,如下:
#include<iostream>#include<string>#include<cstring>#include<sstream>//#include<>using namespace std;class BST{private: //嵌套类,仅在BST中有效 class Node { public: int val; Node *left; Node *right; Node *parent; //增加父节点,在删除节点时父节点很有用 Node() { val = 0; left = right = parent = NULL; } }; Node *m_root; //存储根节点 Node *m_cur; //存储查找的结果 int m_size; //存储树的大小 void SetNode(Node* cur,Node* newNode); //设置节点的父子关系 bool CompareTree(const Node *r1,Node* r2) const;public: bool InsertNode(const int val); //插入节点 bool FindNode(const int val); //找到返回true,val所在的位置就是pos bool DelNode(const int val); //删除节点 void Inorder(Node *p, string &str) const; //中序遍历输出参数可自定义 void DestroyTree(Node *p); //销毁整棵二叉树,模仿后序遍历 bool operator==(BST &t) const; //用于判断两个BST是否相等 void CreateBST(const string &str); Node* GetRootNode() const { return m_root; } BST() :m_root(NULL), m_cur(NULL),m_size(0){} ~BST() { DestroyTree(m_root); }};
一、实现节点插入功能,此处是构建二叉搜索树的基本操作。插入节点时需满足的条件有二:
1、左子树小于父节点。
2、右子树不小于父节点。
以下是详细实现代码,以下代码虽不是最简短的写法,但是但是能想到的思路最清晰的写法,适合用于学习理解二叉搜索树。
bool BST::InsertNode(const int val){ Node *p = new Node(); p->val = val; m_size++; if (m_root == NULL) { m_root = p; return true; } Node * cur = m_root; Node * last = m_root; bool lflag = false; //为true说明是左子树 while (cur != NULL) { last = cur; if (val >= cur->val) { cur = cur->right; lflag = false; } else { cur = cur->left; lflag = true; } } //建立父节点与子树的关系 cur = p; cur->parent = last; lflag == true ? last->left = cur : last->right = cur; return true;}
二、建立二叉搜索树。此处只需要循环调用上面实现的InsertNode方法即可。
void BST::CreateBST(const string &str){ stringstream ss; int val; for (int i = 0; i < str.size(); i++) { ss << str[i]; ss >> val; ss.clear();//多次转换需要清空 InsertNode(val); }}
三、中序遍历二叉搜索树。二叉搜索树中序遍历的结果可得到递增的有序序列,从此处可以理解为BST查找的平均复杂度是O(logn),因为其查找过程类似于二分查找。为了方便输出查看,将输出结果放在string中。中序遍历实现方法有两种,其一为采用递归的方式;其二为使用栈来辅助实现。此处给出递归版本,今后再补充给出非递归版本。
void BST::Inorder(Node *p, string &st) const{ if (p == NULL) { return; } Inorder(p->left,st); stringstream ss; string t; ss << p->val; ss >> t; st += t; Inorder(p->right, st);}
四、在二叉搜索树中查找节点。二叉排序树的查找操作,平均复杂度为O(logn),最差为O(n)。而平衡二叉搜索树能很好的处理最坏情况。常见的平衡二叉搜索树:红黑树等
bool BST::FindNode(const int val){ if (m_root == NULL) { return false; } bool ret = false; Node * cur = m_root; while (cur != NULL) { if (cur->val == val) { m_cur = cur; ret = true; break; } else if (val > cur->val) { cur = cur->right; } else { cur = cur->left; } } return ret;}
五、二叉搜索树删除节点。二叉搜索树不能只是简单的删除某个节点,应该保证BST删除元素后仍然符合二叉搜索树的定义。删除分为三种情况:
1、左子树和右子树均不为空,那么就从右子树中找到一个最小值来替换。
2、仅有一个单个子树,那就将当前元素删除即可
3、没有子树
在编码时一定要注意根节点是特殊情况,根节点没有父节点。
此处代码分为两个步骤,首先找到要替代节点(倘若有),然后替换掉原来的节点。
bool BST::DelNode(const int val){ if (m_root == NULL) { return false; } m_size--; if (!FindNode(val)) { return false; } //难点:找到叶子节点后如何处理父节点,防止父节点中出现野指针 //为方便操作,而返回去增加定义父指针 if (m_cur->left != NULL && m_cur->right != NULL)//有两个子树 { Node * min = m_cur->right; while (min->left != NULL) { min = min->left; } m_cur->right == min ? min->parent->right = NULL : min->parent->left = NULL; SetNode(m_cur,min); delete m_cur; m_cur = NULL; } else if (m_cur->left != NULL || m_cur->right != NULL)//仅有一个子树 { if (m_cur->left != NULL) { SetNode(m_cur, m_cur->left); delete m_cur; m_cur = NULL; } else { SetNode(m_cur, m_cur->right); delete m_cur; m_cur = NULL; } } else //叶子节点、无子树的根节点直接删除 { SetNode(m_cur, NULL); delete m_cur; m_cur = NULL; } return false;}
DelNode方法中调用的SetNode代码实现如下,主要作用是重新设定原来节点周围的父子关系。
总共要设定五条关系
1、当前节点与父节点之间的关系(单边1条)
2、与左子树之间的关系(双边2条)
3、与右子树之间的关系(双边2条)
void BST::SetNode(Node* cur, Node* newNode){ //与父节点 if (cur->parent != NULL) { if (cur->parent->left == cur) { cur->parent->left = newNode; } else { cur->parent->right = newNode; } } else { m_root = newNode; //父节点为空,说明为根节点 } //与左子树 if (cur->left != NULL) { //子->父 cur->left->parent = newNode; //父->子 if (newNode != cur->left) { newNode->left = cur->left; } else { //不操作 } } //右子树 if (cur->right != NULL) { //子->父 cur->right->parent = newNode; if (newNode != cur->right) { newNode->right = cur->right; } else { //不操作 } }}
六、二叉树拓扑结构比较。为了比较两个二叉树是否相同,可采用递归的方式实现,代码如下:
bool BST::CompareTree(const Node *r1,Node* r2) const{ if (r1 == NULL && r2 == NULL) { return true; } if (r1 == NULL || r2 == NULL) { return false; } if (r1->val == r2->val) { if (CompareTree(r1->left, r2->left) && CompareTree(r1->right, r2->right)) { return true; } } return false;}bool BST::operator==(BST &t) const{ return CompareTree(GetRootNode(),t.GetRootNode());}
七、二叉搜索树的销毁。所有节点均使用的new获取的堆空间,因此必须手动释放,否则会造成内存泄漏。释放的方法模仿后序遍历实现。
void BST::DestroyTree(Node *p){ if (p == NULL) { return; } DestroyTree(p->left); DestroyTree(p->right); delete p;}
现在二叉搜索树已经实现,可以通过一道题目来练习。
题目描述
判断两序列是否为同一二叉搜索树序列
输入描述:
开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束。
接下去一行是一个序列,序列长度小于10,包含(0~9)的数字,没有重复数字,根据这个序列可以构造出一颗二叉搜索树。
接下去的n行有n个序列,每个序列格式跟第一个序列一样,请判断这两个序列是否能组成同一颗二叉搜索树。
输出描述:
如果序列相同则输出YES,否则输出NO
输入例子:
2
567432
543267
576342
0
输出例子:
YES
NO
整个工程的全部代码如下:
#include<iostream>#include<stack>#include<vector>#include<queue>#include<map>#include<algorithm>#include<cctype>#include<string>#include<cstring>#include<sstream>//#include<>using namespace std;class BST{private: //嵌套类,仅在BST中有效 class Node { public: int val; Node *left; Node *right; Node *parent; //增加父节点,在删除节点时父节点很有用 Node() { val = 0; left = right = parent = NULL; } }; Node *m_root; //存储根节点 Node *m_cur; //存储查找的结果 int m_size; //存储树的大小 void SetNode(Node* cur,Node* newNode); //设置节点的父子关系 bool CompareTree(const Node *r1,Node* r2) const;public: bool InsertNode(const int val); //插入节点 bool FindNode(const int val); //找到返回true,val所在的位置就是pos bool DelNode(const int val); //删除节点 void Inorder(Node *p, string &str) const; //中序遍历输出参数可自定义 void DestroyTree(Node *p); //销毁整棵二叉树,模仿后序遍历 bool operator==(BST &t) const; //用于判断两个BST是否相等 void CreateBST(const string &str); Node* GetRootNode() const { return m_root; } BST() :m_root(NULL), m_cur(NULL),m_size(0){} ~BST() { DestroyTree(m_root); }};//二叉排序树的查找操作,平均复杂度为O(logn),最差为O(n)。而平衡二叉搜索树能很好的//处理最坏情况。常见的平衡二叉搜索树:红黑树等bool BST::FindNode(const int val){ if (m_root == NULL) { return false; } bool ret = false; Node * cur = m_root; while (cur != NULL) { if (cur->val == val) { m_cur = cur; ret = true; break; } else if (val > cur->val) { cur = cur->right; } else { cur = cur->left; } } return ret;}bool BST::InsertNode(const int val){ Node *p = new Node(); p->val = val; m_size++; if (m_root == NULL) { m_root = p; return true; } Node * cur = m_root; Node * last = m_root; bool lflag = false; while (cur != NULL) { last = cur; if (val >= cur->val) { cur = cur->right; lflag = false; } else { cur = cur->left; lflag = true; } } cur = p; cur->parent = last; lflag == true ? last->left = cur : last->right = cur; return true;}void BST::CreateBST(const string &str){ stringstream ss; int val; for (int i = 0; i < str.size(); i++) { ss << str[i]; ss >> val; ss.clear();//多次转换需要清空 InsertNode(val); }}/*总共要设定五条关系1、当前节点与父节点之间的关系(单边1条)2、与左子树之间的关系(双边2条)3、与右子树之间的关系(双边2条)*/void BST::SetNode(Node* cur, Node* newNode){ //与父节点 if (cur->parent != NULL) { if (cur->parent->left == cur) { cur->parent->left = newNode; } else { cur->parent->right = newNode; } } else { m_root = newNode; //父节点为空,说明为根节点 } //与左子树 if (cur->left != NULL) { //子->父 cur->left->parent = newNode; //父->子 if (newNode != cur->left) { newNode->left = cur->left; } else { //不操作 } } if (cur->right != NULL) { //子->父 cur->right->parent = newNode; if (newNode != cur->right) { newNode->right = cur->right; } else { //不操作 } }}//二叉搜索树不能只是简单的删除某个节点,应该保证删除元素后//仍然符合二叉搜索树的定义/*1、左子树和右子树均不为空,那么就从右子树中找到一个最小值来替换。2、仅有一个单个子树或者没有子树,那就将当前元素删除即可*/bool BST::DelNode(const int val){ if (m_root == NULL) { return false; } m_size--; if (!FindNode(val)) { return false; } //难点:找到叶子节点后如何处理父节点,防止父节点中出现野指针 //为方便操作,而返回去增加定义父指针 if (m_cur->left != NULL && m_cur->right != NULL) { Node * min = m_cur->right; while (min->left != NULL) { min = min->left; } m_cur->right == min ? min->parent->right = NULL : min->parent->left = NULL; SetNode(m_cur,min); delete m_cur; m_cur = NULL; } else if (m_cur->left != NULL || m_cur->right != NULL) { if (m_cur->left != NULL) { SetNode(m_cur, m_cur->left); delete m_cur; m_cur = NULL; } else { SetNode(m_cur, m_cur->right); delete m_cur; m_cur = NULL; } } else //叶子节点、无子树的根节点直接删除 { SetNode(m_cur, NULL); delete m_cur; m_cur = NULL; } return false;}bool BST::CompareTree(const Node *r1,Node* r2) const{ if (r1 == NULL && r2 == NULL) { return true; } if (r1 == NULL || r2 == NULL) { return false; } if (r1->val == r2->val) { if (CompareTree(r1->left, r2->left) && CompareTree(r1->right, r2->right)) { return true; } } return false;}bool BST::operator==(BST &t) const{ return CompareTree(GetRootNode(),t.GetRootNode());}void BST::Inorder(Node *p, string &st) const{ if (p == NULL) { return; } Inorder(p->left,st); stringstream ss; string t; ss << p->val; ss >> t; st += t; Inorder(p->right, st);}void BST::DestroyTree(Node *p){ if (p == NULL) { return; } DestroyTree(p->left); DestroyTree(p->right); delete p;}int main(){ //注释掉的为原始测试代码 //BST tree; //BST tree1,tree2; //string str = "567432";// "567043289"; //string str1 = "543267"; //string str2 = "576342"; //tree.CreateBST(str); //tree1.CreateBST(str1); //tree2.CreateBST(str2); //if (tree2 == tree) //{ // cout << "same tree!!" << endl; //} //str.clear(); //tree.Inorder(tree.GetRootNode(),str); //cout << str << endl; //if(tree.FindNode(4)) //{ // cout << "I find it !!" << endl; //} //else //{ // cout << "I find nothing!" << endl; //} //tree.DelNode(4); //if (tree.FindNode(4)) //{ // cout << "I find it !!" << endl; //} //else //{ // cout << "I find nothing!" << endl; //} //str.clear(); //tree.Inorder(tree.GetRootNode(), str); //cout << str << endl; int n = 0; string str; while (cin >> n >> str) { BST tree; tree.CreateBST(str); while (n--) { string match; cin >> match; BST treeMatch; treeMatch.CreateBST(match); if (tree == treeMatch) { cout << "YES" << endl; } else { cout << "NO"<< endl; } } } return 0;}
写在最后:完整的实现了二叉搜索树后回过来看,在实现删除功能时可以不增加父指针,增加的指针带来了替换操作的复杂度上升。直接导致这部分代码需要更多的时间调试。
- C++一步一步实现二叉搜索树
- [C++] 实现二叉搜索树
- 二叉搜索树 c 语言实现
- 二叉搜索树 C语言实现
- 【C基础】二叉搜索树的实现
- 二叉搜索树(c实现)
- 【C++】实现的二叉搜索树BST
- c语言实现:二叉搜索树
- c语言二叉树和二叉搜索树的实现
- 二叉搜索树(c++)
- 二叉搜索树实现文件C语言(BinarySearchTree.c)
- 二叉搜索树实现
- 搜索二叉树实现
- 搜索二叉树实现
- 二叉搜索树实现
- 3.16(c程序实现)特殊的平衡二叉搜索树之完全二叉搜索树
- 二叉搜索树的基本操作(C代码实现)
- C语言二叉树实现及搜索算法
- c3p0和QueryRunner的结合使用,让开发更加简便
- Nginx服务器架构简析
- tcpdump抓包
- 排序算法:插入排序
- 链表的一些题目
- C++一步一步实现二叉搜索树
- android 短信的接收和发送处理
- windows如何配置java和maven?
- IOS自定义NSLOG和断言的使用/description方法的使用原理(对象,数组,字典)
- 算法与数据结构前绪
- 微信/支付宝支付js
- 欢迎使用CSDN-markdown编辑器
- 鸟哥的linux私房菜学习笔记《二十五》账号管理
- 玄学之坑:RecyclerView: No adapter attached; skipping layout