二叉查找树又称二叉搜索树,二叉排序树。
1、二叉搜索树的原理
二叉搜索树,可以使用一个链表数据结构来表示,其中每一个结点就是一个对象。每个结点还包含属性lchild、rchild和parent,分别指向结点的左孩子、右孩子和双亲(父结点)。如果某个孩子结点或父结点不存在,则相应属性的值为空(NIL)。根结点是树中唯一父指针为NIL的结点,而叶子结点的孩子结点指针也为NIL。
2、二叉搜索树的特点:
1、每个数据都是具有唯一的一个关键字。(不能重复)
2、比根节点大的关键字,放在右边,小的放在左边。(即:左边的都小于根,右边都大于根)
3、二叉树的任何子树都是二叉树。
3、二叉树功能实现
1.查找:比键值来查找数据。
2.插入
3.遍历:遍历采用中序遍历
4.删除:(用创建新结点的方式删除)
删除顶替方法:
1、从删除结点的左子树中找最右边的进行顶替。
2、从删除结点的右子树中找最左边的进行顶替。
删除类型:
(1)叶子节点:直接删除,不影响原树。
(2)仅仅有左或右子树的节点:节点删除后,将它的左子树或右子树整个移动到删除节点的位置就可以。
(3)既有左又有右子树的节点:定义两个移动指针,一个记录,一个移动,先找到需要删除的结点,然后再选择删除方式(这里用第一种删除方式),找到替代的结点,然后再创建一个和替代结点一样的结点,去进行连接,再释放删除的结点,删除时,还要对数进行调,当这个替代结点存在左子树时(找到是最右的数,所以不存在右子树),要进行重复以上的操作,需要删除2次结点(一个是指定删除的,另一个是顶替的)
4、代码实现
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
//每一结点的数据
typedef struct _Data
{
int first;
char str[20];
}Data;
//二叉树结点
typedef struct _TreeNode
{
Data data;
struct _TreeNode*LChild;
struct _TreeNode*RChild;
}Node;
//封装一个二叉搜索树
typedef struct _SeekTree
{
Node* root;
int treeSize;
}SeekTree;
//创建结点
Node* createNode(Data data)
{
Node* newNode = (Node*)malloc(sizeof(Node));
assert(newNode != NULL);
newNode->data = data;
newNode->LChild = NULL;
newNode->RChild = NULL;
return newNode;
}
//创建搜索树
SeekTree* createSeekTree()
{
SeekTree* newTree = (SeekTree*)malloc(sizeof(SeekTree));
assert(newTree != NULL);
newTree->root = NULL;
newTree->treeSize = 0;
return newTree;
}
//打印
void print(Node* tree)
{
printf("%d\t%s\n", tree->data.first,tree->data .str);
}
//递归遍历
void CurTree(Node* tree)
{
if (tree != NULL)
{
CurTree(tree->LChild);
print(tree);
CurTree(tree->RChild);
}
}
//插入
void insertNode(SeekTree* tree, Data data)
{
Node* newNode = createNode(data);
//找合适位置
Node* pMove = tree->root;
Node* pMoveParent = NULL;//记录
//判断位置
while (pMove != NULL)
{
pMoveParent = pMove;
//记录移动结点的父节点
if (data.first < pMove->data.first)
{
//如果小于根节点往左
pMove = pMove->LChild;
}
else if (data.first > pMove->data.first)
{
//大于往右走
pMove = pMove->RChild;
}
else//相同的处理在于你 的选择方式
{
strcpy(pMove->data.str, data.str);
return;
}
}
//退出循环
if (tree->root == NULL)
{
tree->root = newNode;
}
else
{
//pmoveParent
if (pMoveParent->data.first > data.first)
{
pMoveParent->LChild = newNode;
}
else
{
pMoveParent->RChild = newNode;
}
}
tree->treeSize++;
}
//查找
Node* search(SeekTree* tree, int first)
{
Node* pMove = tree->root;
if (pMove == NULL)
{
return pMove;
}
else
{
while (pMove->data.first != first)
{
if (pMove->data.first > first)
{
pMove = pMove->LChild;
}
else
{
pMove = pMove->RChild;
}
if (pMove == NULL)
{
return pMove;
}
return pMove;
}
}
}
//二叉树的删除
void erase(SeekTree* tree, int first)
{
Node*pMove = tree->root; //移动结点
Node*pMoveParent = NULL; //移动结点的父节点
//找结点,找到位置(移动到要删除的位置)
while (pMove != NULL&&pMove->data.first != first)
{
pMoveParent = pMove;
if (first < pMove->data.first)
{
pMove = pMove->LChild;
}
else if (first>pMove->data.first)
{
pMove = pMove->RChild;
}
else
{
break;
}
}
//pMoveParent 这个位置的父节点,即 删除结点的父节点0
// 删除结点的左右子树都存在
if (pMove->LChild != NULL&&pMove->RChild != NULL)
{
//从删除节点的左子树中找最右
Node* moveNode = pMove->LChild; //往左边走
Node* moveNodeParent = pMove; //记录移动的父节点
while (moveNode->RChild != NULL)
{
moveNodeParent = moveNode;
moveNode = moveNode->RChild;
}
//找到最右边的结点
//创建一个元素和找到的结点一样的结点,将它放到删除结点位置
Node* newNode = createNode(moveNode->data);
//将找到结点,将要删除结点的左右子树连上(先连左右子树再连父节点)
newNode->LChild = pMove->LChild;
newNode->RChild = pMove->RChild;
//分类讨论
if (pMoveParent == NULL)
{
//为空表示要删除的是第一个根结点
tree->root = newNode;
}
else if (pMove == pMoveParent->LChild)
{
//再判断,连的是左还是右
pMoveParent->LChild = newNode;
}
else
{
pMoveParent->RChild = newNode;
}
//调整二叉树
//调整删除的指针
if (moveNodeParent == pMove)// moveNodeParent = pMove;
{
//位置没有改变,没有右边,删除结点只有一个左子树
pMoveParent = newNode;
}
else
{
pMoveParent = moveNodeParent;
}
free(pMove);//删除指定删除结点
pMove = moveNode;
}
Node* sNode = NULL;
//如果删除的结点左右存在结点,保存删除结点的下一个结点
if (pMove->LChild!= NULL)
{
//防止左边有结点
sNode = pMove->LChild;
}
else if(pMove->RChild != NULL)
{
sNode = pMove->RChild;
}
//如果删除的是根结点
if (tree->root == pMove)
{
tree->root = sNode;//置空,根节点的孩子结点顶上去
}
else
{
//如果是单边的,直接连上即可。
if (pMove == pMoveParent->LChild)
{
pMoveParent->LChild = sNode;
}
else
{
pMoveParent->RChild = sNode;
}
}
free(pMove);//如果是存在左右子树的情况,释放的就是替代那个结点
tree->treeSize--;
}
int main()
{
SeekTree* tree = createSeekTree();
Data A[5] = { 23, "wda", 12, "dasd", 56, "fger", 43, "dahgdy" };
insertNode(tree, A[0]);
insertNode(tree, A[1]);
insertNode(tree, A[2]);
insertNode(tree, A[3]);
printf("中序遍历\n");
CurTree(tree->root);
Node* F = search(tree, 43);
puts("\n");
print(F);
erase(tree, 56);
printf("中序遍历\n");
CurTree(tree->root);
while (1);
return 0;
}