二叉搜索树(Binary Search Tree):
(又:二叉搜索树,二叉排序树)
它或者是一棵空树,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树。 ————百度百科
二叉搜索树的基本操作:
构成:既然是二叉树那么构成一定包括了左子树、右子树、结点的值域
typedef struct BSTNode
{
struct BSTNode* _left;
struct BSTNode* _right;
BSTDataType _data;
}BNode, *pBNode;
插入:在二叉搜索树中插入新元素时,必须先检测该元素是否在树中已经存在。如果已经存在,则不进行插入;否则将新元素加入到搜索停止的地方。
1、树为空,则直接插入,插入节点为根节点
2、树不为空,则按照二叉搜索树的特性查找要插入的位置,插入
int InsertBSTree(pBNode* pRoot, BSTDataType data)
{
pBNode pCur = NULL; //插入的元素
pBNode pParent = NULL; //插入元素的双亲
assert(pRoot);
//空树
if (NULL == *pRoot) //空树则插入节点为根节点
{
*pRoot = BuyNode(data);
return 1;
}
//找待插入元素的位置
pCur = *pRoot;
while (pCur)
{
if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_left;
}
else if (data > pCur->_data)
{
pParent = pCur;
pCur = pCur->_right;
}
else
{
return 0;
}
}
//插入新节点
pCur = BuyNode(data); //接收新的结点
if (data > pParent->_data)
{
pParent->_right = pCur;
}
else
{
pParent->_left = pCur;
}
return 1;
}
删除:首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要 删除的结点可能分下面四种情况: a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
情况a可以归类到c或者d
对于上述情况,相应的删除方法如下:
a. 直接删除该结点
b. 删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子
结点
c. 删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结
点
d. 在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,在来处理该结点的删除问题
int DeleteBSTree(pBNode* pRoot, BSTDataType data)
{
pBNode pCur = NULL; //标记当前结点
pBNode pParent = NULL; //标记当前结点的双亲节点
pBNode pDel = NULL; //标记要删除的树
/* 存在? 空树? */
assert(pRoot);
if (NULL == *pRoot)
{
return 0;
}
/* 找出删除节点的位置 */
pCur = *pRoot;
while (pCur)
{
if (data == pCur->_data)
break;
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_left;
}
else
{
pParent = pCur;
pCur = pCur->_right;
}
}
if (NULL == pCur)//没找到要删除的结点
{
return 0;
}
/* 对节点进行删除 */
//1.只有左孩子
if (NULL == pCur->_right)
{
pDel = pCur;
//1>节点是根节点------直接删
if (pCur == *pRoot)
{
*pRoot = pCur->_left;
}
//2>节点是有左子树的结点-------让pParent 的左子树指向pCur的左子树
else
{
if (pCur == pParent->_left)//删除没有2的1.
{
pParent->_left = pCur->_left;
}
else
{
pParent->_right = pCur->_left;
}
}
}
//2.只有右孩子
else if (NULL == pCur->_left)
{
pDel = pCur;
//1>根节点----直接删
if (pCur == *pRoot)
{
*pRoot = pCur->_right;
}
//只有右子树的非根节点
else
{
if (pCur == pParent->_right)
{
pParent->_right = pCur->_right;
}
else
{
pParent->_left = pCur->_right;
}
}
}
//3.左右孩子都有-----在右子树中找最小的节点替换后删除最小节点
// -----在左子树中找最大的结点替换后删除
else
{
//方法一:在左子树中找最大的元素替换
//pParent = pCur;
//pDel = pParent->_left;
//while (pDel->_right)
//{
// pParent = pDel;
// pDel = pDel->_right;
//}
//pCur->_data = pDel->_data;
//if (pDel == pParent->_right) //要删除结点是双亲的右
//{
// pParent->_right = pDel->_left;
//}
//else
//{
// pParent->_left = pDel->_left;
//}
//方法二:在右子树中找最小的替换
pParent = pCur;
pDel = pCur->_right;
while (pDel->_left) //遍历找到最小的结点
{
pParent = pDel;
pDel = pDel->_left;
}
pCur->_data = pDel->_data;
if ( pDel == pParent->_left) //删除最小的结点
{
pParent->_left = pDel->_right;
}
else
{
pParent->_right = pDel->_right;
}
}
free(pDel);
pDel = NULL;
return 1;
}
查找:
在子树中查找、找到返回true、否则返回False
pBNode FindBSTree(pBNode pRoot, BSTDataType data)
{
pBNode pCur = pRoot; //从根节点开始
while (pCur) //遍历整个二叉树
{
if (data == pCur->_data)
return pCur;
else if (data > pCur->_data)
{
pCur = pCur->_right;
}
else
{
pCur = pCur->_left;
}
}
return NULL;
}
完整代码:
1.Test.c
#include "BSTree.h"
#include <stdlib.h>
int main()
{
TestBSTree();
system("pause");
return 0;
}
#pragma once //二叉搜索树---O(N) 单支
typedef int BSTDataType;
#define NULL 0
typedef struct BSTNode
{
struct BSTNode* _left;
struct BSTNode* _right;
BSTDataType _data;
}BNode, *pBNode;
void InitBSTree(pBNode* pRoot); //初始化
int InsertBSTree(pBNode* pRoot, BSTDataType data); //插入
int DeleteBSTree(pBNode* pRoot, BSTDataType data); //删除
pBNode FindBSTree(pBNode pRoot, BSTDataType data); //查找
void DestoryBSTree(pBNode* pRoot); //销毁
int InsertBSTree_D(pBNode* pRoot, BSTDataType data);//递归插入
int DeleteBSTree_D(pBNode* pRoot, BSTDataType data);//递归删除
pBNode FindBSTree_D(pBNode pRoot, BSTDataType data);//递归查找
void InOrder(pBNode pRoot); //中序遍历
pBNode BuyNode(BSTDataType data);
void TestBSTree();
3.BSTree.c
#include "BSTree.h"
#include <malloc.h>
#include <assert.h>
#include <stdio.h>
void InitBSTree(pBNode* pRoot)
{
assert(pRoot);
*pRoot = NULL;
}
//构建新节点
pBNode BuyNode(BSTDataType data)
{
pBNode pNode = (pBNode)malloc(sizeof(BNode));
if (NULL == pNode)
{
return NULL;;
}
pNode->_data = data;
pNode->_left = NULL;
pNode->_right = NULL;
return pNode;
}
int InsertBSTree_D(pBNode* pRoot, BSTDataType data)
{
assert(pRoot);
if (NULL == *pRoot)
{
*pRoot = BuyNode(data);
return 1;
}
else
{
if (data == (*pRoot)->_data)
{
return 0;
}
if (data > (*pRoot)->_data)
{
return InsertBSTree_D(&(*pRoot)->_right, data);
}
else
{
return InsertBSTree_D(&(*pRoot)->_left, data);
}
}
}
int InsertBSTree(pBNode* pRoot, BSTDataType data)
{
pBNode pCur = NULL; //插入的元素
pBNode pParent = NULL; //插入元素的双亲
assert(pRoot);
//空树
if (NULL == *pRoot) //空树则插入节点为根节点
{
*pRoot = BuyNode(data);
return 1;
}
//找待插入元素的位置
pCur = *pRoot;
while (pCur)
{
if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_left;
}
else if (data > pCur->_data)
{
pParent = pCur;
pCur = pCur->_right;
}
else
{
return 0;
}
}
//插入新节点
pCur = BuyNode(data); //接收新的结点
if (data > pParent->_data)
{
pParent->_right = pCur;
}
else
{
pParent->_left = pCur;
}
return 1;
}
int DeleteBSTree_D(pBNode* pRoot, BSTDataType data)
{
assert(pRoot);
if (NULL == *pRoot)
{
return 0;
}
else
{
if (data > (*pRoot)->_data)
{
return DeleteBSTree_D(&(*pRoot)->_right, data);
}
else if (data < (*pRoot)->_data)
{
return DeleteBSTree_D(&(*pRoot)->_left, data);
}
else
{
//已找到要删除的结点
//1.只有左子树
pBNode pDel = *pRoot;
if (NULL == (*pRoot)->_right)
{
*pRoot = pDel->_left;
free(pDel);
return 1;
}
//2.只有右子树
else if (NULL == (*pRoot)->_left)
{
*pRoot = pDel->_right;
free(pDel);
return 1;
}
//3.左右子树都存在
else
{
//在右子树中找出要替换的结点(最小)
//pBNode pParent = *pRoot;
pDel = (*pRoot)->_right;
while (pDel->_left)
{
//pParent = pDel;
pDel = pDel->_left;
}
(*pRoot)->_data = pDel->_data;
return DeleteBSTree_D(&(*pRoot)->_right, pDel->_data);
}
}
}
}
int DeleteBSTree(pBNode* pRoot, BSTDataType data)
{
pBNode pCur = NULL; //标记当前结点
pBNode pParent = NULL; //标记当前结点的双亲节点
pBNode pDel = NULL; //标记要删除的树
/* 存在? 空树? */
assert(pRoot);
if (NULL == *pRoot)
{
return 0;
}
/* 找出删除节点的位置 */
pCur = *pRoot;
while (pCur)
{
if (data == pCur->_data)
break;
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_left;
}
else
{
pParent = pCur;
pCur = pCur->_right;
}
}
if (NULL == pCur)//没找到要删除的结点
{
return 0;
}
/* 对节点进行删除 */
//1.只有左孩子
if (NULL == pCur->_right)
{
pDel = pCur;
//1>节点是根节点------直接删
if (pCur == *pRoot)
{
*pRoot = pCur->_left;
}
//2>节点是有左子树的结点-------让pParent 的左子树指向pCur的左子树
else
{
if (pCur == pParent->_left)//删除没有2的1.
{
pParent->_left = pCur->_left;
}
else
{
pParent->_right = pCur->_left;
}
}
}
//2.只有右孩子
else if (NULL == pCur->_left)
{
pDel = pCur;
//1>根节点----直接删
if (pCur == *pRoot)
{
*pRoot = pCur->_right;
}
//只有右子树的非根节点
else
{
if (pCur == pParent->_right)
{
pParent->_right = pCur->_right;
}
else
{
pParent->_left = pCur->_right;
}
}
}
//3.左右孩子都有-----在右子树中找最小的节点替换后删除最小节点
// -----在左子树中找最大的结点替换后删除
else
{
//方法一:在左子树中找最大的元素替换
//pParent = pCur;
//pDel = pParent->_left;
//while (pDel->_right)
//{
// pParent = pDel;
// pDel = pDel->_right;
//}
//pCur->_data = pDel->_data;
//if (pDel == pParent->_right) //要删除结点是双亲的右
//{
// pParent->_right = pDel->_left;
//}
//else
//{
// pParent->_left = pDel->_left;
//}
//方法二:在右子树中找最小的替换
pParent = pCur;
pDel = pCur->_right;
while (pDel->_left) //遍历找到最小的结点
{
pParent = pDel;
pDel = pDel->_left;
}
pCur->_data = pDel->_data;
if ( pDel == pParent->_left) //删除最小的结点
{
pParent->_left = pDel->_right;
}
else
{
pParent->_right = pDel->_right;
}
}
free(pDel);
pDel = NULL;
return 1;
}
pBNode FindBSTree_D(pBNode pRoot, BSTDataType data)
{
if (NULL == pRoot)
{
return NULL;
}
else
{
if (data == pRoot->_data)
{
return pRoot;
}
else if (data > pRoot->_data)
{
return FindBSTree_D(pRoot->_right, data);
}
else
{
return FindBSTree_D(pRoot->_left, data);
}
}
}
void InOrder(pBNode pRoot)
{
if (NULL == pRoot)
{
return;
}
if (pRoot)
{
InOrder(pRoot->_left);
printf("%d ", pRoot->_data);
InOrder(pRoot->_right);
}
}
void DestoryBSTree(pBNode* pRoot) //递归销毁
{
assert(pRoot);
if (NULL == *pRoot)
{
return;
}
else
{
if (NULL != (*pRoot)->_left) //左子树不为空
{
DestoryBSTree(&(*pRoot)->_left);
}
if (NULL != (*pRoot)->_right) //右子树不为空
{
DestoryBSTree(&(*pRoot)->_right);
}
if (NULL == (*pRoot)->_right && NULL == (*pRoot)->_left)//叶子节点
{
free(*pRoot);
*pRoot = NULL;
}
}
}
void TestBSTree()
{
int array[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
pBNode pRoot = NULL;
pBNode pCur = NULL;
int i = 0;
int data = 10;
InitBSTree(&pRoot);
for (; i < sizeof(array) / sizeof(array[0]); i++)
{
InsertBSTree_D(&pRoot, array[i]);
}
InOrder(pRoot);
printf("\n");
DeleteBSTree(&pRoot, 7);
InOrder(pRoot);
printf("\n");
pCur = FindBSTree(pRoot, data);
if (pCur)
{
printf("%d is in BSTree!!!",data);
}
else
{
printf("%d is not in BSTree!!!", data);
}
DestoryBSTree(&pRoot);
}
二叉搜索树性能分析:
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树 .
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:Log2N
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2