二叉树的操作--C语言实现
树是一种比较复杂的数据结构,它的操作也比较多。常用的有二叉树的创建,遍历,线索化,线索化二叉树的遍历,这些操作又可以分为前序,中序和后序。其中,二叉树的操作有递归与迭代两种方式,鉴于我个人的习惯,在这里我是使用递归来操作的,另外,层序遍历需要借助队列来实现。代码亲测,可执行。
#include<stdio.h>
#include<malloc.h>
typedef int ElemType; //数据类型 typedef struct BiTreeNode //二叉树结构体
{
ElemType date; //结点数据
struct BiTreeNode *lChild; //左指针
int lFlag; //左标记(==0时,左指针存储左孩子结点;==1时,左指针存储前驱结点)
struct BiTreeNode *rChild; //右指针
int rFlag; //右标记(==0时,右指针存储右孩子结点;==1时,右指针存储后继结点)
}*BiTree;
BiTree pre; typedef struct QNode //结点结构体
{
BiTree date; //结点数据
struct QNode *next; //结点指针
}*LinkQuePtr; //结点名 typedef struct //链队结构体
{
LinkQuePtr front; //队头结点
LinkQuePtr rear; //队尾结点
}LinkQue; //队名 LinkQuePtr head = (LinkQuePtr)malloc(sizeof(QNode)); //头结点 /*链队的入队操作*/
int EnQueue(LinkQue *Q, BiTree e)
{
LinkQuePtr s = (LinkQuePtr)malloc(sizeof(QNode)); //申请新结点空间
if(!s)
return ;
s->date = e; //新结点的数据等于e
s->next = NULL; //新结点的指针指向空
Q->rear->next = s; //原队尾结点的指针指向新结点
Q->rear = s; //队尾指针指向新结点(使新结点成为队尾结点)
return ;
} /*链队的出队操作*/
int DeQueue(LinkQue *Q)
{
if(Q->front == Q->rear) //判断队列是否为空
return ;
LinkQuePtr s = (LinkQuePtr)malloc(sizeof(QNode)); //申请结点空间s
s = Q->front->next; //s结点等于队头结点(头指针所指向的结点)
Q->front->next = s->next; //头结点的指针指向s结点的下一结点(使s结点的下一结点成为队头元素)
if(Q->rear == s) //判断s是否为队尾元素,若是,说明队列中仅有一个结点
Q->rear = Q->front; //使队尾结点指向头结点
free(s); //释放s结点
return ;
} /*创建二叉树函数*/
void CreatBiTree(BiTree *T)
{
ElemType e; //结点数据
scanf("%d", &e);
if(e == -) //如果输入为-1,当前结点为空
(*T) = NULL;
else
{
(*T) = (BiTree)malloc(sizeof(BiTreeNode)); //申请结点空间
(*T)->date = e; //为当前结点赋值
printf("请输入当前结点 %d 的左孩子,若没有左孩子,请输入-1\n", e);
CreatBiTree(&((*T)->lChild)); //递归创建左子树
printf("请输入当前结点 %d 的右孩子,若没有右孩子,请输入-1\n", e);
CreatBiTree(&((*T)->rChild)); //递归创建右子树
}
} /*先序遍历二叉树*/
void PreorderTraversal(BiTree T)
{
if(T == NULL) //判空
return;
printf("%d ", T->date); //打印当前结点
PreorderTraversal(T->lChild); //递归遍历左子树
PreorderTraversal(T->rChild); //递归遍历右子树
} /*中序遍历二叉树*/
void InorderTraversal(BiTree T)
{
if(T == NULL) //判空
return;
InorderTraversal(T->lChild); //递归左子树
printf("%d ", T->date); //打印当前结点
InorderTraversal(T->rChild); //递归右子树
} /*后序遍历二叉树*/
void PostorderTraversal(BiTree T)
{
if(T == NULL) //判空
return;
PostorderTraversal(T->lChild); //递归左子树
PostorderTraversal(T->rChild); //递归右子树
printf("%d ", T->date); //打印当前结点
} /*层序遍历二叉树*/
void LevelTraversal(BiTree T)
{
if(T == NULL) //判空
return;
LinkQue Q; //创建队Q
Q.front = head; //初始化队列
Q.rear = head;
EnQueue(&Q, T); //将根结点入队
while(Q.front != Q.rear) //判断队列是否为空
{
BiTree s = Q.front->next->date; //获得队列中第一个结点的数据
printf("%d ", s->date); //打印当前结点的数据
if(s->lChild) //若该结点有左孩子,将其左孩子入队
EnQueue(&Q, s->lChild);
if(s->rChild) //若该结点有右孩子,将其右孩子入队
EnQueue(&Q, s->rChild);
DeQueue(&Q); //将队列中第一个结点出队
}
} /*计算树的深度*/
int Depth(BiTree T)
{
if(T == NULL) //如果当前结点为空,返回0
return ;
int L = Depth(T->lChild); //遍历左子树
int R = Depth(T->rChild); //遍历右子树
if(L > R) //取最大值返回
return (L+);
else
return (R+);
} /*中序遍历线索化*/
void Inorder_Traversal_Cue(BiTree &T)
{
if(T)
{
Inorder_Traversal_Cue(T->lChild); //递归左子树
if(T->lChild == NULL) //左孩子为空
{
T->lFlag = ; //左标记为1
T->lChild = pre; //左指针指向前一结点
}
else
{
T->lFlag = ;
}
if(pre->rChild == NULL) //前一结点的右孩子为空
{
pre->rFlag = ; //前一结点的右标记为1
pre->rChild = T; //前一结点的右指针指向当前结点
}
else
{
T->rFlag = ;
}
pre = T; //使当前结点成为前一结点
Inorder_Traversal_Cue(T->rChild); //递归右子树
}
} /*添加头结点,将二叉树线索化*/
BiTree AddHead(BiTree &T)
{
BiTree head = (BiTree)malloc(sizeof(BiTreeNode)); //申请头结点
head->lFlag = ; //头结点左标记为0
head->rFlag = ; //右标记为1
if(!T) //若二叉树为空
{
head->lChild = head; //左指针回指
head->rChild = head; //右指针回指
return NULL;
}
pre = head; //前一结点指向头结点
head->lChild = T; //头结点的左孩子指向根结点
Inorder_Traversal_Cue(T); //中序线索化
pre->rChild = head; //为最后一个结点设置右指针指向头结点
pre->rFlag = ; //右标记为1
head->rChild = pre; //头结点的右指针指向尾结点
return head; //返回头结点
} /*遍历线索二叉树*/
void TreeCueTraversal(BiTree T)
{
BiTree p = T->lChild; //申请结点p指向根结点
while(p != T) //根结点不为空
{
while(p->lFlag == ) //一直寻找第一个左标记为1的结点
p = p->lChild;
printf("%d ", p->date); //打印第一个结点
while(p->rFlag == && p->rChild != T) //若右标记是1,且右孩子不是头结点
{
p = p->rChild; //一直遍历
printf("%d ", p->date);
}
p = p->rChild; //若右标记为0,p赋值为p的右子树
}
printf("\n");
} void main()
{
BiTree T; //声明一个树变量
int dep = ; //树深度变量 while(true)
{
printf("请选择对二叉树的操作:\n");
printf("1.创建\n");
printf("2.先序遍历\n");
printf("3.中序遍历\n");
printf("4.后序遍历\n");
printf("5.层序遍历\n");
printf("6.获取深度\n");
printf("7.中序线索化\n");
printf("8.遍历线索化二叉树\n");
printf("9.退出\n");
int a;
scanf("%d", &a);
switch(a)
{
case :
printf("请输入根节点:\n");
CreatBiTree(&T);
break;
case :
PreorderTraversal(T);
break;
case :
InorderTraversal(T);
break;
case :
PostorderTraversal(T);
break;
case :
LevelTraversal(T);
break;
case :
dep = Depth(T);
printf("树的深度为 %d\n", dep);
break;
case :
T = AddHead(T);
break;
case :
TreeCueTraversal(T);
break;
case :
return;
default:
printf("选择错误\n");
break;
}
}
}
二叉树的操作--C语言实现的更多相关文章
- 二叉树的基本操作(C语言版)
今天走进数据结构之二叉树 二叉树的基本操作(C 语言版) 1 二叉树的定义 二叉树的图长这样: 二叉树是每个结点最多有两个子树的树结构,常被用于实现二叉查找树和二叉堆.二叉树是链式存储结构,用的是二叉 ...
- neo4j初次使用学习简单操作-cypher语言使用
Neo4j 使用cypher语言进行操作 Cypher语言是在学习Neo4j时用到数据库操作语言(DML),涵盖对图数据的增删改查 neo4j数据库简单除暴理解的概念: Neo4j中不存在表的概念, ...
- 栈的实现与操作(C语言实现)
栈的定义 1, 栈是一种特殊的线性表 2,栈仅能在线性表的一端进行操作 3,栈顶(Top): 同意操作的一端 同意操作的一端 4,栈底(Bottom): ,不同意操作的一端 不同意操作 ...
- C/C++二叉树搜索树操作集
啥是二叉查找树 在数据结构中,有一个奇葩的东西,说它奇葩,那是因为它重要,这就是树.而在树中,二叉树又是当中的贵族.二叉树的一个重要应用是它们在查找中的应用,于是就有了二叉查找树. 使二叉树成为一颗二 ...
- 树和二叉树 -数据结构(C语言实现)
读数据结构与算法分析 树的概念 一棵树是一些节点的集合,可以为空 由称做根(root)的节点以及0个或多个非空子树组成,子树都被一条来自根的有向边相连 树的实现 思路 孩子兄弟表示法:树中的每个节点中 ...
- Gremlin--一种支持对图表操作的语言
Gremlin 是操作图表的一个非常有用的图灵完备的编程语言.它是一种Java DSL语言,对图表进行查询.分析和操作时使用了大量的XPath. Gremlin可用于创建多关系图表.因为图表.顶点和边 ...
- 面试题23从上到下打印二叉树+queue操作
//本题思路就是层次遍历二叉树,使用一个队列来模拟过程 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *ri ...
- 动态单链表的传统存储方式和10种常见操作-C语言实现
顺序线性表的优点:方便存取(随机的),特点是物理位置和逻辑为主都是连续的(相邻).但是也有不足,比如:前面的插入和删除算法,需要移动大量元素,浪费时间,那么链式线性表 (简称链表) 就能解决这个问题. ...
- 动态分配的顺序线性表的十五种操作—C语言实现
线性表 定义:是最常用的,也是最简单的数据结构,是长度为n个数据元素的有序的序列. 含有大量记录的线性表叫文件 记录:稍微复杂的线性表里,数据元素为若干个数据项组成,这时把一个数据元素叫记录 结构特点 ...
随机推荐
- 网络安全-主动信息收集篇第二章SNMP扫描
SNMP扫描: snmp在中大型企业中可以用来做网络管理和网络监控的使用,当开启了snmp简单网络管理后,那么客户机就可以通过这个协议向该设备发送snmp协议内容可以轻松查询到目标主机的相关信息. 以 ...
- Spring Boot 2.x实战之StateMachine
本文首发于个人网站:Spring Boot 2.x实战之StateMachine Spring StateMachine是一个状态机框架,在Spring框架项目中,开发者可以通过简单的配置就能获得一个 ...
- 爬虫学习--Urllib库基本使用 Day1
一.Urllib库详解 1.什么是Urllib Python内置的HTTP请求库 urllib.request 请求模块(模拟实现传入网址访问) urllib.error ...
- Java性能分析神器--VisualVM Launcher[1]
Java性能分析神器1--VisualVM Launcher VisualVM 当你日复一日敲代码的时候,当你把各种各样的框架集成到一起的时候,看着大功告成成功运行的日志,有没有那么一丝丝迷茫和惆怅: ...
- python函数的基本语法<三>
实参和形参: 定义函数括号里的一般叫形参 调用时括号里传递的参数一般叫实参 def students(age): print('my age is %s' % age) students(18) ag ...
- (转)白话数字签名(2)——软件&设备
然而它太慢了 非对称加密算法有一个重大缺点——加密速度慢,或者说得更拽一些,编码率比较低.例如在上一篇里我给Clark传的那个1GB的小电影,进行非对称加密足足用了66小时.那个借条小一些吧,也用了将 ...
- JVM,JDK,JRE
JVM,JDK,JRE 什么是JVM Java 虚拟机. 这个名词由Java和虚拟机前后两部分组成. 它有和其他虚拟机共性:JVM是通过软件模拟的计算机系统. 它也有自己的特性:JVM使用软件模拟的指 ...
- 从0开始学前端(笔记备份)----HTML部分 Day1 HTML标签
- shell脚本1——变量 $、read、``
与Shell变量相关的几个命令: 变量只在当前Shell中生效. source 这个命令让脚本影响他们父Shell的环境(. 可以代替source命令) export 这个命令可以让脚本影响其子She ...
- 0MQ 事件驱动 以及 poller
底层IO事件,以及借用socket poller的上层0MQ socket事件. 先来看用于底层和上层的两种poller. 这是用于底层io事件的poller_t,每个socket_base_t都关联 ...