什么是树
树在计算机科学里,是一种十分常见的数据结构,几乎所有操作系统都将文件存放在树状的结构里。
从上面的图来看,树是由节点和边构成,整棵树有一个最上端节点,称为跟节点。每个节点可以拥有具方向性的边用来和其他节点相连。相连的节点中,在上者称为父节点,在下者称为子节点。无子节点称为叶节点。子节点可以存在多个,如果最多只允许两个子节点,即所谓二叉树。根节点的至任何节点之间有唯一的路径,路径所经过的边数为路径长度。
整棵树的高度,便以跟节点的高度来代表,节点A->B之间如果存在一条路径,那么A称为B的祖代,B称为A的子代。任何节点大小是指其所有子代的节点总数。
本文会对树的基本概念做介绍,但重点介绍二叉搜索树。
二叉搜索树
所谓的二叉树,其意义是:“任何节点最多只允许两个子节点”这两个子节点称为左子节点和右子节点,如果以递归的方式来定义二叉树,可以说:“一个二叉树如果不为空,便是由一个根节点和左右两个子树构成;左右子树都可能为空”。
图1-1所示是一棵二叉搜索树:
二叉搜索树的节点放置规则是:**任何节点的键值大于其左子树中的每一个节点的键值,并小于其右子树中的每一个节点的键值。**从根节点一直往左走,直到无路可走,即得到最小的元素;从根节点一直往右走,直到无路可走,即得到最大元素。
要在一棵二叉搜索树中找到最大元素或最小元素,是一件简单的事情,就像上面说的一样,一直往左走或一直往右走即是。
最大元素:
ElemType FindMax(BTreePtr T) {
ElemType max;
while(T != NULL) {
max = T->data;
T = T->rchild;
}
return max;
}
最小元素:
ElemType FindMin(BTreePtr T) {
ElemType min;
while(T != NULL) {
min = T->data;
T = T->lchild;
}
return min;
}
比较麻烦的是元素的插入和移除。图1-2是二叉搜索树的元素插入操作图解。插入新元素时,可从根节点开始,遇到键值较大的向右子树插入,遇键值较小者就向左子树插入。
插入节点值16,与根节点比较,比根节点小,因此将插入到左子树。
二叉搜索树的插入:
int Insert(BTreePtr *T, ElemType e) {
BTreePtr p;
//创建一个节点
if (*T == NULL) {
*T = (BTreePtr)malloc(sizeof(BTree));
(*T)->data = e;
return TRUE;
} else {
p = *T;
while ( p != NULL) {
//比当前节点大,则插入到右子树
if (e > p->data)
{
if (p->rchild == NULL)
{
p->rchild = (BTreePtr) malloc (sizeof(BTree));
p->rchild->data = e; //将节点信息存储在此右叶子节点中
return TRUE;
}
p = p->rchild;
}
else //比当前节点小,则插入到左子树
{
if (p->lchild == NULL)
{
p->lchild = (BTreePtr) malloc (sizeof(BTree));
p->lchild->data = e;//将节点信息存储在此左叶子节点中
return TRUE;
}
p = p->lchild;
}
}
}
return FALSE;
}
二叉搜索树的移除,需要考虑很多情况:
1.删除的节点为叶子节点,直接删除
2.删除的节点有一个子节点,可以将该子节点作为其父节点的子节点
3.删除的节点有两个子节点,我们可以采取这样的策略:用右子树最小值代替该节点,并递归删除那个节点值。需要递归删除是因为这个最小值的节点可能还有右子树,因此需要做同样的删除操作(它不会有左子树,因为它自己的值最小)
删除旧值15,可以看出数值15只有一个子节点,直接将其子节点连至其父节点。
删除旧值12,有两个子节点,以右子树中的最小值取而代之。
int Delete(BTreePtr T, ElemType e)
{
BTreePtr p, pp, minP, minPP, child;
child = NULL;
p = T;
pp = NULL;
while ( (p != NULL) && (p->data != e) ) {
pp = p;
if (e > p->data) //比当前节点值大,从右子树查找并删除
{
p = p->rchild;
}
else //比当前节点值小,从左子树查找并删除
{
p = p->lchild;
}
}
if (p == NULL) return FALSE;
//双节点
if ((p->lchild != NULL) && (p->rchild != NULL))
{
minPP = p;
minP = p->rchild;
while (minP->lchild != NULL) {
minPP = minP;
minP = minP->lchild;
}
p->data = minP->data;
minPP->lchild = minP->rchild;
free(minP);
return TRUE;
}
//有一个节点
if ((p->lchild != NULL) || (p->rchild != NULL)) { //应该将原有的pp同child连接在一起
if (p->lchild) {
child = p->lchild;
} else {
child = p->rchild;
}
if(pp->data>p->data)
{
pp->lchild=child;
} else
{
pp->rchild=child;
}
free(p);
return TRUE;
}
//没有节点
if (pp->lchild == p) {//这里面临pp除p以外的节点为null的情况
pp->lchild = child;
} else {
pp->rchild = child;
}
return TRUE;
}
二叉搜索树 完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef char ElemType;
typedef struct node
{
ElemType data;
struct node *lchild, *rchild;
} BTree, *BTreePtr;
//插入
Status Insert(BTreePtr *T, ElemType e) {
BTreePtr p;
if (*T == NULL) {
*T = (BTreePtr)malloc(sizeof(BTree));
(*T)->data = e;
return TRUE;
} else {
p = *T;
while ( p != NULL)
{
if (e > p->data)
{
if (p->rchild == NULL)
{
p->rchild = (BTreePtr) malloc (sizeof(BTree));
p->rchild->data = e;
return TRUE;
}
p = p->rchild;
}
else
{
if (p->lchild == NULL)
{
p->lchild = (BTreePtr) malloc (sizeof(BTree));
p->lchild->data = e;
return TRUE;
}
p = p->lchild;
}
}
}
return FALSE;
}
// 删除
Status Delete(BTreePtr T, ElemType e) {
BTreePtr p, pp, minP, minPP, child;
child = NULL;
p = T;
pp = NULL;
while ( (p != NULL) && (p->data != e) )
{
pp = p;
if (e > p->data) {
p = p->rchild;
} else {
p = p->lchild;
}
}
if (p == NULL) return FALSE;
//双节点
if ((p->lchild != NULL) && (p->rchild != NULL))
{
minPP = p;
minP = p->rchild;
while (minP->lchild != NULL)
{
minPP = minP;
minP = minP->lchild;
}
p->data = minP->data;
minPP->lchild = minP->rchild;
free(minP);
return TRUE;
}
//有一个节点
if ((p->lchild != NULL) || (p->rchild != NULL)) //应该将原有的pp同child连接在一起
{
if (p->lchild)
{
child = p->lchild;
} else {
child = p->rchild;
}
if(pp->data>p->data)
{
pp->lchild=child;
} else
{
pp->rchild=child;
}
free(p);
return TRUE;
}
//没有节点
if (pp->lchild == p) //这里面临pp除p以外的节点为null的情况
{
pp->lchild = child;
} else {
pp->rchild = child;
}
return TRUE;
}
// 查找
Status Find(BTreePtr T, ElemType e)
{
if (T == NULL) return FALSE;
while ((T != NULL) && (T->data != e))
{
if (e > T->data) {
T = T->rchild;
} else {
T = T->lchild;
}
}
if (T) {
return TRUE;
} else {
return FALSE;
}
}
// 最大值
ElemType FindMax(BTreePtr T)
{
ElemType max;
while(T != NULL)
{
max = T->data;
T = T->rchild;
}
return max;
}
//最小值
ElemType FindMin(BTreePtr T)
{
ElemType min;
while(T != NULL)
{
min = T->data;
T = T->lchild;
}
return min;
}
void PreOrderTraverse(BTreePtr T)//前序遍历二叉树
{
if (T == NULL) return;
if(T)
{
printf("%d ",T->data);
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
}
void DestroyTree(BTreePtr T) {
if (T)
{
if (T->lchild)
{
DestroyTree(T->lchild);
}
if(T->rchild)
{
DestroyTree(T->rchild);
}
free(T);
T = NULL;
}
}
int main(int argc, char const *argv[])
{
BTreePtr T;
T = NULL;
int a[] = {33, 16, 50, 13, 18, 34, 58, 15, 17, 25, 51, 66, 19, 27, 55};
int i;
for (i = 0; i < 15; i++) {
Insert(&T, a[i]);
}
printf("Max is %d\n", FindMax(T));
printf("Min is %d\n", FindMin(T));
Delete(T, 18);
Delete(T, 13);
PreOrderTraverse(T);
DestroyTree(T);
return 0;
}
输出结果:
参考:STL 源码解析