[数据结构 C语言] 严蔚敏版 B-树:愿我心里没有B树

B-树的算法实现最难的还是插入操作和删除操作。就连生成B-树也依赖着插入操作。InsertBTree函数比较好的解决方法还是模块化- 将插入之后结点的数目变化分情况讨论,将复杂的逻辑结构拆分成 1、插入结点操作Insert 2、结点分裂操作Split 3、寻找插入相应的位置的操作Search 4、建立新的根节点的操作NewRoot 四个函数,具体应用到各种情况的讨论中。

删除函数需要注意的是指针域的挪动一个都不能马虎。因为连锁反应引起的删除虚拟结点并不是最后一层非终端结点。需要仔仔细细一个数据都不能漏。

此外由于根节点不是叶子结点(NULL)则至少需要两颗子树。不同于其他非终端结点需要(m/2)上限个,因此时时刻刻都需要单独讨论。

 

主要参考:http://www.cnblogs.com/kangjianwei101/p/5221816.html 自己根据理解加上了一些注释,略有微小的改动。

#ifndef B_TREE_H
#define B_TREE_H

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "Base.h"

//B-Tree
//宏定义
#define M 3 //B树的阶
#define MAX_LEVEL 100
#define EQ(a,b) ((a)==(b))
#define LT(a,b) ((a)<(b))

//类型定义
typedef ElemType_Search BTElemType;
typedef struct BTNode
{
    int keynum; //结点中的关键字个数
    struct BTNode *parent; //指向双亲结点

    KeyType key[M+1]; //关键字向量,0号单元未用
    struct BTNode *ptr[M+1]; //子树的指针向量
}BTNode; //B树的结点
typedef BTNode *BTree;

//查找结果类型
typedef struct
{
    BTree pt; //指向找到的结点
    int i; //关键字在结点中的序号,即插入位置
    int tag; //1查找成功0查找失败
}Result;

//函数列表
Status CreateBTree (BTree *BT, Table T);

Result SearchBTree (BTree BT, KeyType k);

int Search (BTree p, KeyType k);
//返回k在结点p中的次序,若不存在返回0

Status InsertKey (BTree *BT, KeyType k);
//将关键字k插入树

Status InsertBTree (BTree *BT, KeyType k, BTree q, int i);
//插入算法,在结点*q的key[i]和key[i+1]之间插入关键字k

void Insert (BTree q, KeyType i, KeyType x, BTree ap);
//将x和ap分别插入到q->key[i+1]和q->ptr[i+1]中

void split (BTree q, int s, BTree *ap);
//以s为界,将q指向的结点分裂成q和ap指向的两部分

void NewRoot (BTree *BT, BTree q, int x, BTree ap);
//生成含信息(BT,x,ap)的新的根节点*BT,原BT和ap为子树指针,q初始值为NULL

Status DeleteKey (BTree *BT, KeyType k);
//从B树中删除关键字k

Status DeleteBTree (BTree *BT, BTree q, int i);
//从B树中删除结点q中的第i个关键字

void Delete (BTree *BT, BTree q, int i);
//从B树中删除结点q中的第i个关键字

Status SearchMinKey (BTree BT, Result *R);
//找出BT中最小的关键字

Status FoundParent (BTree q, BTree *p, int *order);
//寻找双亲结点,q为p的第i个孩子

Status LeftMove (BTree old_ptr, int m, BTree new_ptr, int n, int len);
//向左移动关键字和指针

Status RightMove (BTree old_ptr, int m, BTree new_ptr, int n, int len);
//向右移动关键字和指针

void PrintBT_Level (BTree BT);
//层序输出B树

void PrintBT_InOrder (BTree BT);
//中序输出B树
#endif // B_TREE_H
#ifndef B_TREE_C
#define B_TREE_C

#include "B-Tree.h"

//函数列表
Status CreateBTree (BTree *BT, Table T)
{
    int i;
    *BT = NULL;

    if (T.length){
        for (i=1;i<=T.length;i++){
            if (!InsertKey(BT,T.elem[i].key))
                break;
        }
    }

    if (i>T.length)
        return OK;
    else
        return ERROR;
}

