今天学习一下非常有用的二叉搜索树!!!
首先看看什么是二叉搜索树,需要满足哪行性质
二叉搜索树的性质:
1. 每个节点都有一个作为搜索依据的关键码(key),所有节点的关键码互不相同。
2. 左子树上所有节点的关键码(key)都小于根节点的关键码(key)。
3. 右子树上所有节点的关键码(key)都大于根节点的关键码(key)。 4. 左右子树都是二叉搜索树
说起来,这种树有什么优点呢?
这里我们要谈到以前学习到 有序的数组 和 无序的链表 这两种结构
数组 可高效的查找
链表 可灵活的插入
但它们都有自身的不足,而二叉搜索树可以将它们俩的优点结合,实现高效的查找和插入删除
初步了解了一下二叉搜索树,那就着手写一颗这样的树
一、二叉树搜索树的创建与插入
首先我们明确树的节点里放关键字key,以及左指针右指针
然后我们想一下算法的思路(插入节点)
1、若为空树,则key作为树的根节点
2、若非空,则key与根节点进行比较
如果key等于根节点,则停止插入;
如果key小于根节点,则向左子树走;
如果key大于根节点,则向右子树走;
#include<iostream>
using namespace std;
template<class K>
class BinarySearchTree
{
typedef TreeNode<K> Node;
public:
BinarySearchTree()
:_root(NULL)
{}
//非递归版插入
bool Insert(const K& key)
{
//若为空,拿key创建一个新节点作为跟
if (_root == NULL)
{
_root = new Node(key);
return true;
}
Node* parent = NULL;//需要一个父节点指针,记录
//方便最后走到空时,能找到父节点进行插入
Node* cur = _root;
while (cur)
{
if (key<cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key>cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
break;
}
}
//走到空了,进行插入节点
//判断左插,还是右插
//要插入的值小于父节点,插入父节点左边
if (key < parent->_key)
{
parent->_left = new Node(key);
}
else if (key>parent->_key)
{
parent->_right = new Node(key);
}
else
{
//值相等,插入失败
return false;
}
}
private:
Node* _root;
};
写好了,插入节点试试
void testBinarySearchTree()
{
BinarySearchTree<int> tree;
tree.Insert(5);
tree.Insert(4);
tree.Insert(3);
}
可以看到已经插入成功了
递归版本
bool _InsertR(Node*& root, const K& key)
{
if (root == NULL)
{
root = new Node(key);
return true;
}
if (key < root->_key)
{
return _InsertR(root->_left, key);
}
else if (key>root->_key)
{
return _InsertR(root->_right, key);
}
else
return false;
}
bool InsertR(const K& key)
{
_InsertR(_root, key);
return true;
}
二、节点删除
我们要删除的节点分为三大类:1、叶子节点(我们可以把这种类型归为第二种情况,叶子结点看为左为空或者右为 空的情况) 2、删除节点的左为空或有为空 3、删除节点左右都不为空
这里我们图示了两种左为空的情况,右为空同理
特殊情况
当要删的为根节点时,直接将右节点给根即可
还有我们要注意这种情况下的parent节点
然后我们再来考虑左右都不为空的情况
这种情况我们就要考虑怎么删了,我们发现当左右都不为空时,我们很难删除这个节点,我们删除这个节点就会牵扯到其他节点改变,可以说“牵一发而动全身”,所以我这里用了一种之前学习过的“假删”,替换法,我们不真删这个节点而是用一个其他节点的值将其进行替换,看起来像删除了
问题一:找谁替换?
删5,可以找4或者6替换
删1,可以用2替换;
删3,可以用2或者4替换
我们可以发现就是要删除节点的左子树的最大值(左子树的最右节点),或者右子树的最小值(右子树的最左节点)
找到了替换节点,那再来分析都有哪些情况?
这里我们以找右子树的最左节点
我们既然要找最左节点,那我们就要考虑有没有左节点,其实就是考虑右子树的最小是有两种情况
如图,我们要删cur,那我们就要找以subRight为根子树的最左节点,可是subRight有可能就没有左节点,(subRight的值就是当前右子树的最小值)那我们就可以直接用subRight的值对cur的值进行替换
然后我们就要考虑这两种情况对于替换后,节点的删除
其实就是我们分别删除5和删除7的两种情况
图示删7的情况
以上我们分析完了所有情况,将其整理,就可以用代码进行实现了