数据结构与算法(3)

1 基于二叉链表的有序二叉树
1 基于二叉链表的有序二叉树
1.1 问题
BST是Binary Search Tree的缩写,译为二叉搜索树,或有序二叉树,是二叉树的一种,它的定义如下:

1)或者是一棵空树;

2)或者是具有下列性质的二叉树:

I) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

II) 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

III)左、右子树也分别为二叉排序树;

BST在查找一个结点或插入一个结点时,具有极大的优势,速度非常快。是一种基础性数据结构,广泛应用于更加抽象的集合、关联数组等数据结构。

1.2 方案
BST的基本操作包括:

  1. 创建结点,新生成一个结点用于向BST中添加。

  2. 增加结点,在BST中加入一个数据元素。

  3. 删除结点,在BST中删除一个数据元素。

  4. 查找结点,在BST中查找指定的数据是否存在,如果存在则得到其地址。

  5. 修改结点,对BST中指定的数据进行修改。

  6. 清除一棵树

1.3 步骤
实现此案例需要按照如下步骤进行。

步骤一:定义BST结点结构

在C语言中:

1)定义一个变量data,用来模拟BST中的数据。

2)定义一个指针left,用于指向该结点的左孩子。

3)定义一个指针right,用于指向该结点的右孩子。

4)这三方面的信息共同描述一个BST结点,可将它们封装在一起。

代码如下:

typedef int DataType;
struct Node{
DataType data;
struct Node *left;
struct Node *right;
};
步骤二:创建结点

首先,向操作系统申请一块存储空间。

然后,存储数据域数据

最后,将左孩子和右孩子指针赋值为空。

代码如下所示:

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
struct Node{
DataType data;
struct Node *left;
struct Node right;
};
//创建一个节点
struct Node
createNode(DataType d)
{
struct Node pn = (struct Node)malloc(sizeof(struct Node));
pn->data = d;
pn->left = pn->right = NULL;
return pn;
}
步骤三:增加结点

首先,判断要插入的结点是否存在。

然后,判断根结点是否为空。

1)若根结点不为空,则当要插入的结点数据大于根结点的数据时,插入右子树;否则插入左子树。

2)若根结点为空,则将新加入的结点添加到根结点处。

代码如下所示:

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
struct Node{
DataType data;
struct Node left;
struct Node right;
};
//创建一个节点
struct Node
createNode(DataType d)
{
struct Node pn = (struct Node)malloc(sizeof(struct Node));
pn->data = d;
pn->left = pn->right = NULL;
return pn;
}
//讲一个节点插入到一个树中
void insert(struct Node
* root, struct Node* pn) {
if (pn == NULL) return;
//树为空
if (*root == NULL) {*root = pn; return;}
//树不为空
if (pn->data > (*root)->data) {
insert(&(*root)->right, pn);
}else {
insert(&(*root)->left, pn);
}
}
步骤四:删除结点

首先,查找要删除的结点,若不存在,则直接返回。

然后,若要被删除的结点没有左子树,则作如下处理:

1)要被删除的结点的双亲结点不存在时,则表明要删除根结点,此时只需将要删除结点的右孩子结点变成根结点即可。

2)要被删除的结点是其双亲结点的左孩子时,将要删除结点的右子树变成双亲结点的左孩子即可。

  1. 要被删除的结点是其双亲结点的右孩子时,将要删除结点的右子树变成双亲结点的右孩子即可。

4)释放要被删除结点所占的存储空间。

最后,若要被删除的结点有左子树,则作如下处理:

1)在要删除结点的左子树中查找最右下的结点。

2)如果最右下结点的双亲结点与被删除结点是同一结点,则将最右下结点的双亲结点的左孩子变为最右下结点的左孩子。

3)如果最右下结点的双亲结点与被删除结点不是同一结点,则将最右下结点的双亲结点的右孩子变为最右下结点的左孩子。

4)将最右下结点的数据复制到要删除结点内。

