二叉搜索树是一棵很有启发意义的树,AVL树红黑树都是由他来的。
目录
- 二叉搜索树的概念:
- BSTree的实现:
- 插入:
- 删除:
- 递归版的插入与删除:
- 完整源码:
- 总结:
二叉搜索树的概念:
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
BSTree的实现:
其实他的实现真的很简单,我们先来看节点的定义与私有变量:
节点:
template
struct BinarySearchTreeNode { BinarySearchTreeNode(int key) :_left(nullptr) ,_right(nullptr) ,_key(key) {} typedef BinarySearchTreeNode BSTNode; BSTNode* _left; BSTNode* _right; K _key; }; BSTree的私有变量:
private: Node* _root = nullptr;
插入:
注意:我们不允许插入相同的数字!
整体逻辑是先找到插入的位置,但要注意一下要记录父亲的位置,否则找到时cur为空,没法链接了~
bool Insert(const K& key) { Node* newnode = new Node(key); if (_root == nullptr) { _root = newnode; } 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 { return false; } } // 插入操作 if (parent->_key < key) { parent->_right = newnode; } else { parent->_left = newnode; } return true; }
删除:
删除有一点繁琐,需要将各种情况考虑到位。
- a. 要删除的结点无孩子结点
- b. 要删除的结点只有左孩子结点
- c. 要删除的结点只有右孩子结点
- d. 要删除的结点有左、右孩子结点
那我们针对这几种情况各需要进行怎样的处理呢?
首先a情况是可以归类为b与c,因为叶子节点也符合嘛
那我们现在看看如何处理~
Node* cur = _root; Node* parent = nullptr; while (cur) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } else { // erase // ...删除操作 }
情况a:
情况b与c(因为都只有一个孩子,所以放在一起讨论了):
情况a,b,c的代码
注意:有可能出现单边树的情况,此时需要更新一下root节点的值。
if (cur->_left == nullptr) { if (parent == nullptr) { _root = cur->_right; } else { if (parent->_left == cur) parent->_left = cur->_right; else parent->_right = cur->_right; } } else if(cur->_right == nullptr) { if (parent == nullptr) { _root = cur->_left; } else { if (parent->_left == cur) parent->_left = cur->_left; else parent->_right = cur->_left; } }
情况d:
我们选择左子树最大节点作为替换节点
由于我们还要进行情况b与c(在这里我们不需要进行_root的更新,因为删除的节点肯定不会是_root),所以需要一个父指针指向左子树最大节点的父亲进行链接。
但是要注意,这里的FMaxLeft要自己灵活控制,下面只是一种方式之一,(比如父指针还是给空,但是我们要进行判断一下。)
else { Node* MaxLeft = cur->_left; Node* FMaxLeft = cur->_left; while (MaxLeft->_right) { FMaxLeft = MaxLeft; MaxLeft = MaxLeft->_right; } cur->_key = MaxLeft->_key; del = MaxLeft; if (FMaxLeft == MaxLeft) { cur->_left = nullptr; } else if (FMaxLeft->_left == MaxLeft) { FMaxLeft->_left = MaxLeft->_left; } else { FMaxLeft->_right = MaxLeft->_left; } }
递归版的插入与删除:
注意这里的引用用的很妙,不需要在记录parent指针了
bool _InsertR(Node*& root, const K& key) { if (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 (root->_key > 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*& MaxLeft = root->_left; while (MaxLeft->_right) { MaxLeft = MaxLeft->_right; } swap(root->_key, MaxLeft->_key); _eraseR(MaxLeft, key); } return true; } return false; }
从递归这里我们可以看到很好的利用的引用的特性,不需要额外使用多余的对象去进行记录。
完整源码:
源码附带了析构,拷贝等函数(比较简单就没有写在上边),更加完整
#pragma once #include
using namespace std; template struct BinarySearchTreeNode { BinarySearchTreeNode(int key) :_left(nullptr) ,_right(nullptr) ,_key(key) {} typedef BinarySearchTreeNode BSTNode; BSTNode* _left; BSTNode* _right; K _key; }; template class BinarySearchTree { private: typedef BinarySearchTreeNode Node; public: BinarySearchTree() {} ~BinarySearchTree() { destory(_root); } BinarySearchTree(const BinarySearchTree & BSTree) { _root = copy(BSTree._root); } bool Insert(const K& key) { Node* newnode = new Node(key); if (_root == nullptr) { _root = newnode; } 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 { return false; } } // 插入操作 if (parent->_key < key) { parent->_right = newnode; } else { parent->_left = newnode; } return true; } bool InsertR(const K& key) { return _InsertR(_root, key); } void InOrder() { _InOrder(_root); } bool eraseR(const K& key) { return _eraseR(_root, key); } bool erase(const K& key) { Node* cur = _root; Node* parent = nullptr; while (cur) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } else { // erase Node* del = cur; if (cur->_left == nullptr) { if (parent == nullptr) { _root = cur->_right; } else { if (parent->_left == cur) parent->_left = cur->_right; else parent->_right = cur->_right; } } else if(cur->_right == nullptr) { if (parent == nullptr) { _root = cur->_left; } else { if (parent->_left == cur) parent->_left = cur->_left; else parent->_right = cur->_left; } } else { Node* MaxLeft = cur->_left; Node* FMaxLeft = nullptr; while (MaxLeft->_right) { FMaxLeft = MaxLeft; MaxLeft = MaxLeft->_right; } cur->_key = MaxLeft->_key; del = MaxLeft; if (FMaxLeft == nullptr) { cur->_left = nullptr; } else if (FMaxLeft->_left == MaxLeft) { FMaxLeft->_left = MaxLeft->_left; } else { FMaxLeft->_right = MaxLeft->_left; } } delete del; return true; } } return false; } private: bool _InsertR(Node*& root, const K& key) { if (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 (root->_key > 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*& MaxLeft = root->_left; while (MaxLeft->_right) { MaxLeft = MaxLeft->_right; } swap(root->_key, MaxLeft->_key); _eraseR(MaxLeft, key); } return true; } } Node* copy(Node* root) { if (root == nullptr) { return nullptr; } Node* newroot = new Node(root->_key); newroot->_left = copy(root->_left); newroot->_right = copy(root->_right); return newroot; } void destory(Node* root) { if (root == nullptr) { return; } Node* left = root->_left; Node* right = root->_right; delete root; destory(left); destory(right); } void _InOrder(Node* root) { if (root == nullptr) { return; } _InOrder(root->_left); cout << root->_key << " "; _InOrder(root->_right); } Node* _root = nullptr; }; 总结:
BSTree还是很简单的,但还是要注意细节的把控,尤其是删除的地方,要画图仔细分析一下。
有问题的话欢迎进行讨论哦~
还没有评论,来说两句吧...