Result SearchBTree (BTree BT, KeyType k)
{
    Result R = {NULL,0,0};
    BTree p,q;
    int found,i;

    p = BT;
    q = NULL;
    found = FALSE;
    i=0;

    while (p && !found){
        i = Search(p,k);

        if (i>0 &&p->key[i]==k)
            found  = TRUE;
        else {
            q = p;
            p = p->ptr[i];
        }
    }

    R.i = i;

    if (found){
        R.pt = p;
        R.tag = 1;
    } else {
        R.pt = q;
        R.tag = 0;
    }

    return R;
}

int Search (BTree p, KeyType k)
{
    int i,j;

    for (i=0,j=1;j<=p->keynum;j++){
        if (p->key[j]<=k)
            i = j;
        else
            break;
    }

    return i;
}
//返回k在结点p中的次序,若不存在返回0

Status InsertKey (BTree *BT, KeyType k)
{
    Result R;
    R = SearchBTree(*BT,k);

    if (R.tag==1)
        return ERROR;
    else {
        InsertBTree(BT,k,R.pt,R.i);
    }

    return OK;
}
//将关键字k插入树

Status InsertBTree (BTree *BT, KeyType k, BTree q, int i)
{
    KeyType x = k;
    BTree ap = NULL;
    int finished = FALSE;
    int s;

    while (q && !finished){
        Insert(q,i,x,ap); //将x和ap分别插入到q中

        if (q->keynum<M)
            finished = TRUE;
        else {
            //关键字数目超出限制
            s = ceil((double)M/2); //除根节点之外至少有s棵子树,s-1个关键字

            split(q,s,&ap); //以s为界分q为q和ap
            x = q->key[s];
            q = q->parent;

            if (q) //在双亲结点中寻找插入x的位置
                i = Search(q,x);
        }
    }

    if (!finished) //BT是空树q的初始值为NULL或者根结点分裂为*q *ap
        NewRoot(BT,q,x,ap);

    return OK;
}
//插入算法,在结点*q的key[i]和key[i+1]之间插入关键字k

void Insert (BTree q, KeyType i, KeyType x, BTree ap)
{
    int j;

    for (j=q->keynum;j>i;j--){
            //注意key和ptr定义比上限多出一位
        q->key[j+1] = q->key[j];
        q->ptr[j+1] = q->ptr[j];
    }
    q->key[i+1] = x;
    q->ptr[i+1] = ap;
    q->keynum++;
}
//将x和ap分别插入到q->key[i+1]和q->ptr[i+1]中

void split (BTree q, int s, BTree *ap)
{
    (*ap) = (BTree)malloc (sizeof(BTNode));
    if (!(*ap))
        exit (OVERFLOW);
    (*ap)->ptr[0] = q->ptr[s];

    int i;

    for (i=s+1;i<=M;i++){
        (*ap)->key[i-s] = q->key[i];
        (*ap)->ptr[i-s] = q->ptr[i];
    }

    (*ap)->keynum = M-s;
    q->keynum = s - 1;

    (*ap)->parent = q->parent;

    for(i=0;i<=(*ap)->keynum;i++){
        if ((*ap)->ptr[i])
            (*ap)->ptr[i]->parent = *ap;
    }

}
//以s为界,将q指向的结点分裂成q和ap指向的两部分

void NewRoot (BTree *BT, BTree q, int x, BTree ap)
{
    BTree p = (BTree)malloc(sizeof(BTNode));
    if (!p)
        exit (OVERFLOW);

    p->keynum = 1;
    p->parent = NULL;
    p->key[1] = x;
    p->ptr[0] = *BT;
    p->ptr[1] = ap;

    if (p->ptr[0])//原树不为空
        p->ptr[0]->parent = p;
    if (p->ptr[1]) //新分裂出的根结点
        p->ptr[1]->parent = p;

    *BT = p;

}
//生成含信息(BT,x,ap)的新的根节点*BT,原BT和ap为子树指针,q初始值为NULL

Status DeleteKey (BTree *BT, KeyType k)
{
    Result R;

    R = SearchBTree(*BT,k);

    if (R.tag==1){
        DeleteBTree(BT,R.pt,R.i);
        return OK;
    } else
        return 0;
}
//从B树中删除关键字k

Status DeleteBTree (BTree *BT, BTree q, int i)
{ //找到真正需要删除的结点位置
    Result R = {q,i,1};

    if (i<1 || i>q->keynum)
        return 0; //第i个关键词存在

    if (q->ptr[i])
        SearchMinKey(q->ptr[i],&R);
    //右子树的最小结点替换当前结点
    q->key[i] = R.pt->key[R.i];

    Delete(BT,R.pt,R.i); //删除R.pt中第R.i个终端结点

    return OK;
}
//从B树中删除结点q中的第i个关键字

