注释详细完整且健壮的二叉树的顺序/链式以及线索二叉树实现与常规操作的代码:
//
// main.c
// SqBiTree
//
// Created by Eason on 2020/8/7.
// Copyright © 2020 Eason. All rights reserved.
//
#include
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100
typedef int Status; //作为返回状态 如ERROR OK FALSE TRUE,其实也就是返回一个int值
typedef int ElemType; //元素数据类型,这里就先定义为int
typedef ElemType BiTree[MAXSIZE]; //二叉树的元素存储数组,通过数组的下标实现二叉树结构
//这里一定要注意的是这里存储容量MAXSIZE 因为获取孩子的判断是要判断T[2*MAXSIZE]这个数量级的,所以实际存储容量要比真实存储的内容大一倍
//比如放了10个元素,这时候我的存储容量至少为21以上,不然判断的时候数组越界会报错,数值原因见获取孩子方法
ElemType null=0; //将0作为空元素
//初始化一个二叉树
Status initTree(BiTree T){
for(int i=0;i<MAXSIZE;i++){ //将二叉树中所有元素位置置为空元素标志null即0
T[i] = null; //即将空元素存入
}
printf("二叉树初始化完成\n");
return OK;
}
//清空一个二叉树(也可以使用#define clearTree initTree)
Status clearTree(BiTree T){ //同初始化一个二叉树相同
for(int i=0;i<MAXSIZE;i++){
T[i] = null;
}
return OK;
}
//创建一个二叉树
Status creatBTree(BiTree T){
printf("创建一个元素为1-10的二叉树\n");
int i=0; //当前数组位置计位器
int data=1; //存入的数据元素
while(data<=10){ //准备存入1-10
T[i]=data; //将当前数据元素存入当前数组位置处
if(i!=0 && i>=MAXSIZE && T[(i+1)/2-1]==null && T[i]!=0){ //即当前结点是非根无双亲且不为空的结点,即为一个悬浮的结点,那么是不能给这个结点赋值的
printf("出现了无双亲的非根结点\n");
return ERROR;
}
i++; //数组位置进一
data++; //数据元素+1
}
return OK;
}
//判断二叉树是否为空
Status isEmpty(BiTree T){
if(T[0]==null){ //若二叉树没有根结点,则表明当前二叉树是空二叉树
return TRUE;
}else{
return FALSE;
}
}
//获取当前二叉树的根结点数据
Status getRoot(BiTree T, int *e){
if(isEmpty(T)){ //如果当前二叉树为空,则没有根结点
printf("当前二叉树为空,无根结点数据\n");
return ERROR;
}
*e = T[0]; //如果不为空则将根结点的数据存入e中供返回查看
return OK;
}
//获取二叉树的深度
int getDepth(BiTree T){
int i; //数组位置计位器
for(i=MAXSIZE-1;i>=0;i--){ //从二叉树数组的最后位置向前找
if(T[i]!=null){ //找到最后一个不为空的元素后推出循环
break;
}
}
i++; //将i作为位置
int depth = 1; //初始depth为1
while(i>=powl(2, depth)){ //2的depth次方,当2的depth次方大于当前最后元素位置时,depth就为当前二叉树的深度
depth++; //若当前深度仍不包含最后一个元素,则继续加深深度
}
return depth; //返回深度值
}
//获取二叉树某个结点的parent(获取二叉树中e元素的双亲结点元素)
ElemType getParent(BiTree T, ElemType e){
if(isEmpty(T)){ //判断当前二叉树是否为空
printf("二叉树为空,无法查找\n");
return ERROR;
}
for(int i=0;i<MAXSIZE;i++){ //寻找指定数值结点的位置
if(T[i]==e){ //若当前位置元素与指定元素相等
return T[(i+1)/2-1]; //则返回此位置元素的双亲结点数据,若返回0则表示无双亲结点
}
}
printf("二叉树中无此结点,无法查找\n");
return ERROR;
}
//获取二叉树某个结点的左孩子(获取二叉树中e元素的左孩子)
ElemType getLeftChild(BiTree T, ElemType e){
if(isEmpty(T)){ //判断当前二叉树是否为空
printf("二叉树为空,无法查找\n");
return ERROR;
}
for(int i=0;i<MAXSIZE;i++){ //寻找指定数值结点的位置
if(T[i]==e){ //若当前位置元素与指定元素相等
return T[i*2+1]; //则返回此位置元素的左孩子结点的数据,若返回0则表示无左孩子
}
}
return ERROR;
}
//获取二叉树某个结点的右孩子(获取二叉树中e元素的右兄弟)
ElemType getRightChild(BiTree T, ElemType e){
if(isEmpty(T)){ //判断当前二叉树是否为空
printf("二叉树为空,无法查找\n");
return ERROR;
}
for(int i=0;i<MAXSIZE;i++){ //寻找指定数值结点的位置
if(T[i]==e){ //若当前位置元素与指定元素相等
return T[i*2+2]; //则返回此位置元素的右孩子结点的数据,若返回0则表示无右孩子
}
}
return ERROR;
}
//获取二叉树某个结点的左兄弟(获取二叉树中e元素的左兄弟)
ElemType getLeftBrother(BiTree T, ElemType e){
if(isEmpty(T)){ //判断当前二叉树是否为空
printf("二叉树为空,无法查找\n");
return ERROR;
}
for(int i=0;i<MAXSIZE;i++){ //寻找指定数值结点的位置
if(T[i]==e && i%2==0){ //若当前位置元素与指定元素相等,并且本身位置为右
return T[i-1]; //则返回此位置右孩子的数据
}
}
return 0; //若无可满足条件的元素则返回0,也就是说若返回0则表示这个结点是左结点,也可能是没有左兄弟
}
//获取二叉树某个结点的右兄弟(获取二叉树中e元素的右兄弟)
ElemType getRightBrother(BiTree T, ElemType e){
if(isEmpty(T)){ //判断当前二叉树是否为空
printf("二叉树为空,无法查找\n");
return ERROR;
}
for(int i=0;i<MAXSIZE;i++){ //寻找指定数值结点的位置
if(T[i]==e && i%2!=0){ //若当前位置元素与指定元素相等,并且本身位置为左
return T[i+1]; //则返回此位置左孩子的数据
}
}
return 0; //若无可满足条件的元素则返回0,也就是说若返回0则表示这个结点是右结点,也可能是没有又兄弟
}
//获取某指定位置的结点数据(获取二叉树中第level层第num个结点元素)
ElemType getValue(BiTree T, int level, int num){
if(isEmpty(T)){ //判断当前二叉树是否为空
printf("二叉树为空,无法查找\n");
return ERROR;
}
return T[(int)pow(2, level-1)+num-2];
}
//替换某指定位置的结点数据(将二叉树第level层第num个数据元素替换为value)
Status replace(BiTree T, int level, int num, ElemType value){
int i = (int)pow(2, level-1)+num-2; //将指定的level层第num个数据元素替换成在数组中的下标位置
if(T[i+1/2-1]==null){ //若指定位置的结点无双亲结点则无法赋值
printf("此结点的双亲结点为空,无法赋值\n");
return ERROR;
}
if(value==null && (T[i*2+1]!=null || T[i*2+2]!=null)){ //若此结点有孩子结点那么则不可为这个结点赋控制
printf("此结点的孩子结点不为空,不可以为其赋空值\n");
return ERROR;
}
T[i] = value; //将value存入指定位置
return OK;
}
//--------------------二叉树的遍历----------------------
//均利用递归的方式进行遍历
//先序访问输出(供先序遍历调用)
void preOrderVisit(BiTree T, int now){ //先序遍历即先访问根结点然后遍历左子树最后访问右子树
printf("%d ", T[now]); //访问当前结点数据并打印
if(T[now*2+1]!=null){ //若当前位置有左子树则先访问左子树
preOrderVisit(T, now*2+1); //访问左子树
}
if(T[now*2+2]!=null){ //若当前位置有右子树则访问右子树
preOrderVisit(T, now*2+2); //访问右子树
}
}
//先序遍历
Status preOrderTraverse(BiTree T){
if(isEmpty(T)){ //判断二叉树是否为空
printf("二叉树为空,无法遍历\n");
return ERROR;
}
preOrderVisit(T, 0); //调用前序遍历函数
printf("\n");
return OK;
}
//中序访问输出(供中序遍历调用)
void inOrderVisit(BiTree T, int now){ //中序遍历即先遍历左子树后访问根结点最后遍历右子树(参考前序遍历,只是访问次序变更)
if(T[now*2+1]!=null){
inOrderVisit(T, now*2+1);
}
printf("%d ", T[now]);
if(T[now*2+2]!=null){
inOrderVisit(T,now*2+2);
}
}
//中序遍历
Status inOrderTraverse(BiTree T){
if(isEmpty(T)){
printf("二叉树为空,无法遍历\n");
return ERROR;
}
inOrderVisit(T, 0);
printf("\n");
return OK;
}
//后序访问输出(供后序遍历使用)
void postOrderVisit(BiTree T, int now){ //后序遍历即先遍历左子树后遍历右子树最后访问根结点(参考前序遍历,只是访问次序变更)
if(T[now*2+1]!=null){
postOrderVisit(T,now*2+1);
}
if(T[now*2+2]!=null){
postOrderVisit(T,now*2+2);
}
printf("%d ", T[now]);
}
//后序遍历
Status postOrderTraverse(BiTree T){
if(isEmpty(T)){
printf("二叉树为空,无法遍历\n");
return ERROR;
}
postOrderVisit(T, 0);
printf("\n");
return OK;
}
//层序遍历
Status levelOrderTraverse(BiTree T){ //即一层一层的访问元素,这里主要是判断哪些元素在哪一层
if(isEmpty(T)){ //判断二叉树是否为空
printf("二叉树为空,无法遍历\n");
return ERROR;
}
int level = 1; //初始为第一层
int e; //用于存储获取的元素数值
for(int i=level;i<=getDepth(T);i++){ //从第一层到此二叉树的深度
printf("第%d层:", i); //打印当前层数
for(int j=1;j<=pow(2,i-1);j++){ //从本层的第一个元素到本层的最后元素
e = getValue(T, i, j); //获取当前位置的元素数值
printf("%d ", e); //访问并打印当前获得的元素数值
}
printf("\n");
}
return OK;
}
//测试
int main(int argc, const char * argv[]) {
BiTree T;
int e;
initTree(T);
printf("初始化后的二叉树是否为空?(1是0否):%d\n", isEmpty(T));
creatBTree(T);
printf("创建完成后二叉树是否为空?(1是0否):%d\n", isEmpty(T));
printf("前序遍历二叉树T:\n");
preOrderTraverse(T);
printf("中序遍历二叉树T:\n");
inOrderTraverse(T);
printf("后序遍历二叉树T:\n");
postOrderTraverse(T);
printf("层序遍历二叉树T:\n");
levelOrderTraverse(T);
getRoot(T, &e);
printf("当前二叉树的根结点为:%d\n", e);
printf("当前二叉树的深度为:%d\n", getDepth(T));
e = getParent(T, 5);
printf("结点5的parent为:%d\n", e);
e = getLeftBrother(T, 5);
printf("结点5的左兄弟为:%d\n", e);
e = getRightBrother(T, 5);
printf("结点5的右兄弟为:%d\n", e);
e = getLeftChild(T, 5);
printf("结点5的左孩子为:%d\n", e);
e = getRightChild(T, 5);
printf("结点5的右孩子为:%d\n", e);
e = getValue(T, 4, 2);
printf("获取第4层的第2个元素:%d\n", e);
replace(T, 4, 2, 666);
e = getValue(T, 4, 2);
printf("将第4层的第2个元素替换为666后得:%d\n", e);
printf("将二叉树清空后可得:\n");
clearTree(T);
levelOrderTraverse(T);
return 0;
}
//
// main.c
// LinkBiTree
//
// Created by Eason on 2020/8/10.
// Copyright © 2020 Eason. All rights reserved.
//
#include
#include
#include "math.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 //二叉树的最大长度
typedef char ElemType; //二叉树的数据元素类型
typedef int Status; //用int作为状态类型,对应预定义的OK ERROR TRUE FALSE
int i=1; //创建二叉树时的字符串定位器
typedef char String[MAXSIZE]; //定义一个字符数组作为字符串来创建二叉树
String S; //数组对象
typedef struct BTNode //二叉树的链式存储结构
{
ElemType data;
struct BTNode *leftChild,*rightChild;
}BTNode,*BTree; //BTree = *BTNode
//字符串初始化(将ch长度地址范围里的字符依次读入字符串中)
Status initString(String S, char *ch){
if(strlen(ch)>=MAXSIZE){ //判断用来初始化的字符长度是否超出预定义的字符串长度
return ERROR;
}
S[0] = strlen(ch); //S[0]用来存储字符串长度
int loc=0; //ch定位器
for(int i=1;i<=S[0];i++){ //依次循环读入字符串中
S[i] = *(ch+loc); //当前ch下一个地址的ch
loc++; //加到第几个位置的ch
}
printf("字符串初始化完成\n");
return OK;
}
//打印字符串
Status printString(String S){ //没啥好说的就是个循环遍历打印初始化过的字符串
for(int i=0;i<=strlen(S);i++){
printf("%c ", S[i]);
}
printf("\n");
return OK;
}
//初始化链二叉树
Status initTree(BTree *T){ //将二叉树的根结点置为空则初始化成功
*T=NULL;
printf("初始化成功!\n");
return OK;
}
//判断链二叉树是否为空
Status isEmpty(BTree T){ //若二叉树的根结点为空,则此树为空
if(T==NULL){
return TRUE;
}else{
return FALSE;
}
}
//创建链二叉树(利用初始化的字符串向二叉树中读入来创建二叉树)
void createBTree(BTree *T){
ElemType ch; //临时字符存储器
ch = S[i++]; //将当前数组的位置的字符读入ch中后位置++
if(ch=='#'){ //若字符为‘#’则说明此位置为空
*T=NULL; //则将此位置置为空
}else{
*T=(BTree)malloc(sizeof(BTNode)); //若此处的字符不为空,则为其分配一个结点大小的内存
if(!*T){ //判断能否为新结点申请内存
printf("内存分配失败\n");
exit(OVERFLOW);
}else{ //分配内存成功
(*T)->data = ch; //将此位置的数据置为对应的字符值
createBTree(&(*T)->leftChild); //递归继续寻找是否左孩子结点存在若存在则按照步骤分配内存成立对应的结点,若无则返回向下
createBTree(&(*T)->rightChild); //此时是若结点无左孩子则递归寻找是否是有右孩子若有则按照步骤分配内存成立对应的结点,若无则返回上一级的结点判断右孩子继续递归
}
}
}
//销毁二叉树(清空二叉树clearBTree()相同)
Status destroyBTree(BTree *T){
if(*T){ //若当前结点存在
if((*T)->leftChild){ //则判断是否有左孩子,若有则先销毁左孩子
destroyBTree(&(*T)->leftChild); //递归销毁左孩子
}
if((*T)->rightChild){ //无左孩子则判断是否有右孩子,若有则销毁右孩子
destroyBTree(&(*T)->rightChild); //递归销毁右孩子
}
free(*T); //若都没有则释放当前结点
*T=NULL; //将结点置空
}
return OK;
}
//获得二叉树的深度
int getDepth(BTree *T){
int deep = 0; //深度计数
if(*T!=NULL){ //如果当前结点不为空
int leftdeep = getDepth(&(*T)->leftChild); //则先判断其左子树的深度
int rightdeep = getDepth(&(*T)->rightChild); //然后判断右子树的深度
deep = leftdeep>=rightdeep?leftdeep+1:rightdeep+1; //二叉树的深度对应左右子树中最大的一个 再加上当前的深度1
}
return deep; //将深度值返回
}
//获取根结点的数据
char getRoot(BTree T){
if(isEmpty(T)){ //判断二叉树是否为空
printf("根结点不存在,无法获取\n");
return ERROR;
}
return T->data; //若二叉树不为空,则返回当前结点的数据域
}
//获取指定结点的数据
char getValue(BTree T){
if(isEmpty(T)){ //判断当前结点是否存在
printf("当前结点不存在,无法获取\n");
return ERROR;
}
return T->data; //返回当前结点的数据
}
//为指定结点的数据更换数值(将结点T的数据域更换为指定的value值)
Status replaceValue(BTree *T, char *value){
(*T)->data = *value; //将制定的value值替换入指定结点的数据域中
return OK;
}
//前序遍历
void preOrederTraverse(BTree T){
if(T==NULL){ //判断当前结点是否存在,不存在则推出当前方法返回上一级,若无上一级则退出
return;
}
printf("%c ", T->data); //先访问根结点
preOrederTraverse(T->leftChild); //后遍历左子树
preOrederTraverse(T->rightChild); //最后遍历右子树
}
//中序遍历
void inOrderTraverse(BTree T){
if(T==NULL){ //判断当前结点是否存在,不存在则推出当前方法返回上一级,若无上一级则退出
return;
}
inOrderTraverse(T->leftChild); //先遍历左子树
printf("%c ", T->data); //后访问根结点
inOrderTraverse(T->rightChild); //最后遍历右子树
}
//后序遍历
void postOrderTraverse(BTree T){
if(T==NULL){ //判断当前结点是否存在,不存在则推出当前方法返回上一级,若无上一级则退出
return;
}
postOrderTraverse(T->leftChild); //先遍历左子树
postOrderTraverse(T->rightChild); //再遍历右子树
printf("%c ", T->data); //最后访问根结点
}
//测试
int main()
{
BTree T;
initTree(&T);
initString(S, "EASO#N###H##EGO###G#O##");
printf("字符串初始为:\n");
printString(S);
printf("创建二叉树T\n");
createBTree(&T);
printf("完成创建二叉树T\n");
printf("二叉树的深度为:%d\n", getDepth(&T));
printf("前序遍历二叉树T:\n");
preOrederTraverse(T);
printf("\n中序遍历二叉树T:\n");
inOrderTraverse(T);
printf("\n后序遍历二叉树T:\n");
postOrderTraverse(T);
printf("\n此时的根结点为:%c\n", getRoot(T));
printf("将二叉树销毁后遍历可得:\n");
destroyBTree(&T);
preOrederTraverse(T);
printf("此时的根结点为:%c\n", getRoot(T));
return 0;
}
//
// main.c
// ThreadBTree
//
// Created by Eason on 2020/8/16.
// Copyright © 2020 Eason. All rights reserved.
//
//线索二叉树是将二叉树中那些未有左右孩子的结点利用起来用来指向前驱与后继结点
//线索二叉树会有一个头结点用来方便作为第一个结点的前驱与最后一个结点的后继,头结点的左孩子指向线索二叉树的根结点
#include
#include
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define Thread 1 //线索标志
#define Normal 0 //非线索标志
typedef char ElemType; //二叉树数据类型
typedef int Status; //状态变量对应OK ERROR TRUE FALSE
typedef struct TBTNode{ //线索二叉树的存储结构,多了判断孩子指针域是否为线索的判断位置
ElemType data; //数据域
struct TBTNode *leftChild, *rightChild; //左右孩子指针
int lefttag; //左孩子是否为线索标志 Thread为线索normal为非线索
int righttag; //右孩子标志同上
}TBTNode,*TBTree; //结构别名TBTNode 指针TBTree = *TBTNode
char null = '#'; //用‘#’来表示此结点为空结点
TBTree pre; //用在线索化过程中,一直作为刚刚访问过的结点
//创建线索二叉树(控制台输入来创建)
Status creatTBTNode(TBTree *T){
ElemType temp; //临时数据类型变量,用来存储控制台的输入
scanf("%c", &temp); //控制台输入存储临时变量temp 每次读入一个字符
if(temp==null){ //如果当前temp为‘#’
*T=NULL; //则将此结点置空
}else{
*T = (TBTree)malloc(sizeof(TBTNode)); //若为当前结点的数据域非空则为新结点分配内存空间
if(!*T){ //如果新结点不存在,即未能成功分配内存
printf("内存分配失败导致结点创建失败\n");
return ERROR;
}
(*T)->data = temp; //将当前临时数据存入当前结点的数据域
creatTBTNode(&(*T)->leftChild); //递归创建左子树
if((*T)->leftChild){ //若当前结点有左子树
(*T)->lefttag=Normal; //则将左线索标志置为Normal即非线索0
}
creatTBTNode(&(*T)->rightChild); //递归创建右子树
if((*T)->rightChild){ //若当前结点有右子树
(*T)->righttag=Normal; //则将右线索标志置为Normal即非线索0
}
}
return OK;
}
//中序遍历线索化(从一个结点出发线索化)
void inOrderThread(TBTree T){
if(T){ //如果结点存在
inOrderThread(T->leftChild); //递归线索化此结点的左子树
if(!T->leftChild){ //若此结点不存在左孩子
T->lefttag=Thread; //则将此结点的左线索标志置为Thread即线索1
T->leftChild = pre; //将此结点的左孩子指向刚刚访问过的结点即前驱结点,即左孩子作为前驱结点的线索指向此结点的前驱结点
}
if(!pre->rightChild){ //若前驱结点的右孩子不存在
pre->righttag=Thread; //则将前驱结点的右线索标志置为Thread即线索1
pre->rightChild = T; //将前驱结点的右孩子指向当前结点即后继结点,即右孩子作为后继结点的线索指向此结点的后继结点
}
pre = T; //完成后此结点作为前驱结点之后再次循环进行做准备
inOrderThread(T->rightChild); //递归线索化此结点的右子树
}
}
//中序遍历线索化二叉树T(head为指向线索二叉树T根结点的头结点)
Status threadBTree(TBTree *head, TBTree T){
*head = (TBTree)malloc(sizeof(TBTNode)); //为头结点分配内存空间
if(!*head){ //若为头结点分配内存空间失败
printf("头结点内存分配失败\n");
return ERROR;
}
(*head)->lefttag=Normal; //头结点的左线索标志为Normal 因为左孩子指向二叉树的根结点
(*head)->righttag=Thread; //头结点的右线索标志为Thread 因为其指向二叉树遍历的最后一个结点,即为最后一个结点的后继结点
(*head)->rightChild=(*head); //初始化为指向指向自己的后继结点
if(!T){ //如果当前二叉树不存在
(*head)->rightChild=*head; //头结点的左孩子也指向自己,说明只有指向自己的头结点而无二叉树
}else{
(*head)->leftChild=T; //如果二叉树存在,则头结点指向此二叉树的根结点
pre = (*head); //当前刚刚访问的为头结点(这点可以看出头结点的作用,方便对二叉树所有结点都执行相同的线索化操作,即便是第一个还是最后一个结点)
inOrderThread(T); //中序线索化二叉树T
pre->righttag=Thread; //中序二叉树后pre指向的中序遍历二叉树的最后一个结点
pre->rightChild=*head; //则最后一个结点的右孩子指向头结点,即后继结点为头,变成了一个循环链表
(*head)->rightChild=pre; //头结点的右孩子也指向遍历二叉树的最后一个结点,
}
return OK;
}
//中序遍历输出二叉树T(从线索二叉树的头结点开始)
Status inOrderTraverse(TBTree H){
TBTree T; //定义结点T作为二叉树的根结点
T=H->leftChild; //头结点的左孩子即为树的根结点
while(H!=T){ //若不是指向头指针自身,即表示当前二叉树存在
while(T->lefttag==Normal){ //如果当前左线索标志为Normal表示当前右左孩子结点
T = T->leftChild; //则指向左孩子结点,即先访问左孩子结点(即先遍历二叉树的左子树)
}
printf("%c ", T->data); //访问当前结点的数据域
while(T->righttag==Thread && T->rightChild!=H){ //如果右线索标志为Thread则表示右孩子表示的为后继结点
T = T->rightChild; //则指向此后继结点
printf("%c ", T->data); //访问当前结点的后继结点
}
T = T->rightChild; //继续循环遍历右子树
}
return OK;
}
//测试
int main(int argc, const char * argv[]) {
TBTree H, T;
printf("请按前序顺序输入要创建的二叉树序列('#'为空):\n"); //例如:EOAE##S##HN###OT##P##
creatTBTNode(&T);
threadBTree(&H, T);
printf("中序遍历线索二叉树:\n");
inOrderTraverse(H);
printf("\n");
return 0;
}
参考:
《大话数据结构》