“折半查找”这个词相信大家在学习C语言的时候已经很熟悉,由于数组中数字的排列是有序的,那么我们就可以将要查找的数字与最中间的数进行比较,每次比较都可以把范围缩小一半,依次下去,很好的提高了查找效率。而在数据结构中,我们也有处理这种问题的对应模型------二叉搜索树(也称为二叉排序树)。
二叉搜索树的性质:
(1)每个节点都有一个作为搜索依据的关键码(key),所有节点的关键码互不相同
(2)左子树上所有节点的关键码(key)都小于根节点的关键码(key)
(3)右子树上所有节点的关键码(key)都大于根节点的关键码(key)
(4)左右子树都是二叉搜索树
以下面这颗树为例:
二叉搜索树节点的定义:
struct SearchBinaryTreeNode
{
SearchBinaryTreeNode<K>* _left;
SearchBinaryTreeNode<K>* _right;
K _key;
//节点的初始化
SearchBinaryTreeNode(const K&k)
:_left(NULL)
, _right(NULL)
, _key(k)
{}
};
插入:
这里说一下插入的思路:分两种情况讨论---
(1)如果本来就是一颗空树,则直接插入
(2)树不为空,则将待插元素与根节点的值进行比较,若大于根节点则移向右子树,再与右子树的根节点进行比较,依次下去,直到找到合适的插入位置。。。小于根节点同理
还是直接上图帮助理解:(就是上面那棵树,插入12)
空树比较简单,直接new一个节点插入就好;这里着重讲一下树不为空的情况-->
(1)如图,首先定义一个cur指针,最初指向根节点,每一次比较,cur指针都进行一次移动(上面蓝色箭头和圈的序号表示移动的位置和次序);
(2)12比5大,则说明在5的右子树,于是cur向右移,移到7的位置;12比7大,,说明在7的右子树,则cur再向右移,移到8的位置;以此类推,当cur移到9所在位置时,12还比9大,于是cur再向右移,这时cur为空,因为9已经是叶子节点了。
到这里,最关键的一步就来了:cur这时已经为空,那么如何把12链到9的右边?可见,定义一个指针远远不够,必须再定义一个parent指针,用来保存cur的路径(即cur每走一步,都要先赋给parent),这样最终parent就指向9,把12直接链上去就好。
附上代码:
bool Insert(const K&key)
{
if (_root == NULL)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node*parent = cur;
while (cur)
{
if (key>cur->_key)//插入节点的数值比当前节点大,则cur向右子树移动
{
parent = cur;
cur = cur->_right;
}
else if (key<cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;//要插入的节点元素值与这棵树中的节点元素值相等,不能插入
}
}
//到这里cur已经指向了叶子节点的下一步,需要将插入值与叶子节点进行比较,来确定插在叶子节点的左子树还是右子树
if (key>parent->_key)
{
parent->_right = new Node(key);
}
else
{
parent->_left = new Node(key);
}
return true;
}
删除:
删除比较复杂,我们从最简单的情况入手:
(1)首先,如果树只有一个根节点--->直接删除,并置空
(2)删除的是叶子节点--->删除后,还需考虑父节点指向其的指针要置空
接下来就是较复杂的情况:非叶子节点(又可以分为3种情况)
(1)左为空,右不为空
(2)左不为空,右为空
(3)左、右都不为空
再仔细想,我们可以把叶子节点和非叶子节点的(1)和(2)合并,那么问题就简单化了
综上,归纳总结如下:
重点:替换法删除(左、右都不为空的情况)
这里再来说一个值得关注的点,左为空或者右为空的情况需不需要替换法?(我之前就把问题复杂化了)
可以看出,替换和不替换的中序遍历结果都一样,且都符合二叉搜索树的条件,所以这里不需要使用替换法删除。
附上代码:
bool Remove(const K&key)
{
Node*parent = NULL;
Node*cur = _root;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else//已找到要删除的节点
{
Node*del = cur;
if (cur->_left == NULL)//cur的左为空(右为空或右不为空)
{
if (parent == NULL)//若删除的是根节点,则再赋予新的根节点
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)//cur在parent的左边
{
parent->_left = cur->_right;//如果是叶子节点则这句的作用为:将parent的左置空;如果右边还有子树则这句的作用为:将parent的左指针指向子树
}
else//cur在parent的右边
{
parent->_right = cur->_right;//如果是叶子节点则这句的作用为:将parent的右置空;如果右边还有子树则这句的作用为:将parent的右指针指向子树
}
}
}
else if (cur->_right == NULL)//cur的右为空(左为空或左不为空)
{
if (parent == NULL)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)//cur在parent的左边
{
parent->_left == cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
}
else//cur的左,右都不为空
{
parent = cur;
Node*subleft = cur->_right;
//寻找右子树的最左节点,替换法删除
while (subleft->_left)
{
parent = subleft;
subleft = subleft->_left;
}
del = subleft;
cur->_key = subleft->_key;//替换
if (parent->_left == subleft)//右子树的最左节点存在
{
parent->_left = subleft->_right;
}
else//右子树的最左节点就是右子树的根
{
parent->_right = subleft->_right;
}
}
delete del;
return true;
}
}
return false;
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
最后附上所有源代码:
//SearchBinaryTree.h
#include<iostream>
using namespace std;
template<class K>
//定义二叉搜索树节点
struct SearchBinaryTreeNode
{
SearchBinaryTreeNode<K>* _left;
SearchBinaryTreeNode<K>* _right;
K _key;
//节点的初始化
SearchBinaryTreeNode(const K&k)
:_left(NULL)
, _right(NULL)
, _key(k)
{}
};
//定义二叉搜索树
template<class K>
class SearchBinaryTree
{
typedef SearchBinaryTreeNode<K> Node;
public:
SearchBinaryTree()
:_root(NULL)
{}
~SearchBinaryTree()
{
Destory(_root);
}
//插入
bool Insert(const K&key)
{
if (_root == NULL)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node*parent = cur;//这里之所以要定义parent指针,是因为要记录cur走的路径。每比较一次,cur指针移动一步,cur每次移动之前都要把它给parent,以便于最终找到节点的插入位置
while (cur)
{
if (key>cur->_key)//插入节点的数值比当前节点大,则cur向右子树移动
{
parent = cur;
cur = cur->_right;
}
else if (key<cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;//要插入的节点元素值与这棵树中的节点元素值相等,不能插入
}
}
//到这里cur已经指向了叶子节点的下一步,需要将插入值与叶子节点进行比较,来确定插在叶子节点的左子树还是右子树
if (key>parent->_key)
{
parent->_right = new Node(key);
}
else
{
parent->_left = new Node(key);
}
return true;
}
//删除
bool Remove(const K&key)
{
Node*parent = NULL;
Node*cur = _root;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else//已找到要删除的节点
{
Node*del = cur;
if (cur->_left == NULL)//cur的左为空(右为空或右不为空)
{
if (parent == NULL)//若删除的是根节点,则再赋予新的根节点
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)//cur在parent的左边
{
parent->_left = cur->_right;
}
else//cur在parent的右边
{
parent->_right = cur->_right;
}
}
}
else if (cur->_right == NULL)//cur的右为空(左为空或左不为空)
{
if (parent == NULL)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)//cur在parent的左边
{
parent->_left == cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
}
else//cur的左,右都不为空
{
parent = cur;
Node*subleft = cur->_right;
//寻找右子树的最左节点,替换法删除
while (subleft->_left)
{
parent = subleft;
subleft = subleft->_left;
}
del = subleft;
cur->_key = subleft->_key;//替换
if (parent->_left == subleft)//右子树的最左节点存在
{
parent->_left = subleft->_right;
}
else//右子树的最左节点就是右子树的根
{
parent->_right = subleft->_right;
}
}
delete del;
return true;
}
}
return false;
}
//查找
Node*Find(const K&key)
{
if (_root == NULL)
{
return NULL;
}
Node*cur = _root;
while (cur)
{
if (key > cur->_key)
{
cur = cur->_right;
}
else if (key < cur->_key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return NULL;
}
//中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
protected:
void Destory(Node*&root)
{
if (root == NULL)
{
return;
}
Destory(root->_left);
Destory(root->_right);
delete root;
root = NULL;
}
void _InOrder(Node*root)
{
if (root == NULL)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
protected:
Node*_root;
};
//test.c
#include"SearchBinaryTree.h"
#include<stdlib.h>
void TestBSTree()
{
int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
SearchBinaryTree<int> t1;
for (size_t i = 0; i < sizeof(a) / sizeof(a[10]); i++)
{
t1.Insert(a[i]);
}
t1.InOrder();
SearchBinaryTreeNode<int>*ret = t1.Find(9);
cout << ret->_key << endl;
t1.Remove(0);
t1.Remove(1);
t1.Remove(2);
t1.Remove(3);
t1.Remove(4);
t1.Remove(5);
t1.Remove(6);
t1.Remove(7);
t1.Remove(8);
t1.Remove(9);
//t1.Remove(10);
t1.InOrder();
}
int main()
{
TestBSTree();
system("pause");
}