void Delete (BTree *BT, BTree q, int i)
{
    //删除R.pt中第R.i个终端结点,可能处于第一个,也可能处于中间某处
    int s = ceil ((double)M/2);
    BTree p = NULL;
    int order = -1;
    int tag = 0;
    BTree lc,rc;

    //寻找双亲结点,q为p的order孩子
    if (!FoundParent(q,&p,&order))
        tag = 1; //只有一个根结点
    else {
        if (q->keynum>=s)
            tag = 2; //直接删除结点即可
        else {
                //需要删除整个结点,此时结点的关键字数目为s-1
            if (tag==0 && order<p->keynum && p->ptr[order+1]->keynum>=s)
                tag = 3; //右兄弟关键字个数大于最小值
            if (tag==0 && order>0 && p->ptr[order-1]->keynum>=s)
                tag = 4; //左兄弟关键字个数大于最小值
            if (tag==0 && order<p->keynum && p->ptr[order+1]->keynum==s-1)
                tag = 5; //右兄弟关键字个数等于最小值
            if (tag==0 && order>0 &&p->ptr[order-1]->keynum==s-1)
                tag = 6; //左兄弟关键字个数等于最小值
        }
    }

    switch (tag){
        case 1: //根节点是否只有一个需要删除的结点
            if (q->keynum==1 && i==1){
                *BT = q->ptr[0];
                free (q);
            } else {
                //Status LeftMove (BTree old_ptr, int m, BTree new_ptr, int n, int len)
                //从old-ptr的m序号开始拷贝到new-ptr的n序号,长度为len
                LeftMove(q,i+1,q,i,q->keynum-i);
                q->keynum--;
            }
            break;
        case 2: //直接删除结点即可
            LeftMove(q,i+1,q,i,q->keynum-i);
            q->keynum--;
            break;
        case 3://右兄弟关键字个数大于最小值
            rc = p->ptr[order+1]; //rc为右兄弟
            LeftMove(q,i+1,q,i,q->keynum-i);
            q->key[q->keynum] = p->key[order+1]; //紧靠上移的关键字下移到q中
            q->ptr[q->keynum] = rc->ptr[0]; //将rc的结点挪到q的最后一位指针上
            p->key[order+1] = rc->key[1]; //右兄弟最小关键字上升到双亲结点
            rc->ptr[0] = rc->ptr[1]; //调整rc
            LeftMove(rc,2,rc,1,rc->keynum-1);
            rc->keynum--;
            break;
        case 4: //左兄弟关键字个数大于最小值
            lc = p->ptr[order-1]; //lc为左兄弟
            q->ptr[i] = q->ptr[i-1]; //删除结点的左边的指针向右挪动到删除结点的指针
            RightMove(q,i-1,q,i,i-1); //key0 p0往后的单元向右挪动
            q->key[1] = p->key[order]; //双亲结点值下移
            q->ptr[0] = lc->ptr[lc->keynum]; //左兄弟最后一个指针挪动到q
            p->key[order] = lc->key[lc->keynum]; //左兄弟最大值上移到p
            lc->keynum--;
            break;
        case 5: //结点和右兄弟关键字个数均等于最小值
            rc = p->ptr[order+1]; //结点的右兄弟
            //Status LeftMove (BTree old_ptr, int m, BTree new_ptr, int n, int len)
                //从old-ptr的m序号开始拷贝到new-ptr的n序号,长度为len
            LeftMove(q,i+1,q,i,q->keynum-i); //删除目标值,
            q->key[q->keynum] = p->key[order+1]; //双亲结点中的下一个值下移到q的最后一位
            q->ptr[q->keynum] = rc->ptr[0];
            LeftMove(rc,1,q,q->keynum+1,rc->keynum); //将右兄弟全部合并到q中
            q->keynum += rc->keynum;
            free (p->ptr[order+1]); //释放右兄弟
            LeftMove(p,order+2,p,order+1,p->keynum-order-1); //删除p中下移结点
            p->keynum--;
            if (p->keynum<s-1){
                //构造一个虚拟关键字
                p->keynum++;
                q = p;
                Delete(BT,q,q->keynum);
            }
            break;
        case 6: //结点和左兄弟关键字个数均等于最小值
            lc = p->ptr[order-1]; //左兄弟结点
            lc->key[lc->keynum+1] = p->key[order]; //双亲结点值下移到左兄弟结点
            lc->ptr[lc->keynum+1] = q->ptr[0];
            //Status LeftMove (BTree old_ptr, int m, BTree new_ptr, int n, int len)
                //从old-ptr的m序号开始拷贝到new-ptr的n序号,长度为len
            LeftMove(q,1,lc,lc->keynum+2,i-1);
            LeftMove(q,i+1,lc,lc->keynum+i+1,q->keynum-i); //将q中删除结点的剩余结点全部拷贝到左兄弟中
            lc->keynum += q->keynum;
            free (p->ptr[order]); //释放q结点
            LeftMove(p,order+1,p,order,p->keynum-order); //双亲结点中删除下移过后的order
            p->keynum--;
            if (p->keynum<s-1){
                    //如果双亲结点现有个数小于最小值则引起连锁反应
                p->keynum++;
                q = p;
                Delete(BT,q,q->keynum); //删除不存在的结点
            }
            break;
    }

}
//从B树中删除结点q中的第i个关键字

