二叉搜索树也叫二叉排序树。
特点:每个根节点的左子树上的数据都小于该根节点的数据,每个根节点的右子树的数据都大于该根节点的数据。
对于二叉搜索树其本质也是一个二叉树,所以其具有二叉树的所有操作,只不过他是有规律的,所以可以对其进行指定结点的删除和查找,并且可以进行插入操作。
对二叉搜索树进行中序遍历就可以得到一个递增的序列。
主要接口:
//对二叉搜索树的的插入
void BSTreeInsert(BSTreeNode** root, BSTDataType x);
//对二叉搜索的删除一个数据
int BSTreeRemove(BSTreeNode** root, BSTDataType x);
//查找二叉搜索树的一个结点
BSTreeNode* BSTreeFind(BSTreeNode** root, BSTDataType x);
//对二叉搜索树进行中序遍历
void BSTreeInOrder(BSTreeNode** root);
对于二叉搜索树的结构体定义:
typedef int BSTDataType;
typedef struct BSTreeNode
{
struct BSTreeNode* _left;
struct BSTreeNode* _right;
BSTDataType _data;
}BSTreeNode;
接口实现:
1.二叉搜索树中插入一个结点
思路:先找到要插入的位置,然后再进行插入。
找插入位置:一次次的进行比较,直到比较到叶子结点为止,然后再比较要插入的数据与叶子节点的大小,再决定要将其插入到左子树还是右子树。
特例:1.如果该二叉树为空树的话,那就直接申请一个结点,令该指针指向这个节点即可
2.如果在进行查找的时候,要插入的的数据等于二叉搜索树中的数据了,那么就直接返回,不再插入
//申请一个结点的空间,为插入节点做准备
BSTreeNode* BuyBSTreeNode(BSTDataType x)
{
BSTreeNode* newNode = (BSTreeNode*)malloc(sizeof(BSTreeNode));
if (newNode == NULL)
{
perror("malloc for memory!");
}
newNode->_data = x;
newNode->_left = NULL;
newNode->_right = NULL;
return newNode;
}
void BSTreeInsert(BSTreeNode** root, BSTDataType x)
{
assert(root);
//如果树为空,则直接将其当作根节点
if (*root == NULL)
{
*root = BuyBSTreeNode(x);
}
BSTreeNode* cur = *root;
BSTreeNode* parent = NULL;
//找要插入的结点
while (cur)
{
//向左插入
if (cur->_data > x)
{
parent = cur;
cur = cur->_left;
}
//向右插入
else if (cur->_data < x)
{
parent = cur;
cur = cur->_right;
}
//已经有该数据了,无需插入
else
{
return;
}
}
//cur已经到叶子节点的孩子节点了,parent为叶子节点
//在parent左边插入
if (parent->_data > x)
{
parent->_left = BuyBSTreeNode(x);
}
else
{
parent->_right = BuyBSTreeNode(x);
}
}
2.删除一个二叉搜索树的结点
思路:删除节点分为以下几种。
1.删除的是叶子节点(可将其归纳到第二种情况中,这是第二种情况的一个特例)
2.删除的结点的左子树为空或者右子树为空
(1)删除的节点是根节点
方法:直接将根节点,变为左子树(根节点的右子树为空):直接将根节点变为右子树(根节点的左子树为空)
(2)删除的节点不是根节点
方法:使要删除的结点的双亲结点指向要删除结点的指针指向要删除的结点的非空的一侧
3.删除的结点的左右子树都不为空(替换删除法)
方法:对于要删除的结点的左右子树都不为空,可以采取替换删除法。也就是找到一个好删除的结点,令该节点的数据赋值到要删除的结点的数据域上,然后再将这个结点进行删除。
一般替换删除的这个节点,都是该节点的左子树上最大的那个节点(也就是左子树上右路结点的最后一个结点),或者右子树上最小的那个节点(右子树上左路结点的最后一个节点)。
//返回1,代表删除成功:返回0,代表删除失败
//方法一:
//以是否为根节点进行分支,
//对于删除左右都不为空,两个都有,其余要分为删除根节点和删除非根节点。
int BSTreeRemove(BSTreeNode** root, BSTDataType x)
{
assert(root);
//没有数据可供删除
if ((*root) == NULL)
{
return 0;
}
//删除数据
BSTreeNode* cur = *root;
BSTreeNode* parent = NULL;
while (cur)
{
if (cur->_data > x)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_data < x)
{
parent = cur;
cur = cur->_right;
}
else
{
//1.左结点为空,或者右结点为空
//表示删除的不是根节点
if (parent != NULL)
{
//左结点为空,parent连上cur的右子树
if (cur->_left == NULL)
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
free(cur);
cur = NULL;
}
else
{
parent->_right = cur->_right;
free(cur);
cur = NULL;
}
return 1;
}
//右结点为空,parent连上cur的左子树
else if (cur->_right == NULL)
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
free(cur);
cur = NULL;
}
else
{
parent->_right = cur->_left;
free(cur);
cur = NULL;
}
return 1;
}
}
//删除的是根节点
else
{
//找到了,但是找到的是根节点
parent = cur;
//左子树为空
if (cur->_left == NULL)
{
*root = cur->_right;
free(cur);
cur = NULL;
return 1;
}
//右子树为空
if (cur->_right == NULL)
{
*root = cur->_left;
free(cur);
cur = NULL;
return 1;
}
}
//2.左右结点都不为空(替换删除法)
//第一种方法:令要删除的结点,替换为其右子树的最小值(左路结点)
//replace_right是cur右子树的最小值
BSTreeNode* replace_right = cur->_right;
while (replace_right->_left)
{
replace_right = replace_right->_left;
}
//必须要将要替换的值记录下来,因为最后就将其的空间释放了,找不到了
int tmp = replace_right->_data;
BSTreeRemove(root, replace_right->_data);
cur->_data = tmp;
//第二种方法:令要删除的结点,替换为左子树上的最大值(右路结点)
//replace_left为左子树上的最大值
//BSTreeNode* replace_left = cur->_left;
//while (replace_left->_right)
//{
// replace_left = replace_left->_right;
//}
//int tmp = replace_left->_data;
//BSTreeRemove(root, replace_left->_data);
//cur->_data = tmp;
}
}
//没有找到要删除的数据,无法进行删除
return 0;
}
//方法二:以删除类型进行分支,看删除的是左子树为空,右子树为空,或者左右都不为空
int BSTreeRemove(BSTreeNode** root, BSTDataType x)
{
assert(root);
//树为空,无法删除
if (*root == NULL)
{
return 0;
}
//parent是cur的双亲结点
BSTreeNode* cur = *root;
BSTreeNode* parent = NULL;
while (cur)
{
if (cur->_data > x)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_data < x)
{
parent = cur;
cur = cur->_right;
}
else
{
//找到,删除
//左子树为空
if (cur->_left == NULL)
{
//删除根节点
if (parent == NULL)
{
*root = (*root)->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
}
//右子树为空
else if (cur->_right == NULL)
{
if (parent == NULL)
{
*root = (*root)->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
}
//左右子树都不为空
else
{
BSTreeNode* replace_right = cur->_right;
while (replace_right->_left)
{
replace_right = replace_right->_left;
}
cur->_data = replace_right->_data;
return BSTreeRemove(&cur->_right, replace_right->_data);
}
//已经删除,进行释放结点
free(cur);
cur = NULL;
return 1;
}
}
return 0;
}
3.查找二叉搜索树中的一个结点(基本思路和插入一个结点的思路一样)
思路;一直比较即可,找到或者找不到
特例:1.树为空,直接返回找不到
2.一直比较,找到了
3.比较到了叶子节点也没有找到
//在二叉搜索树中查找一个数据
//思路:如果大于结点就在右边,否则就在左边。
//结束条件:1.树为空 2.找到了 3.找到叶子节点的下一个结点依旧没有找到
BSTreeNode* BSTreeFind(BSTreeNode** root, BSTDataType x)
{
assert(root);
BSTreeNode* cur = *root;
if (cur == NULL)
{
return NULL;
}
while (cur)
{
//向左查找
if (cur->_data > x)
{
cur = cur->_left;
}
//向右查找
else if (cur->_data < x)
{
cur = cur->_right;
}
//找到了
else
{
return cur;
}
}
//表示已经找到叶子节点的下一个节点了,没有找到
return NULL;
}
4.对二叉搜索树进行中序遍历(就可以得到一个递增的序列)
就相当于对二叉数进行中序遍历,代码十分简单。
关键是要记住对二叉搜索树进行中序遍历可以得到一个递增序列
void BSTreeInOrder(BSTreeNode** root)
{
assert(root);
if (*root == NULL)
{
return;
}
BSTreeInOrder(&((*root)->_left));
printf("%d ", (*root)->_data);
BSTreeInOrder(&((*root)->_right));
}