二叉树:二叉树是每个节点最多有两个子树的树结构
二叉树分两种:顺序结构的二叉树和链式存储的二叉树
但前者容易造成空间的浪费,所以适合用于完全二叉树
而二叉树链表就不会出现这样的问题
一、二叉树链表结构: lchild-data-rchild
一个数据域和两个指针域,分别存放左孩子和右孩子的指针
二叉树链表C代码实现:
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
//二叉树分两种:顺序结构的二叉树和链式存储的二叉树
//但前者容易造成空间的浪费,所以适合用于完全二叉树
//而二叉树链表就不会出现这样的问题
// 二叉树链表: lchild-data-rchild
//一个数据域和两个指针域,分别存放左孩子和右孩子的指针
#define ClearBiTree DestroyBITree;
typedef int Status ;
int index = 1;
typedef char String[24]; //在这里我们把0号单元存放字符串的长度
String str;
typedef char Ele;
Ele Nil = ' '; //Nil为空
//赋值字符数组
Status StrAssign(String T,char *chars){
int i;
if(strlen(chars)>MAXSIZE){
return ERROR;
}else{
T[0] = strlen(chars);
for(i=1;i<=T[0];i++){
T[i] = *(chars+i-1);
}
}
return OK;
}
//定义二叉树链表的数据结构
typedef struct BiTNode{
Ele data; //数据域
struct BiTNode *lchild,*rchild; //两个指针域
}BiTNode,*BiTree;
//初始化为空的空二叉树
Status InitBiTree(BiTree *T){
*T = NULL;
return OK;
}
//摧毁一个二叉树,递归实现
void DestroyBiTree(BiTree *T){
if(*T){
if((*T)->lchild){
DestroyBiTree(&(*T)->lchild); //销毁左孩子树
}
if((*T)->rchild){
DestroyBiTree(&(*T)->rchild); //销毁右孩子树
}
free(*T);
*T = NULL;
}
}
//构造二叉树
//这里我们使用 # 作为空的判断,并赋值为NULL
//继续递归实现
void CreateBiTree(BiTree *T) {
Ele ch;
ch = str[index++];
if(ch=='#'){
*T = NULL;
} else{
*T = (BiTree)malloc(sizeof(BiTNode)); //分配结点
if(!*T){
return; //如果分配失败,返回
}
(*T)->data = ch; //赋值
CreateBiTree(&(*T)->lchild);//构造左结点
CreateBiTree(&(*T)->rchild);//构造右结点
}
}
//是否为空?
Status BiTreeEmpty(BiTree T){
if(T){
printf("该二叉树不为空\n");
return FALSE;
}else{
printf("该二叉树为空\n");
return TRUE;
}
}
//返回二叉树的深度
//递归的终止条件为0;
//由于return 对于i和j进行+1操作,
//所以实现了逐级递增
int BiTreeDepth(BiTree T){
int i,j;
if(!T){
return ERROR;
}
if(T->lchild){
i = BiTreeDepth(T->lchild);
}else{
i = 0;
}
if(T->rchild){
j = BiTreeDepth(T->rchild);
}else{
j = 0;
}
return i>j?i+1:j+1;
}
//返回二叉树的根
Ele Root(BiTree T){
if(BiTreeEmpty(T)){
return Nil;
}else{
return T->data;
}
}
//三种遍历方式
//前序
void PreOrder(BiTree T){
if(T==NULL){
return;
}
printf("%c ",T->data); /* 先结点,显示结点数据 */ //根-->左-->右
PreOrder(T->lchild); //再左子树
PreOrder(T->rchild); //右子树
}
//中序
void MiddleOrder(BiTree T){
if(T==NULL){
return ;
}
MiddleOrder(T->lchild); //左-->根--->右
printf("%c ",T->data);
MiddleOrder(T->rchild);
}
//后序
void PostOrder(BiTree T){
if(T==NULL){
return;
}
PostOrder(T->lchild); //左-->右-->根
PostOrder(T->rchild);
printf("%c ",T->data);
}
int main(){
int i;
BiTree T;
Ele e;
InitBiTree(&T);
StrAssign(str,"ABDH#K###E##CFI###G#J##");
CreateBiTree(&T);
printf("树的深度=%d \n\n",BiTreeDepth(T));
printf("树的根=%c \n",Root(T));
//前序遍历
printf("前序遍历:");
PreOrder(T);
printf("\n");
//中序遍历
printf("中序遍历:");
MiddleOrder(T);
printf("\n");
//后序遍历
printf("后序遍历:");
PostOrder(T);
printf("\n\n");
//清空
printf("清空后\n");
DestroyBiTree(&T);
BiTreeEmpty(T);
return 0;
}
二、二叉排序树
一般我们把小于结点p放在p结点的左边–p->lchild,将大于p结点的放在右边–p->rchild.
删除结点图:
1、
2、
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
typedef int Status;
//二叉树结构
typedef struct BiTNode{
int data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
/* 递归查找二叉排序树T中是否存在key, */
/* 指针f指向T的双亲,其初始调用值为NULL */
/* 若查找成功,则指针p指向该数据元素结点,并返回TRUE */
/* 否则指针p指向查找路径上访问的最后一个结点并返回FALSE */
Status SearchBiTree(BiTree T,int key,BiTree f,BiTree *p){
if(!T){ //查找不成功
*p = f;
return FALSE;
}else if(key==T->data){ //查找成功
*p = T;
return TRUE;
}else if(key<T->data){
return SearchBiTree(T->lchild,key,T,p); //继续左子树查找
}else {
return SearchBiTree(T->rchild,key,T,p); //继续右子树找
}
}
//插入一个点(数据)
Status InsertBiTree(BiTree *T,int key){
BiTree p,s;
if(!SearchBiTree(*T,key,NULL,&p)){
s = (BiTree)malloc(sizeof(BiTNode)); //新结点
s->data = key;
s->lchild = s->rchild = NULL;
if(!p){ //如果查找的二叉树刚好为空,则插入作为根结点
*T = s;
}else if(key<p->data){ //插入左边
p->lchild = s;
}else if(key>p->data){ //插入右边
p->rchild = s;
}
return TRUE; //不是关键字,并且插入成功
}else{
return FALSE; //说明是二叉树的关键字
}
}
//删除一个结点p
Status Delete(BiTree *p){
BiTree q,s;
if((*p)->rchild==NULL){ //右空,只需重接左结点
q = *p;
*p = (*p)->lchild;
free(q);
}else if((*p)->lchild==NULL){ //左空, 只需重接右结点
q =*p;
*p = (*p)->rchild;
free(q);
}else{ //左右均不为空,那就需要重接左右结点
q = *p;
s = (*p)->lchild; //先转左边,然后右到尽头,最终找到删除点的前驱,,
while(s->rchild){ //个人觉得这里的删除并不是唯一的,也可以先右再左 ,
q = s; //只要符合大小的排序就行
s = s->rchild;
}
(*p)->data = s->data;
if(q!=(*p)){
q->rchild = s->lchild; /* 重接q的右子树 */
}else{
q->lchild = s->lchild;/* 重接q的左子树 */
}
free(s);
}
return TRUE;
}
//递归找到要删除的结点
Status DeleteBiTree(BiTree *T,int key){
if(!T){
return FALSE;
}else if((*T)->data == key){
Delete(T);
}else if(key<(*T)->data){
return DeleteBiTree(&(*T)->lchild,key);
}else{
return DeleteBiTree(&(*T)->rchild,key);
}
}
//遍历
void PreOrderTraverse(BiTree T)
{
if(T==NULL)
return;
printf("%d ",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
PreOrderTraverse(T->lchild); /* 再先序遍历左子树 */
PreOrderTraverse(T->rchild); /* 最后先序遍历右子树 */
}
int main(){
int arr[10] = {62,88,58,47,35,73,51,99,37,93};
BiTree T = NULL;
int i;
//此时将数组变成一颗排序二叉树比较简单
for(i=0;i<10;i++){
InsertBiTree(&T,arr[i]);
}
//当画出图后,我们用前序遍历的遍历出来的结果和输出的结果是一致的
PreOrderTraverse(T);
printf("\n");
DeleteBiTree(&T,47);
PreOrderTraverse(T);
printf("\n");
DeleteBiTree(&T,93);
PreOrderTraverse(T);
return 0;
}