Status SearchMinKey (BTree BT, Result *R)
{
    BTree q = BT;

    while (q && q->ptr[0])
        q = q->ptr[0]; //最下层左端点

    if (q){
        R->pt = q;
        R->i = 1;
        R->tag = 1;
        return OK;
    } else
        return ERROR;
}
//找出BT中最小的关键字

Status FoundParent (BTree q, BTree *p, int *order)
{
    *p = q->parent;

    if (!(*p)){ //q为根节点
        *order = -1;
        return ERROR;
    } else {
        for (*order=0;(*p)->ptr[*order]!=q;(*order)++){}
        return OK;
    }

    return OK;
}
//寻找双亲结点,q为p的order孩子

Status LeftMove (BTree old_ptr, int m, BTree new_ptr, int n, int len)
{
    int k;

    if (!old_ptr || !new_ptr || m<1 || m>old_ptr->keynum)
        return ERROR;

    for (k=0;k<len;k++,m++,n++){
        new_ptr->key[n] = old_ptr->key[m];
        new_ptr->ptr[n] = old_ptr->ptr[m];
    }

    return OK;
}
//向左移动关键字和指针

Status RightMove (BTree old_ptr, int m, BTree new_ptr, int n, int len)
{
    int k;

    if (!old_ptr || !new_ptr || m<1 || m>old_ptr->keynum)
        return ERROR;

    for (k=0;k<len;k++,m--,n--){
        new_ptr->key[n] = old_ptr->key[m];
        new_ptr->ptr[n-1] = old_ptr->ptr[m-1];
    }

    return OK;
}
//向右移动关键字和指针

void PrintBT_Level (BTree BT)
{
    BTree p[MAX_LEVEL],q[MAX_LEVEL];
    int i,j,k;
    int a,b,count;

    a = 1;
    p[a] = BT;
    count = 0;

    while (a){
        printf("第%2d行关键字:",++count);

        b = 0;
        for (i=1;i<=a;i++){
            printf("(");
            for (j=0;j<=p[i]->keynum;j++){
                if (j)
                    printf(" %2d",p[i]->key[j]);
                if (p[i]->ptr[j])
                    q[++b] = p[i]->ptr[j];
            }
            printf(") ");
        }
        printf("\n");

        a = b;
        for (k=1;k<=b;k++)
            p[k] = q[k];
    }

}
//层序输出B树

void PrintBT_InOrder (BTree BT)
{
    int j;

    if(BT){
        for (j=0;j<=BT->keynum;j++){
            PrintBT_InOrder(BT->ptr[j]);
            if (j<BT->keynum)
                printf("%d ",BT->key[j+1]);
        }
    }
}
//中序输出B树

#endif // B_TREE_C
#ifndef BASE_H
#define BASE_H

//查找表基础结构

#include <stdio.h>
#include <stdlib.h>
#include "Status.h"
#include "Scanf.h"

//查找表类型定义
typedef int KeyType;
typedef struct
{
    KeyType key;
    float weight; //其它域,用于拓展
} ElemType_Search; //有序表元素类型

//0号单元弃用
typedef struct
{
    ElemType_Search *elem; //数据元素存储空间基址
    int length;
}Table;

//函数列表
Status Create (FILE *fp, Table *T, int n);

void Destory (Table *T);

void Traverse (Table T,void(*Visit)(ElemType_Search));

void PrintKey (ElemType_Search e);
//只输出key域

#endif // BASE_H
#ifndef BASE_C
#define BASE_C

#include "Base.h"

////查找表类型定义
//typedef int KeyTpe;
//typedef struct
//{
//    KeyTpe key;
//    float weight; //其它域,用于拓展
//} ElemType_Search; //有序表元素类型
//
////0号单元弃用
//typedef struct
//{
//    ElemType_Search *elem; //数据元素存储空间基址
//    int length;
//}Table;

//函数列表
Status Create (FILE *fp, Table *T, int n)
{
    T->elem = (ElemType_Search *)malloc ((n+1)*sizeof(ElemType_Search));
    if (!T->elem)
        exit (OVERFLOW);

    //0号单元弃用
    int i;
    int a;
    float b;
    for (T->length=0,i=1;i<=n;i++){
        if (Scanf(fp,"%d%f",&a,&b)==2){
            T->elem[i].key = a;
            T->elem[i].weight = b;
            T->length++;
        }
    }
    return OK;
}

void Destory (Table *T)
{
    if (T->elem)
        free (T->elem);

    T->elem = NULL;
    T->length = 0;
}

void Traverse (Table T,void(*Visit)(ElemType_Search))
{
    int i;
    for (i=0;i<T.length;i++){

        if (i && !(i%10))
            printf("\n"); //每10个元素打一个回车

        Visit (T.elem[i+1]);
    }

    printf("\n");
}

void PrintKey (ElemType_Search e)
{
    printf("%d ",e.key);
}
//只输出key域

#endif // BASE_C
#ifndef SCANF_H
#define SCANF_H

#include <stdio.h>
#include <string.h>
#include <stdarg.h>				//提供宏va_list、va_start、va_arg、va_end
#include <ctype.h> 				//提供isprint原型

/*
    自定义的数据录入函数,用于从文件fp
中读取格式化的输入。

    与fscanf不同之处在于此函数只会读取
西文字符,对于中文字符,则会跳过。
*/
int Scanf(FILE *fp, char *format, ...);

#endif // SCANF_H
/*********************
 *                   *
 * 文件夹: ▲01 绪论 *
 *                   *
 * 文件名: Scanf.c   *
 *                   *
 *********************/

#ifndef SCANF_C
#define SCANF_C

#include "Scanf.h"

/*
    自定义的数据录入函数,用于从文件fp
中读取格式化的输入。

    与fscanf不同之处在于此函数只会读取
西文字符,对于中文字符,则会跳过。
*/

int Scanf(FILE *fp, char *format, ...)
{
	int *i;
	char *ch, *s;
	float *f;
	int count, k, len, n;
	int tmp;
	va_list ap;

	len = strlen(format);

	va_start(ap, format);

	for(count=0,k=2; k<=len; k=k+2)
	{
		while((tmp=getc(fp))!=EOF)			//跳过所有非西文字符
		{
			if((tmp>=0 && tmp<=127))
			{
				ungetc(tmp, fp);			//遇到首个西文字符,将此西文字符放入输入流
				break;
			}
		}

		if(tmp==EOF)
			break;

		if(format[k-1]=='c')				//读取字符
		{
			ch = va_arg(ap, char*);

			if(tmp!=EOF)
				count += fscanf(fp, "%c", ch);
		}

		if(format[k-1]=='d')				//读取整型
		{
			i = va_arg(ap, int*);

			while((tmp=getc(fp))!=EOF)
			{
				if((tmp>='0' && tmp<='9') || tmp=='-' || tmp=='+')
				{
					ungetc(tmp, fp);
					break;
				}
			}

			if(tmp!=EOF)
				count += fscanf(fp, "%d", i);
		}

		if(format[k-1]=='f')				//读取浮点型
		{
			f = va_arg(ap, float*);

			while((tmp=getc(fp))!=EOF)
			{
				if((tmp>='0' && tmp<='9') || tmp=='-' || tmp=='+'|| tmp=='.' )
				{
					ungetc(tmp, fp);
					break;
				}
			}

			if(tmp!=EOF)
				count += fscanf(fp, "%f", f);
		}

		if(format[k-1]=='s')				//读取字符串
		{
			s = va_arg(ap, char*);

			while((tmp=getc(fp))!=EOF && (!isprint(tmp) || tmp==' '))
				;

			n = 0;
			if(!feof(fp))
			{
				ungetc(tmp, fp);
				while((tmp=getc(fp))!=EOF)
				{
					if(isprint(tmp) && tmp!=' ')
						s[n++] = tmp;
					else
						break;
				}
				ungetc(tmp, fp);
			}

			s[n] = '\0';

			count++;
		}
	}

	va_end(ap);

	return count;
}

