1、基本概念
二叉搜索树:又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
- 没有键值相等的节点
特点:
- 二叉搜索树的最左子树存放最小的值,最右子树存放最大的值;
- 二叉搜索树如果按中序遍历的话,得到的数列是有序的;
2、基本操作分析
【普通版】
查找:
根据二叉搜索树的特点,我们要查找一个数,可以进行以下操作:
- 从根节点开始遍历,如果为空树,返回false;如果不为空树,则令要查找的值与根值进行比较;
- 如果等于根值,则返回true;
- 如果大于根值,进入该树的右子树;
- 如果小于根值,进入该树的左子树;
- 重复以上步骤,直到不满足循环条件;
插入:
这个也是依据二叉搜索树的特性来看的,与查找很相似
- 判断是否为空树,若为空,则直接给该树的根节点赋值,并返回true;
- 若不为空,则开始查找要插入元素的位置。若该值小于根值,进入该数的左子树;
- 若该值大于根值,进入该数的右子树;
- 不满足条件退出循环,表示找到要插入的位置了;
- 如果要插入的值小于标记的值,就把该值放到标记的左子树里;
- 如果要插入的值大于标记的值,就把该值放到标记的右子树里;
【注】:标记是在查找循环里用来标记当前节点的上一节点的;因为循环条件的原因,退出循环时,当前节点肯定为空,而标记就是要插入值的根节点了。
删除:
- 判断该树是否为空,若为空树,则返回错误;
- 查找将要删除节点所在位置
- 删除节点
删除节点则要考虑以下方面:
待删除节点是叶子节点&待删除节点只有右孩子:
待删除节点是叶子节点&待删除节点只有左孩子:
与上一情况(待删除节点是叶子节点&待删除节点只有右孩子)差不多,这里不做过多说明;
待删除节点左右孩子均存在:
3、源码
【普通版】
BSTree.h
#pragma once
#ifndef __BSTREE_H__
#define __BSTREE_H__
#include<iostream>
#include<assert.h>
using namespace std;
template<class T>
struct BSTreeNode
{
public:
BSTreeNode(const T& data)
:_data(data)
, _pLeft(NULL)
, _pRight(NULL)
{}
BSTreeNode()
:_pLeft(NULL)
, _pRight(NULL)
{}
public:
BSTreeNode<T>* _pLeft;
BSTreeNode<T>* _pRight;
T _data;
};
template<class T>
class BSTree
{
public:
typedef BSTreeNode<T> Node;
typedef Node* pNode;
BSTree()
:_pRoot(NULL)
{}
BSTree(pNode node)
:_pRoot(node)
{}
//查找
bool Find(const T& data){
if (NULL == _pRoot)
return false;
pNode pCur = _pRoot;
while (pCur){
if (data == pCur->_data)
return true;
else if (data > pCur->_data)
pCur = pCur->_pRight;
else
pCur = pCur->_pLeft;
}
return false;
}
//插入
bool Insert(const T& data){
//要插入的为空树
if (NULL == _pRoot){
_pRoot = new Node(data);
return true;
}
//查找要插入的位置
pNode pCur = _pRoot;
pNode pParent = NULL;
while (pCur){
if (data == pCur->_data)//树中已经存在该值
return false;
else if (data < pCur->_data){
pParent = pCur;
pCur = pCur->_pLeft;
}
else{
pParent = pCur;
pCur = pCur->_pRight;
}
}
//进行插入操作
if (data < pParent->_data)
pParent->_pLeft = new Node(data);
else
pParent->_pRight = new Node(data);
return true;
}
//删除操作
bool Delete(const T& data){
//判断树是否存在
if (NULL == _pRoot)
return false;
//查找要删除的节点
pNode pCur = _pRoot;
pNode pParent = NULL;
pNode pDel = NULL;
while (pCur){
if (data > pCur->_data){
pParent = pCur;
pCur = pCur->_pRight;
}
else if (data < pCur->_data){
pParent = pCur;
pCur = pCur->_pLeft;
}
else{
//删除节点
//待删除节点是叶子节点 & 当前节点只有右孩子
if (NULL == pCur->_pLeft){
pDel = pCur;
if (NULL == pParent){
//待删除节点是根节点
_pRoot = pDel->_pRight;
}
else if (pDel == pParent->_pLeft){
//待删除节点是上一节点的左子树
pParent->_pLeft = pDel->_pRight;//待删除节点的左子树一定为空
}
else{
//待删除节点是上一节点的右子树
pParent->_pRight = pDel->_pRight;
}
}
else if (NULL == pCur->_pRight){
//待删除节点是叶子节点 & 当前节点只有左孩子
pDel = pCur;
if (NULL == pParent){
//待删除节点是根节点
_pRoot = pDel->_pLeft;
}
else if (pDel == pParent->_pLeft){
//待删除节点是上一节点的左子树
pParent->_pLeft = pDel->_pLeft;
}
else{
//待删除节点是上一节点的右子树
pParent->_pRight = pDel->_pLeft;
}
}
else{
//待删除节点左右子树均存在
pNode subRight = pCur->_pRight;
pParent = pCur;
//parent 在这里不能为空,否则在特定场景下判断subRight与上一节点的关系时会崩溃
//查找待删除节点的右子树的最小值(左子树)节点
while (subRight->_pLeft){
pParent = subRight;
subRight = subRight->_pLeft;
}
pDel = subRight;//要删除的节点
//交换待删除节点和它的右子树的值,再直接删除它的右子树节点
pCur->_data = pDel->_data;
//还需判断pDel是pParent的左节点还是右节点
//parent 在这里不能为空的原因,↓
if (pDel == pParent->_pLeft)
pParent->_pLeft = pDel->_pLeft;
else
pParent->_pRight = pDel->_pRight;
}
delete pDel;
return true;
}
}
return false;
}
//中序遍历
void InOrder(){
_InOrder(_pRoot);
cout << endl;
}
//析构
~BSTree(){
_Destory(_pRoot);
}
protected:
void _InOrder(pNode pRoot){
if (NULL == pRoot)
return;
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
void _Destory(pNode pRoot){
if (NULL == pRoot)
return;
_Destory(pRoot->_pLeft);
_Destory(pRoot->_pRight);
delete pRoot;
}
private:
pNode _pRoot;
};
#endif //__BSTREE_H__
Test.c
#include"BSTree.h"
#include<Windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
//测试二叉搜索树
void test2(){
int arr[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
BSTree<int> BSTree;
//创建二叉搜索树
for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i){
BSTree.Insert(arr[i]);
}
BSTree.InOrder();//正确的搜索二叉树的中序遍历一定是有序的
//检测查找6,9,2
if (BSTree.Find(6))
cout << "找到了6" << endl;
else
cout << "没找到6" << endl;
if (BSTree.Find(9))
cout << "找到了9" << endl;
else
cout << "没找到9" << endl;
if (BSTree.Find(2))
cout << "找到了2" << endl;
else
cout << "没找到2" << endl;
//检测删除元素
BSTree.Delete(4);
BSTree.InOrder();
BSTree.Delete(5);
BSTree.InOrder();
}
int main(){
test2();
system("pause");
return 0;
}
【程序运行结果】