C++之二叉搜索树的模拟
二叉搜索树的模拟实现
节点结构
分为左指针,右指针和数据域
除此之外我们还需要进一步写一个构造函数,让新生成的节点可以使用,要进行初始化,比如指针置空和数据的拷贝,为了其多样性我们应该要用模板。
template <class K> struct BSTNode { K _key; BSTNode<K>* _left; BSTNode<K> * _right; //构造函数 BSTNode(const K& key) :_key(key) ,_left(nullptr) ,_right(nullptr) { } };
树的结构
运用原来的节点,避免忘记传入类型,将根节点置空。
insert插入操作
在执行插入操作的时候,cue直接new一个,没办法与上面的parent节点产生链接,也就是说,有parent但是找不到cur的位置,所以我们要记录cur的父亲节点。
bool Insert(const K& key) { if (_root == nullptr) { _root = new Node(key); return true; } //可以用递归也可以不用 Node* cur= _root; Node* parent = nullptr; while (cur!=nullptr) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } //tree是否可以插入相同的值 else return false; } cur = new Node(key); if (cur->_key < parent->_key) { parent->_left = cur; } else { parent->_right = cur; } return true; }
中序遍历(非递归实现)
到实践的时候,发现传参很不方便,因为节点是私有的,在类外不能进行访问
可以采用下面方法
private中序遍历的子函数,用public函数调用private子函数就可以完成实现
//中序遍历 void Inorder() { _Inorder(_root); cout << endl; } private: void _Inorder(Node* root) { if (root == nullptr) return; _Inorder(root->_left); cout << root->_key<<" "; _Inorder(root->_right); }
find
与插入的逻辑很相似,在insert函数稍加修改即可
bool find (const K& key) { Node* cur = _root; while (cur != nullptr) { if (cur->_key < key) { cur = cur->_right; } else if (cur->_key > key) { cur = cur->_left; } else { //cout << "找到了" << key << endl; return true; } } //cout << "没找到" << endl; return false; }
erase
删除操作是里面最难的操作要分析多个条件
1.删除的是叶子节点或者是只有一个孩子的节点相对而言较为简单,将节点删除,parent节点left或者right置nullptr,有一个孩子的需要将他的parent连接到该节点的孩子
2.接下来是相对麻烦的,删除的节点同时拥有左孩子和右孩子。想要让左孩子的最大值或者有孩子的最小值来代替这个节点
第二种的情况更加复杂
如果以上面的方法来做,这种情况可以解决。
但是对于这种情况就会失效
要注意根的位置
这样对上面情况的分析也是要考虑根的情况,不然会有空指针的解引用问题
//删除 bool Erase(const K& key) { Node* cur = _root; Node* parent = nullptr; while (cur != nullptr) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } else //找到了该节点的位置 { if (cur->_left == nullptr && cur->_right == nullptr) { if (cur != _root) { //左右孩子都为空时 if (parent->_key > cur->_key) parent->_left = nullptr; else parent->_right = nullptr; delete cur; return true; } else { delete cur; } } else if (cur->_left == nullptr) { if (cur != _root) { //cur只有右孩子 if (parent->_left == cur) parent->_left = cur->_right; else parent->_right = cur->_right; delete cur; return true; } else { _root = cur->_right; } } else if (cur->_right == nullptr) { if (cur != _root) { //cur只有左孩子 if (parent->_left == cur) parent->_left = cur->_left; else parent->_right = cur->_left; delete cur; return true; } else { _root = cur->_left; } } else { //cur既有左孩子又有右孩子 //target找到左孩子最大,或者右孩子的最小,这里我们选择找左孩子最大 //cur和target的key交换,删除target的节点,target的parent节点置空。 Node* maxleft = cur->_left; Node* maxleftparent = cur; while (maxleft->_right != nullptr) { maxleftparent = maxleft; maxleft = maxleft->_right; } cur->_key = maxleft->_key; if(maxleft==maxleftparent->_left) { maxleftparent->_left = maxleft->_left; } else { maxleftparent->_right = maxleft->_left; } delete maxleft; } } } return false; }
插入insert(递归实现)
与非递归实现相类似也是需要解决传参不方便的问题。
与非递归不同,参数还要多传入一个节点,
先一直递归,然后找到插入的位置,new一个节点给root
走到最后递归出口是root为nullptr
但是问题是如何能让root,new一个新的节点的时候与之前的父亲节点产生联系。
重点就是在于引用,让root的引用可以引起全局的变化,就和传值返回和传址返回的区别一样。
void insert(const K& key) { _insert(_root,key); } private: bool _insert(Node* root,const K& key) { if (root == nullptr) { root = new Node(key); return true; } if (root->_key < key) return _insert(root->_right, key); else if (root->_key > key) return _insert(root->_left, key); else return false; }
#pragma once #include<iostream> using namespace std; template <class K> struct BSTNode { K _key; BSTNode<K>* _left; BSTNode<K> * _right; //构造函数 BSTNode(const K& key) :_key(key) ,_left(nullptr) ,_right(nullptr) { } }; template<class K> class BSTree { typedef BSTNode<K> Node; public: bool Insert(const K& key) { if (_root == nullptr) { _root = new Node(key); return true; } //可以用递归也可以不用 Node* cur= _root; Node* parent = nullptr; while (cur!=nullptr) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } //tree是否可以插入相同的值 else return false; } cur = new Node(key); if (cur->_key < parent->_key) { parent->_left = cur; } else { parent->_right = cur; } return true; } //删除 bool Erase(const K& key) { Node* cur = _root; Node* parent = nullptr; while (cur != nullptr) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } else //找到了该节点的位置 { if (cur->_left == nullptr && cur->_right == nullptr) { if (cur != _root) { //左右孩子都为空时 if (parent->_key > cur->_key) parent->_left = nullptr; else parent->_right = nullptr; delete cur; return true; } else { delete cur; } } else if (cur->_left == nullptr) { if (cur != _root) { //cur只有右孩子 if (parent->_left == cur) parent->_left = cur->_right; else parent->_right = cur->_right; delete cur; return true; } else { _root = cur->_right; } } else if (cur->_right == nullptr) { if (cur != _root) { //cur只有左孩子 if (parent->_left == cur) parent->_left = cur->_left; else parent->_right = cur->_left; delete cur; return true; } else { _root = cur->_left; } } else { //cur既有左孩子又有右孩子 //target找到左孩子最大,或者右孩子的最小,这里我们选择找左孩子最大 //cur和target的key交换,删除target的节点,target的parent节点置空。 Node* maxleft = cur->_left; Node* maxleftparent = cur; while (maxleft->_right != nullptr) { maxleftparent = maxleft; maxleft = maxleft->_right; } cur->_key = maxleft->_key; if(maxleft==maxleftparent->_left) { maxleftparent->_left = maxleft->_left; } else { maxleftparent->_right = maxleft->_left; } delete maxleft; } } } return false; } bool find (const K& key) { Node* cur = _root; while (cur != nullptr) { if (cur->_key < key) { cur = cur->_right; } else if (cur->_key > key) { cur = cur->_left; } else { //cout << "找到了" << key << endl; return true; } } //cout << "没找到" << endl; return false; } //中序遍历 void Inorder() { _Inorder(_root); cout << endl; } void insert(const K& key) { _insert(_root,key); } private: bool _insert(Node*& root,const K& key) { if (root == nullptr) { root = new Node(key); return true; } if (root->_key < key) return _insert(root->_right, key); else if (root->_key > key) return _insert(root->_left, key); else return false; } void _Inorder(Node* root) { if (root == nullptr) return; _Inorder(root->_left); cout << root->_key << " "; _Inorder(root->_right); } private: Node* _root=nullptr; };
二叉搜索树的应用
1.只有key作为关键码<set>,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。key的搜索场景实现的二叉树搜索树支持增删查,但是不支持修改,修改key破坏搜索树结构了。比如小区车库识别,然后抬杆的操作,还有检查单词是否拼写错误。
2.每一个关键码key<map>,都有与之对应的值value,value可以任意类型对象。树的结构中(结点)除了需要存储key还要存储对应的value,增/删/查还是以key为关键字走二叉搜索树的规则进行比较,可以快速查找到key对应的value。key/value的搜索场景实现的二叉树搜索树支持修改,但是不支持修改key,修改key破坏搜索树性质了,可以修改value。比如看一个词在文章里面的出现次数,翻译词典中英互译。
key/value二叉搜索树代码实现
这个修改相对比较简单,多添加一个模板参数
然后在节点,和树那里修改,new一个节点也要加
对find稍加修改
#pragma once #include<iostream> using namespace std; template <class K,class V> struct BSTNode { K _key; V _value; BSTNode<K,V>* _left; BSTNode<K,V> * _right; //构造函数 BSTNode(const K& key,const V& value) :_key(key) ,_value(value) ,_left(nullptr) ,_right(nullptr) { } }; template<class K, class V> class BSTree { typedef BSTNode<K, V> Node; public: bool Insert(const K& key,const V& value) { if (_root == nullptr) { _root = new Node(key,value); return true; } //可以用递归也可以不用 Node* cur= _root; Node* parent = nullptr; while (cur!=nullptr) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } //tree是否可以插入相同的值 else return false; } cur = new Node(key,value); if (cur->_key < parent->_key) { parent->_left = cur; } else { parent->_right = cur; } return true; } //删除 bool Erase(const K& key) { Node* cur = _root; Node* parent = nullptr; while (cur != nullptr) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } else //找到了该节点的位置 { if (cur->_left == nullptr && cur->_right == nullptr) { if (cur != _root) { //左右孩子都为空时 if (parent->_key > cur->_key) parent->_left = nullptr; else parent->_right = nullptr; delete cur; return true; } else { delete cur; } } else if (cur->_left == nullptr) { if (cur != _root) { //cur只有右孩子 if (parent->_left == cur) parent->_left = cur->_right; else parent->_right = cur->_right; delete cur; return true; } else { _root = cur->_right; } } else if (cur->_right == nullptr) { if (cur != _root) { //cur只有左孩子 if (parent->_left == cur) parent->_left = cur->_left; else parent->_right = cur->_left; delete cur; return true; } else { _root = cur->_left; } } else { //cur既有左孩子又有右孩子 //target找到左孩子最大,或者右孩子的最小,这里我们选择找左孩子最大 //cur和target的key交换,删除target的节点,target的parent节点置空。 Node* maxleft = cur->_left; Node* maxleftparent = cur; while (maxleft->_right != nullptr) { maxleftparent = maxleft; maxleft = maxleft->_right; } cur->_key = maxleft->_key; if(maxleft==maxleftparent->_left) { maxleftparent->_left = maxleft->_left; } else { maxleftparent->_right = maxleft->_left; } delete maxleft; } } } return false; } Node* find (const K& key) { Node* cur = _root; while (cur != nullptr) { if (cur->_key < key) { cur = cur->_right; } else if (cur->_key > key) { cur = cur->_left; } else { //cout << "找到了" << key << endl; return cur; } } //cout << "没找到" << endl; return nullptr; } //中序遍历 void Inorder() { _Inorder(_root); cout << endl; } /*void insert(const K& key) { _insert(_root,key); }*/ private: /*bool _insert(Node*& root,const K& key) { if (root == nullptr) { root = new Node(key); return true; } if (root->_key < key) return _insert(root->_right, key); else if (root->_key > key) return _insert(root->_left, key); else return false; }*/ void _Inorder(Node* root) { if (root == nullptr) return; _Inorder(root->_left); cout << root->_key << " " << root->_value << endl;; _Inorder(root->_right); } private: Node* _root=nullptr; };
至此二叉搜索树的模拟实践到此结束
更多推荐





























所有评论(0)