5)释放最右下结点所占的存储空间。

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
struct Node{
DataType data;
struct Node left;
struct Node right;
};
//创建一个节点
struct Node
createNode(DataType d)
{
struct Node pn = (struct Node)malloc(sizeof(struct Node));
pn->data = d;
pn->left = pn->right = NULL;
return pn;
}
//讲一个节点插入到一个树中
void insert(struct Node
* root, struct Node* pn) {
if (pn == NULL) return;
//树为空
if (*root == NULL) {*root = pn; return;}
//树不为空
if (pn->data > (root)->data) {
insert(&(root)->right, pn);
}else {
insert(&(root)->left, pn);
}
}
//在二叉排序树t中删去关键字为k的结点
void delete(struct Node
root, DataType k)
{
struct Node
p, *f, *s, *q;
p=*root;
f=NULL;
while§//查找关键字为k的待删结点p
{
if(p->datak ) break; //找到则跳出循环
f=p; //f指向p结点的双亲结点
if(p->data>k)
p=p->left;
else
p=p->right;
}
if(p
NULL) return; //若找不到,返回原来的二叉排序树
if(p->leftNULL) //p无左子树
{
if(f
NULL)
*root=p->right; //p是原二叉排序树的根
else if(f->leftp) //p是f的左孩子
f->left=p->right; //将p的右子树链到f的左链上
else //p是f的右孩子
f->right=p->right; //将p的右子树链到f的右链上
free§;
}
else //p有左子树
{
q=p;
s=p->left;
while(s->right) //在p的左子树中查找最右下结点
{
q=s;
s=s->right;
}
if(q
p)
q->left=s->left; //将s的左子树链到q上
else
q->right=s->left;
p->data=s->data; //将s的值赋给p
free(s);
}
}
上述代码中,以下代码:

while(p)//查找关键字为k的待删结点p
{
    if(p->data==k )  break;  //找到则跳出循环
    f=p;   //f指向p结点的双亲结点
    if(p->data>k)
        p=p->left;
    else
        p=p->right;
}
if(p==NULL)  return;  //若找不到,返回原来的二叉排序树

设置循环查找要删除的结点,若不存在,则直接返回。

上述代码中,以下代码:

if(p->left==NULL)  //p无左子树
{
    if(f==NULL)
        *root=p->right;  //p是原二叉排序树的根
    else if(f->left==p)  //p是f的左孩子
        f->left=p->right;   //将p的右子树链到f的左链上
    else  //p是f的右孩子
        f->right=p->right;   //将p的右子树链到f的右链上
    free(p);
}

是若要被删除的结点没有左子树时的情形,其中指针p指向要被删除的结点,指针f指向被删除的结点的双亲结点。

    if(f==NULL)
        *root=p->right;  //p是原二叉排序树的根

表示要被删除的结点的双亲结点不存在时,要删除根结点。

    else if(f->left==p)  //p是f的左孩子
        f->left=p->right;   //将p的右子树链到f的左链上

表示要被删除的结点是其双亲结点的左孩子时,将要删除结点的右子树变成双亲结点的左孩子即可。

    else  //p是f的右孩子
        f->right=p->right;   //将p的右子树链到f的右链上

表示要被删除的结点是其双亲结点的右孩子时,将要删除结点的右子树变成双亲结点的右孩子即可。

上述代码中,以下代码:

else  //p有左子树
{
    q=p;
    s=p->left;
    while(s->right)  //在p的左子树中查找最右下结点
    {
        q=s;
        s=s->right;
    }
    if(q==p)
        q->left=s->left;   //将s的左子树链到q上
    else
        q->right=s->left;
    p->data=s->data;  //将s的值赋给p
    free(s);
}

是若要被删除的结点有左子树时的情形,其中指针p指向要被删除的结点,指针s指向最右下结点,指针q指向最右下结点的双亲结点。

    s=p->left;
    while(s->right)  //在p的左子树中查找最右下结点
    {
        q=s;
        s=s->right;
    }

是在要删除结点的左子树中查找最右下的结点。

    if(q==p)
        q->left=s->left;   //将s的左子树链到q上

表示如果最右下结点的双亲结点q与被删除结点p是同一结点,则将最右下结点的双亲结点q的左孩子q->left变为最右下结点s的左孩子s->left。

    else
        q->right=s->left;

表示如果最右下结点的双亲结点q与被删除结点p不是同一结点,则将最右下结点的双亲结点q的右孩子q->right变为最右下结点s的左孩子s->left。

步骤五:查找结点

首先,判断根结点是否为空,若是则直接返回NULL。

然后,在根结点不为空的情况下,若要查找的数据大于根结点数据,遍历右子树查找。

下一步,在根结点不为空的情况下,若要查找的数据小于根结点数据,遍历左子树查找。

最后,在根结点不为空的情况下,若要查找的数据等于根结点数据,则返回根结点。

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
struct Node{
DataType data;
struct Node left;
struct Node right;
};
//创建一个节点
struct Node
createNode(DataType d)
{
struct Node pn = (struct Node)malloc(sizeof(struct Node));
pn->data = d;
pn->left = pn->right = NULL;
return pn;
}
//讲一个节点插入到一个树中
void insert(struct Node
* root, struct Node* pn) {
if (pn == NULL) return;
//树为空
if (*root == NULL) {*root = pn; return;}
//树不为空
if (pn->data > (root)->data) {
insert(&(root)->right, pn);
}else {
insert(&(root)->left, pn);
}
}
//在二叉排序树t中删去关键字为k的结点
void delete(struct Node
root, DataType k)
{
struct Node
p, *f, *s, *q;
p=root;
f=NULL;
while§//查找关键字为k的待删结点p
{
if(p->datak ) break; //找到则跳出循环
f=p; //f指向p结点的双亲结点
if(p->data>k)
p=p->left;
else
p=p->right;
}
if(p
NULL) return; //若找不到,返回原来的二叉排序树
if(p->leftNULL) //p无左子树
{
if(f
NULL)
root=p->right; //p是原二叉排序树的根
else if(f->leftp) //p是f的左孩子
f->left=p->right; //将p的右子树链到f的左链上
else //p是f的右孩子
f->right=p->right; //将p的右子树链到f的右链上
free§;
}
else //p有左子树
{
q=p;
s=p->left;
while(s->right) //在p的左子树中查找最右下结点
{
q=s;
s=s->right;
}
if(q
p)
q->left=s->left; //将s的左子树链到q上
else
q->right=s->left;
p->data=s->data; //将s的值赋给p
free(s);
}
}
//查找一个树
struct Node
find(struct Node
root, DataType d){
if(root == NULL)
return NULL;
if(d > root->data) //查找右子树
return find(root->right,d);
else if(d < root->data) //查找左子树
return find(root->left,d);
else
return root;
}
步骤六:修改结点

首先,使用步骤四的方法查找到要修改的结点。

然后,将要修改的结点数据修改为指定数据。

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
struct Node{
DataType data;
struct Node left;
struct Node right;
};
//创建一个节点
struct Node
createNode(DataType d)
{
struct Node pn = (struct Node)malloc(sizeof(struct Node));
pn->data = d;
pn->left = pn->right = NULL;
return pn;
}
//讲一个节点插入到一个树中
void insert(struct Node
* root, struct Node* pn) {
if (pn == NULL) return;
//树为空
if (*root == NULL) {*root = pn; return;}
//树不为空
if (pn->data > (root)->data) {
insert(&(root)->right, pn);
}else {
insert(&(root)->left, pn);
}
}
//在二叉排序树t中删去关键字为k的结点
void delete(struct Node
root, DataType k)
{
struct Node
p, *f, s, q;
p=root;
f=NULL;
while§//查找关键字为k的待删结点p
{
if(p->datak ) break; //找到则跳出循环
f=p; //f指向p结点的双亲结点
if(p->data>k)
p=p->left;
else
p=p->right;
}
if(p
NULL) return; //若找不到,返回原来的二叉排序树
if(p->leftNULL) //p无左子树
{
if(f
NULL)
root=p->right; //p是原二叉排序树的根
else if(f->leftp) //p是f的左孩子
f->left=p->right; //将p的右子树链到f的左链上
else //p是f的右孩子
f->right=p->right; //将p的右子树链到f的右链上
free§;
}
else //p有左子树
{
q=p;
s=p->left;
while(s->right) //在p的左子树中查找最右下结点
{
q=s;
s=s->right;
}
if(q
p)
q->left=s->left; //将s的左子树链到q上
else
q->right=s->left;
p->data=s->data; //将s的值赋给p
free(s);
}
}
//查找一个树
struct Node
find(struct Node
root, DataType d){
if(root == NULL)
return NULL;
if(d > root->data) //查找右子树
return find(root->right,d);
else if(d < root->data) //查找左子树
return find(root->left,d);
else
return root;
}
//修改树中结点的值
void modify(struct Node
root, DataType oldData, DataType newData)
{
struct Node
p;
p = find(root, oldData);
p->data = newData;
}
1.4 完整代码
本案例的完整代码如下所示:

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
struct Node{
DataType data;
struct Node left;
struct Node right;
};
//创建一个节点
struct Node
createNode(DataType d)
{
struct Node pn = (struct Node)malloc(sizeof(struct Node));
pn->data = d;
pn->left = pn->right = NULL;
return pn;
}
//讲一个节点插入到一个树中
void insert(struct Node
* root, struct Node* pn) {
if (pn == NULL) return;
//树为空
if (*root == NULL) {*root = pn; return;}
//树不为空
if (pn->data > (root)->data) {
insert(&(root)->right, pn);
}else {
insert(&(root)->left, pn);
}
}
//在二叉排序树t中删去关键字为k的结点
void delete(struct Node
root, DataType k)
{
struct Node
p, *f, s, q;
p=root;
f=NULL;
while§//查找关键字为k的待删结点p
{
if(p->datak ) break; //找到则跳出循环
f=p; //f指向p结点的双亲结点
if(p->data>k)
p=p->left;
else
p=p->right;
}
if(p
NULL) return; //若找不到,返回原来的二叉排序树
if(p->leftNULL) //p无左子树
{
if(f
NULL)
root=p->right; //p是原二叉排序树的根
else if(f->leftp) //p是f的左孩子
f->left=p->right; //将p的右子树链到f的左链上
else //p是f的右孩子
f->right=p->right; //将p的右子树链到f的右链上
free§;
}
else //p有左子树
{
q=p;
s=p->left;
while(s->right) //在p的左子树中查找最右下结点
{
q=s;
s=s->right;
}
if(q
p)
q->left=s->left; //将s的左子树链到q上
else
q->right=s->left;
p->data=s->data; //将s的值赋给p
free(s);
}
}
//查找一个树
struct Node
find(struct Node
root, DataType d){
if(root == NULL)
return NULL;
if(d > root->data) //查找右子树
return find(root->right,d);
else if(d < root->data) //查找左子树
return find(root->left,d);
else
return root;
}
//修改树中结点的值
void modify(struct Node
root, DataType oldData, DataType newData)
{
struct Node
p;
p = find(root, oldData);
p->data = newData;
}
//清除一颗树
void clears(struct Node **root) {
if (*root == NULL) return;
clears(&(*root)->left);
clears(&(*root)->right);
free(*root);
root = NULL;
}
//打印一颗树
void print(struct Node
root) {
if (root == NULL) return;
printf("%d ", root->data);
print(root->left);
print(root->right);
}
int main() {

struct Node* root = NULL;
insert(&root, createNode(10));
insert(&root, createNode(5));
insert(&root, createNode(3));
insert(&root, createNode(2));
insert(&root, createNode(1));
insert(&root, createNode(4));
insert(&root, createNode(7));
insert(&root, createNode(6));
insert(&root, createNode(9));
insert(&root, createNode(8));
insert(&root, createNode(15));
print(root);
printf("\n");

printf("%d\n", find(root, 5)->data);

delete(&root, 10);
print(root);
return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值