提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 一、搜索二叉树概念
- 二、搜索二叉树的操作
- 1.插入
- 2. 查找
- 3. 中序遍历
- 4. 删除
- 三、默认成员函数
- 1.析构函数
- 2.拷贝构造
- 3. 赋值运算符重载
- 四、完整代码
一、搜索二叉树概念
搜索二叉树
也叫做二叉排序树,它要么是一棵空树,要么具有以下性质:
(1)若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
(2)若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
(3)它的左右子树也分别为二叉搜索树
如下就是搜索二叉树,对于任何一个节点,它的左子树的所有节点值都比它小,它的右子树的所有节点值都比它大:
int a[] = {9,6,15,4,13,5,1,20,8,27};
总结:在左子树值比根小,右子树值比根大。 当树走中序遍历时,序列都是有序的。
搜索二叉树的结构定义:
//定义树的结点
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;//构造函数BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};//树结构
template<class K>
class BStree
{typedef BSTreeNode<K> Node;
public://构造函数只需要将根初始化为空就行了BSTree():_root(nullptr){}private:Node* _root;//根
};
提示:以下是本篇文章正文内容,下面案例可供参考
二、搜索二叉树的操作
1.插入
插入节点分两步:
(1)找位置
①key比当前节点值大,向右走②key比当前节点值小,向左走③key等于当前节点值,该节点值已经存在,插入失败
(2)插入
①key比父亲节点值小就插入父亲左子树②key比父亲节点值大就插入父亲右子树
由于插入后,要将节点链接到树中,因此要定义parent节点,用来链接新节点:
bool Insert(const K& key){//空树 -》 直接插入if (_root == nullptr){_root = new Node(key);}//寻找插入位置Node* cur = _root;Node* parent = cur;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}//搜索二叉树中不允许出现相同结点else{return false;}}//找到了插入节点的位置了-》判断插入节点与parent->_key的大小,判断插入到左子树还是右子树if (parent->_key > key){parent->_left = new Node(key);}else{parent->_right = new Node(key);}return true;}//递归插入 形成二叉树
bool _InsertR(Node*& _root, const K& key){//说明找到插入节点的位置了 / 树为NULLif (_root == nullptr){_root = new Node(key);return true;}if (_root->_key > key){return _InsertR(_root->_left, key);}else if (_root->_key < key){return _InsertR(_root->_right, key);}else return false;}
2. 查找
查找比较简单:
①key比当前节点值大,向右走②key比当前节点值小,向左走③key等于当前节点值,找到了
//查找 迭代Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn cur;}return cur;}//查找递归版本Node* _FindR(Node* _root, const K& key){if (_root == nullptr) return _root;if (_root->_key > key) return _FindR(_root->_left, key);else if(_root->_key < key) return _FindR(_root->_right, key);else return _root;}
3. 中序遍历
由于根节点的访问限定符是私有的,那么在main函数中要终须遍历一棵树时,就无法将二叉搜索树的对象根节点传给中序遍历,因为类外面访问不到私有成员。
因此可以这样考虑:这个搜索二叉树对象是有根节点的,可以考虑在里面套一层,给套在里面的函数传参_root ,去递归调用自己:
//内层函数使用_rootvoid _Inorder(Node* root){if (root == nullptr){return;}_Inorder(root->_left);//递归调用自己cout << root->_key << " ";_Inorder(root->_right);//递归调用自己}//先调不传参数的InOrdervoid InOrder(){//把_root传给子函数,让子函数去使用_root_InOrder(_root);cout << endl;}
4. 删除
(1)找位置
①key比当前节点值大,向右走②key比当前节点值小,向左走③key等于当前节点值,找到了,准备删除
(2)删除,有两种删除方法:非递归和递归
非递归删除:
①该节点没有孩子,即该节点是叶子节点,删除节点后把父亲指向自己的指针置空
②该节点有一个孩子,就把该节点的孩子节点的链接给该节点的父亲,顶替自己的位置,①可以当成②的特殊情况
③该节点有两个孩子,找比它自己的左孩子大,比它自己的右孩子小的节点替换它(也就是拿它的左子树的最大节点或右子树的最小节点替换它),替换之后,该节点就只有一个孩子或没有孩子了,就变成①或②了。
//删除的非递归版本bool Erase(const K& key){Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else //找到结点 -》 删除结点{// 删除结点的左子树为空 if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}//删除结点的右子树为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_right;}}delete cur;}//删除节点的左右子树都不为空else{//寻找右子树的最小结点 最左Node* MinRightNode = cur->_right;Node* MinParentNode = cur;while (MinRightNode->_left){MinParentNode = MinRightNode;MinRightNode = MinRightNode->_left;}//找到了右子树的最小结点swap(MinRightNode->_key, cur->_key);if (MinParentNode->_left == MinRightNode){MinParentNode->_left = MinRightNode->_right;}else{MinParentNode->_right = MinRightNode->_right;}delete MinRightNode;}return true;}}return false;}//递归删除bool _EraseR(Node*&_root, const K& key){if (_root == nullptr)return false;if (key < _root->_key){return _EraseR(_root->_left, key);}else if (_root->_key < key){return _EraseR(_root->_right, key);}else{//删除// 左子树为空 if (_root->_left == nullptr){Node* del = _root;_root = _root->_right;delete del;}//右子树为空else if (_root->_right == nullptr){Node* del = _root;_root = _root->_left;delete del;}//左右子树都不为空else{//寻找右子树的最小结点 最左Node* MinRightNode = _root->_right;Node* MinParentNode = _root;while (MinRightNode->_left){MinParentNode = MinRightNode;MinRightNode = MinRightNode->_left;}//找到了右子树的最小结点swap(MinRightNode->_key, _root->_key);if (MinParentNode->_left == MinRightNode){MinParentNode->_left = MinRightNode->_right;}else{MinParentNode->_right = MinRightNode->_right;}delete MinRightNode;}return true;}}
三、默认成员函数
1.析构函数
递归调用子函数去析构
~BSTree(){Destory(_root);_root = nullptr;}void Destory(Node* _root){if (_root == nullptr) return;Destory(_root->_left);Destory(_root->_right);delete _root;}//中序遍历void _InOrder(Node* _root){if (_root == nullptr) return;_InOrder(_root->_left);cout << _root->_key << " ";_InOrder(_root->_right);}
2.拷贝构造
拷贝构造利用递归调用子函数不断拷贝节点:
//拷贝构造BSTree(const BSTree<K>& t){_root = t.copy(t._root);}
在子函数处:
Node* _copy(Node* root){if (root == nullptr)//如果根为空,直接返回{return;}Node* copyNode = new Node(root->_key);//创建根节点copyNode->_left = _copy(root->_left);//递归拷贝左子树节点copyNode->_right = _copy(root->_right);//递归拷贝右子树节点return copyNode;//返回根}
3. 赋值运算符重载
使用节点互换的逻辑
BSTree<K>& operator=(BSTree<K> t) //t这里调用的拷贝构造-》深拷贝{swap(_root, t._root);Destory(t._root);t._root = nullptr;return *this;}
四、完整代码
// .h 文件#pragma once
#include <iostream>
using namespace std;
template <class K>
//定义树的结点
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;//构造函数BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};template <class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:BSTree() = default;//插入 形成搜索二叉树 迭代版本bool Insert(const K& key){//空树 -》 直接插入if (_root == nullptr){_root = new Node(key);}//寻找插入位置Node* cur = _root;Node* parent = cur;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}//搜索二叉树中不允许出现相同结点else{return false;}}//找到了插入节点的位置了-》判断插入节点与parent->_key的大小,判断插入到左子树还是右子树if (parent->_key > key){parent->_left = new Node(key);}else{parent->_right = new Node(key);}return true;}//递归插入 形成二叉树bool InsertR(const K& key){return _InsertR(_root, key);}bool Erase(const K& key){Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else //找到结点 -》 删除结点{// 删除结点的左子树为空 if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}//删除结点的右子树为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_right;}}delete cur;}//删除节点的左右子树都不为空else{//寻找右子树的最小结点 最左Node* MinRightNode = cur->_right;Node* MinParentNode = cur;while (MinRightNode->_left){MinParentNode = MinRightNode;MinRightNode = MinRightNode->_left;}//找到了右子树的最小结点swap(MinRightNode->_key, cur->_key);if (MinParentNode->_left == MinRightNode){MinParentNode->_left = MinRightNode->_right;}else{MinParentNode->_right = MinRightNode->_right;}delete MinRightNode;}return true;}}return false;}//递归删除bool EraseR(const K& key){return _EraseR(_root, key);}//查找 迭代Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn cur;}return cur;}//查找->递归版本Node* FindR(const K& key){return _FindR(_root, key);}//中序打印void InOrder(){_InOrder(_root);cout << endl;}BSTree(const BSTree<K>& t){_root = Copy(t._root);}~BSTree(){Destory(_root);_root = nullptr;}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);Destory(t._root);t._root = nullptr;return *this;}private://直接给缺省值,调用BSTree默认构造就不用初始化了Node* _root = nullptr;Node* Copy(const Node* _root){if (_root == nullptr) return nullptr;//拷贝根节点 左子树 右子树Node* newnode = new Node(_root->_key);newnode->_left = Copy(_root->_left);newnode->_right = Copy(_root->_right);return newnode;}void Destory(Node* _root){if (_root == nullptr) return;Destory(_root->_left);Destory(_root->_right);delete _root;}//中序遍历void _InOrder(Node* _root){if (_root == nullptr) return;_InOrder(_root->_left);cout << _root->_key << " ";_InOrder(_root->_right);}//递归插入 !!!Node*& _root 就表示当前节点的引用,我直接让_root直接等于new Node(key),直接链接bool _InsertR(Node*& _root, const K& key){//说明找到插入节点的位置了 / 树为NULLif (_root == nullptr){_root = new Node(key);return true;}if (_root->_key > key){return _InsertR(_root->_left, key);}else if (_root->_key < key){return _InsertR(_root->_right, key);}else return false;}//递归删除bool _EraseR(Node*&_root, const K& key){if (_root == nullptr)return false;if (key < _root->_key){return _EraseR(_root->_left, key);}else if (_root->_key < key){return _EraseR(_root->_right, key);}else{//删除// 左子树为空 if (_root->_left == nullptr){Node* del = _root;_root = _root->_right;delete del;}//右子树为空else if (_root->_right == nullptr){Node* del = _root;_root = _root->_left;delete del;}//左右子树都不为空else{//寻找右子树的最小结点 最左Node* MinRightNode = _root->_right;Node* MinParentNode = _root;while (MinRightNode->_left){MinParentNode = MinRightNode;MinRightNode = MinRightNode->_left;}//找到了右子树的最小结点swap(MinRightNode->_key, _root->_key);if (MinParentNode->_left == MinRightNode){MinParentNode->_left = MinRightNode->_right;}else{MinParentNode->_right = MinRightNode->_right;}delete MinRightNode;}return true;}}//查找递归版本Node* _FindR(Node* _root, const K& key){if (_root == nullptr) return _root;if (_root->_key > key) return _FindR(_root->_left, key);else if(_root->_key < key) return _FindR(_root->_right, key);else return _root;}
};// .cpp 文件#include"BinarySearchTree.h"int main()
{BSTree<int> root;int Tree[] = { 7,2,4,6,3,1,5};for (int i = 0; i < (sizeof(Tree) / sizeof(Tree[0])); i++){root.InsertR(Tree[i]);}root.InOrder();BSTree<int> root1 = root;root1.InOrder();}