#endif
/*********************
 *                   *
 * 文件夹: ▲01 绪论 *
 *                   *
 * 文件名: Scanf.c   *
 *                   *
 *********************/

#ifndef SCANF_C
#define SCANF_C

#include "Scanf.h"

/*
    自定义的数据录入函数,用于从文件fp
中读取格式化的输入。

    与fscanf不同之处在于此函数只会读取
西文字符,对于中文字符,则会跳过。
*/

int Scanf(FILE *fp, char *format, ...)
{
	int *i;
	char *ch, *s;
	float *f;
	int count, k, len, n;
	int tmp;
	va_list ap;

	len = strlen(format);

	va_start(ap, format);

	for(count=0,k=2; k<=len; k=k+2)
	{
		while((tmp=getc(fp))!=EOF)			//跳过所有非西文字符
		{
			if((tmp>=0 && tmp<=127))
			{
				ungetc(tmp, fp);			//遇到首个西文字符,将此西文字符放入输入流
				break;
			}
		}

		if(tmp==EOF)
			break;

		if(format[k-1]=='c')				//读取字符
		{
			ch = va_arg(ap, char*);

			if(tmp!=EOF)
				count += fscanf(fp, "%c", ch);
		}

		if(format[k-1]=='d')				//读取整型
		{
			i = va_arg(ap, int*);

			while((tmp=getc(fp))!=EOF)
			{
				if((tmp>='0' && tmp<='9') || tmp=='-' || tmp=='+')
				{
					ungetc(tmp, fp);
					break;
				}
			}

			if(tmp!=EOF)
				count += fscanf(fp, "%d", i);
		}

		if(format[k-1]=='f')				//读取浮点型
		{
			f = va_arg(ap, float*);

			while((tmp=getc(fp))!=EOF)
			{
				if((tmp>='0' && tmp<='9') || tmp=='-' || tmp=='+'|| tmp=='.' )
				{
					ungetc(tmp, fp);
					break;
				}
			}

			if(tmp!=EOF)
				count += fscanf(fp, "%f", f);
		}

		if(format[k-1]=='s')				//读取字符串
		{
			s = va_arg(ap, char*);

			while((tmp=getc(fp))!=EOF && (!isprint(tmp) || tmp==' '))
				;

			n = 0;
			if(!feof(fp))
			{
				ungetc(tmp, fp);
				while((tmp=getc(fp))!=EOF)
				{
					if(isprint(tmp) && tmp!=' ')
						s[n++] = tmp;
					else
						break;
				}
				ungetc(tmp, fp);
			}

			s[n] = '\0';

			count++;
		}
	}

	va_end(ap);

	return count;
}

#endif
#include <stdio.h>
#include <stdlib.h>

#include "B-Tree.h"

#define MAX 15


int main()
{
    Table T;
    BTree BT;

    FILE *fp = fopen("TestDataTable.txt","r");
    if (!fp)
        exit (-1);
    Create(fp,&T,MAX);
//    Traverse(T,Print);
//    printf("\n");
    Traverse(T,PrintKey);
    printf("\n");
//    Destory(&T);
//    printf("length = %d\n",T.length);

    printf("构造B树并输出关键字\n");
    CreateBTree(&BT,T);
    PrintBT_InOrder(BT);
    printf("\n");

    printf("层序输出\n");
    PrintBT_Level(BT);
    printf("\n\n");

    int k = 45;
    printf("删除关键字%d,\n",k);
    DeleteKey(&BT,k);
    printf("中序输出关键字\n");
    PrintBT_InOrder(BT);
    printf("\n");
    printf("层序输出\n");
    PrintBT_Level(BT);
    printf("\n\n");


    return 0;
}
TestData输入样例 :(24,0.0) (45,0.0) (50,0.0) (53,0.0) (100,0.0) (37,0.0)
(12,0.0) (61,0.0) (90,0.0) (70,0.0) (3,0.0) (30,0.0) 
(26,0.0) (85,0.0) (7,0.0)

 

    原文作者:B树
    原文地址: https://blog.csdn.net/as12cs/article/details/81546044
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