目录

首先写一个模版,然后写一个搜索二叉树的类 BSTree,类里面给 BSTe进行重命名为:Node。
- template<class K>
- class BSTree
- {
- tyepdef BSTree
Node; - private:
- Node* root = nullptr;
- };
再写一个搜二叉的结构体
- template<class K>
- struct BSTreeNode
- {
- BSTreeNode< K>* _left;
- BSTreeNode< K>* _right;
- K _key;
- };
- struct BSTreeNode
- {
- BSTreeNode< K>* _left;
- BSTreeNode< K>* _right;
- K _key;
- BSTree
- :_left(nullptr)
- ,_right(nullptr)
- ,_key(key)
- {
-
- }
- };
如果根节点为空,就开辟一个节点,然后把要插入的值给这个节点:
- bool Insert(const k& key)
- {
- if (_root == nullptr)
- {
- _root = new Node(key);
- return true;
- }
- }
如果跟节点不为空,就按搜索二叉树的性质来插入
即:如果key值比root值大,就把key值插在root节点右边,否则就插在root节点左边。
假如下图这个搜二叉,我们要插入个key=16,该怎么插:
插入的历程如下:
code
- else{
- Node* cur = _root;
- if (cur->_key < key)//如果key大
- {
- cur = cur->_right; //往右走
- }
- else if (cur->_key > key) //如果key小
-
- {
- cur = cur->_left; //往左走
- }
- }
还有一种情况,那就是如果插入值和cur相等呢?
搜二叉不允许节点值相等,所以这就是为什么Insert函数给bool类型的原因。
如果相等就return false;
- else
- {
- Node* cur = _root;
- if (cur->_key < key)
- {
- cur = cur->_right;
- }
- else if (cur->_key > key)
-
- {
- cur = cur->_left;
- }
- else
- {
- return false;
- }
上面的只是走一次,走到哪里结束呢?还需要再加个限制条件,我们让cur走到空的时候就不走了
当while(cur为空就不走了):
- else{
- while (cur)
- {
- Node* cur = _root;
- if (cur->_key < key)
- {
- cur = cur->_right;
- }
- else if (cur->_key > key)
-
- {
- cur = cur->_left;
- }
- else
- {
- return false;
- }
- }
- }
开始插入
在为空的地方开辟一个新节点,把要插入的值push进这个新节点

- else
- {
- while (cur)
- {
- Node* cur = _root;
- if (cur->_key < key)
- {
- cur = cur->_right;
- }
- else if (cur->_key > key)
-
- {
- cur = cur->_left;
- }
- else
- {
- return false;
- }
- }
- cur = new Node(key);
- return true;
- }
ps:
虽然把值push进新节点了,但是此时新节点与搜二叉是一个断层状态,我们还需要把它们链起来。
可以弄一个快慢指针,cur先走,parent再走,当cur走完了,parent正好是cur的父亲节点,再把他俩链起来。
- Node* cur = _root;
- Node* parent = nullptr;
- else
- {
- while (cur)
- {
- parent = cur;
- if (cur->_key < key)
- {
- cur = cur->_right;
- }
- else if (cur->_key > key)
-
- {
- cur = cur->_left;
- }
- else
- {
- return false;
- }
- }
- cur = new Node(key);
- if (cur->key < parent->_key)cur = parent->left;
- else if (cur->key > parent->_key)cur = parent->_right;
- return true;
- }
写一个中序来遍历
- void Inorder(Node * root)
- {
- if (root == nullptr)return;
- Inorder(root->_left);
- cout << root->_key << " ";
- Inorder(root->_right);
- }cout << endl;
写完了我们现在要调用Inorder
- main
-
- BSTree<int> bt;
- bt.Inorder();
递归调用需要传参,我们应该传个root给inorder()。但是我们在main函数里拿不到root,列如:

方法一,写一个getroot函数,返回root,我们调用getroot就行了:
- Node* getroot()
- {
- return root;
- }
- main
-
-
- bt.Inorder(bt.getroot());
方法2:我们外层写一个递归INORDER(),里面嵌套我们的_INORDER(),以此实现递归:
- void _Inorder(Node * root)
- {
- if (root == nullptr)return;
- _Inorder(root->_left);
- cout << root->_key << " ";
- _Inorder(root->_right);
- }cout << endl;
-
-
- void Inorder()
- {
- _Inoreder(_root);
-
- }
- main
-
-
-
- bt.Inorder( );

查找key值,如果key比cur大就往右找,否则往左找,如果找到节点为空都找不到,返回false。如果找到了,返回true
- bool Find(const K& key)
- {
- Node* cur = _root;
- if (_root == nullptr)return;
- if (cur->_key < key)
- {
- cur = cur->right;
- }
- else if (cur->_key > key)
- {
- cur = cur->left;
- }
- else
- {
- //找遍了就是没找到,return false;
- return false;
- }
- return true;//否则找到节点为空就是找到了,return true;
- }
我们都知道叶子节点最好删除,只要置空,然后free掉就可以了,如下图:

假设我们要删除根节点呢?
根据之前写二叉树的经验,根节点不能直接删除,再把其他节点挪上来,否则会改变树的结构。
而根节点处理好了,删除其他节点都可以按一样的方法进行处理。
我们可以用替换法,就是找一个节点与要交换的节点进行交换,但是交换之后仍然要保证搜二叉地的特性。
比如我可以把值为8节点和值为7节点进行交换:
也可以把值为8节点与值为10节点交换:
我们发现规律:与左子树最大的值的节点交换/与右子树最小的值的节点交换。
然后再置空,free:
注意点:
先写出框架:
- bool Erase(const K& key )
- {
- Node* parent = nullptr; //初始化
- Node* cur = root;
-
- //要删除数如果大于root就往右走
- if (cur->_key < key)
- {
- cur = cur->right;
- }
-
- //要删除数如果大于root就往左走
- else if (cur->_key > key)
- {
- cur = cur->left;
- }
- else
- {
- //走到空,相等了,相等就是找到了
- //找到了就准备删除
- }
-
-
- }
然后来写删除的具体步骤:
我们把8删掉了,就断层了:
我们需要重新链起来,可以这样写:
key->right=root->right
但是这样不完善,因为万一被删除的节点左边还有节点呢?
所以还要写一个
key->left=root->right

code
- Node* parent = nullptr; //初始化
- Node* cur = root;
-
- if (cur->left == nullptr)
- {
- if (cur = parent->left)
- {
- parent->left = cur->right;
- }
- else if(cur = parent->right)
- {
- parent->right = cur->right;
- }
- }
- else if (cur->right == nullptr)
- {
-
- if (cur = parent->left)
- {
- parent->left = cur->left;
- }
- else if (cur = parent->right)
- {
- parent->right = cur->left;
- }
- }
诈一看,好像没什么大问题,但实际上有纰漏。代码漏了一种情况,那就是下图这种情况:
cur=8=root=key,8就是我们要删除的节点,8没有parent。
所以还需要加个条件:
- if (cur == parent->left)
- {
- cur = cur->right;
- }

- if (cur = parent->left)
- {
- parent->left = cur->left;
- }

还有一种情况,就是左右子树都不为空,例如3节点:
现在我们要删除3节点怎么删:
还是上面的方法,要么找右子树的最左节点(也就是右子树最小节点)或者找左子树最由节点(也就是左子树最大节点),然后进行替换。
拿找右子树的最左节点举例:
先向右走
Node* subleft = cur->_right;

然后再判断右子树的左分支子树是否为空,不为空就往左走,走到空为止:
- else
- {
- Node* parent = cur;
- Node* subleft = cur->_right;
- while (subleft->left)
- {
- parent = subleft;
- subleft = subleft->_left;
- }
- }

找到节点之后进行交换:

- swap(cur->_key, subLeft->_key);
-
- if (subLeft == parent->_left)
- parent->_left = subLeft->_right;
- else
- parent->_right = subLeft->_right;
- }
然后return true显示找到了值并且删除了:
return true;
整个删除就结束了,如果没有找到并且删除就return fales;
return false;
Erase完整code
-
-
-
-
- bool Erase(const K& key)
- {
- Node* parent = nullptr;
- Node* cur = _root;
- while (cur)
- {
- if (cur->_key < key)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (cur->_key > key)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- // 准备删除 20:15继续
- if (cur->_left == nullptr)
- {//左为空
- if (cur == _root)
- {
- _root = cur->_right;
- }
- else
- {
- if (cur == parent->_left)
- {
- parent->_left = cur->_right;
- }
- else
- {
- parent->_right = cur->_right;
- }
- }
- }
- else if (cur->_right == nullptr)
- {//右为空
- if (cur == _root)
- {
- _root = cur->_left;
- }
- else
- {
- if (cur == parent->_left)
- {
- parent->_left = cur->_left;
- }
- else
- {
- parent->_right = cur->_left;
- }
- }
- }
- else
- {//左右都不为空
-
- // 右树的最小节点(最左节点)
- Node* parent = cur;
- Node* subLeft = cur->_right;
- while (subLeft->_left)
- {
- parent = subLeft;
- subLeft = subLeft->_left;
- }
-
- swap(cur->_key, subLeft->_key);
-
- if (subLeft == parent->_left)
- parent->_left = subLeft->_right;
- else
- parent->_right = subLeft->_right;
- }
-
- return true;
- }
- }
-
- return false;
- }
删除一下看看:
- bt.Inorder();
- printf("\n");
-
- bt.Erase(3);
- bt.Inorder( );
- printf("\n");
-
-
- bt.Erase(14);
- bt.Inorder();
- printf("\n");
改的话目前不能改,如我们把3改为80,那就改变搜二叉的结构了:
