您的位置:首页 > 理论基础 > 数据结构算法

数据结构与算法--二叉树(一)

2016-04-22 08:16 357 查看



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)要被删除的结点是其双亲结点的左孩子时,将要删除结点的右子树变成双亲结点的左孩子即可。

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

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(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);
}
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(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);
}
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(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);
}
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(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);
}
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: