二叉树基本操作C语言实现

一、二叉树的创建

二叉树结构体的定义

typedef char ElemType;//定义数据类型
typedef struct BiNode
{
	ElemType data;
	struct BiNode* lchild;//左孩子
	struct BiNode* rchild;//右孩子
}BiNode,*BiTree;

1、前序递归创建
递归是一种比较难理解的算法,但是既然能递归,就说明有一定的内在规律,所以理解的时候可以简化,把二叉树简化成3个结点,那么创建完A结点,再将左孩子指针作为形参传入就可以创建B结点,同理创建C结点。
在这里插入图片描述

void CreateBiTree(BiTree *T)//传入的是一个二级指针
{
	ElemType ch;
	scanf("%c",&ch);
	if(ch == '#')
		*T = NULL;
	else
	{
		*T = (BiTree)malloc(sizeof(BiNode));
		(*T)->data = ch;
		CreateBiTree(&(*T)->lchild);//T->lchild是一级指针,要传入它的地址	
		CreateBiTree(&(*T)->rchild);
	}
}

2、读边法创建创建
队列的存储结构

//队列
typedef struct node
{
	BiNode* data;
	struct node* next;
}QNode,*Qptr;

typedef struct
{
	Qptr front,rear;
}Queue;

输入双亲节点、孩子结点和rlflag,rlflag=0为左孩子,rlflag=1为右孩子。
利用队列,首先对初始化的结点进队,然后如果这个结点的双亲结点是’#',那么这个为头结点,然后再进入一个结点,通过判断队头的结点是否为该结点的双亲结点,是则根据rlflag连接,否则不断出队,直到队头结点是该结点的双亲结点。

void Create_Edge(BiTree *T)
{
	BiNode *p,*s;
	Queue Q;
	ElemType fa,ch;
	int rlflag;
	IniteQueue(&Q);
	scanf("%c%c%d",&fa,&ch,&rlflag);
	getchar();
	while(ch != '#')
	{
		p = (BiNode*)malloc(sizeof(BiNode));
		p->data = ch;
		p->lchild = NULL;
		p->rchild = NULL;
		EnQueue(&Q,p);
		if(fa == '#')
			*T=p;
		else
		{
			s=getHead(Q);//得到队头
			while(s->data != fa)
			{
				DeQueue(&Q);
				s=getHead(Q);
			}//寻找双亲结点
			if(rlflag == 0)
				s->lchild = p;
			if(rlflag == 1)
				s->rchild = p;
		}
		scanf("%c%c%d",&fa,&ch,&rlflag);
		getchar();
	}
}

二、二叉树的遍历

1、递归遍历——前序、中序、后序遍历

//前序
void pre_order(BiTree T)
{
	if(T)
	{
		printf("%c ",T->data);
		pre_order(T->lchild);
		pre_order(T->rchild);
	}
}
//中序
void in_order(BiTree T)
{
	if(T)
	{
		in_order(T->lchild);
		printf("%c ",T->data);
		in_order(T->rchild);
	}
}
//后序
void post_order(BiTree T)
{
	if(T)
	{
		post_order(T->lchild);
		post_order(T->rchild);
		printf("%c ",T->data);
	}
}

2、任务分析法遍历
栈中数据域的数据类型,这里的栈没设top指针,不过效果一样。

typedef struct 
{
	BiNode* tptr;
	int task;
}E;
typedef struct stacknode
{
	E data;
	struct stacknode *next;
}SNode,*Stack;

任务分析法,栈中的数据域包括指向树结点的指针和任务(int类型),task=0表示访问,task=1表示遍历。先初始化task=1,然后把根节点压栈,当栈不为空时,弹栈相当于任务处理,如果task=0就访问,否则就进行遍历,例如先序遍历,那么最先压栈的应该是右子树,此时给它布置的还是遍历任务,然后再压栈左子树,最后压栈子树根,并给它布置访问任务,这样,下一轮处理时就可以访问根结点,然后左子树出栈,给它布置访问任务,以此类推。

A0			
B1		B0
C1		C1	  C0
void preorder_iter(BiTree T)
{
	E e;
	BiNode *p;
	Stack S;
	IniteStack(&S);
	e.task=1;
	e.tptr=T;
	Push(&S,e);
	while(!isEmpty_stack(S))
	{
		Pop(&S,&e);
		if(e.task == 0)
			printf("%c ",e.tptr->data);
		else
		{
			p = e.tptr;
			e.tptr = p->rchild;
			e.task = 1;
			if(e.tptr)
				Push(&S,e);
				//-------只需要改变这三步的顺序就可以实现中序和后序遍历
			e.tptr = p->lchild;
			if(e.tptr)
				Push(&S,e);
				//---------
			e.task = 0;
			e.tptr = p;
			Push(&S,e);
		}
	}
}

void inorder_iter(BiTree T)
{
	E e;
	BiNode *p;
	Stack S;
	IniteStack(&S);
	e.task=1;
	e.tptr=T;
	Push(&S,e);
	while(!isEmpty_stack(S))
	{
		Pop(&S,&e);
		if(e.task == 0)
			printf("%c ",e.tptr->data);
		else
		{
			p = e.tptr;
			e.tptr = p->rchild;
			if(e.tptr)
				Push(&S,e);
				//-------
			e.task = 0;
			e.tptr = p;
			Push(&S,e);
			//---------
			e.task = 1;
			e.tptr = p->lchild;
			if(e.tptr)
				Push(&S,e);
		}
	}
}

void postorder_iter(BiTree T)
{
	E e;
	BiNode *p;
	Stack S;
	IniteStack(&S);
	e.task=1;
	e.tptr=T;
	Push(&S,e);
	while(!isEmpty_stack(S))
	{
		Pop(&S,&e);
		if(e.task == 0)
			printf("%c ",e.tptr->data);
		else
		{
			e.task = 0;
			Push(&S,e);
			//--------
			p = e.tptr;
			e.tptr = p->rchild;
			e.task = 1;
			if(e.tptr)
				Push(&S,e);
				//---------
			e.tptr = p->lchild;
			if(e.tptr)
				Push(&S,e);
		}
	}
}

3、层序遍历
利用队列,将每一层入队然后出队输出。先将根节点入队,然后进入循环,不断得到队头元素进行输出,然后再将队头元素出队,如果改结点有左孩子,则左孩子入队,如果有右孩子,右孩子也入队,就可以实现一层结点按从左到右的顺序入队,当队列为空时,循环结束。

void layer_order(BiTree T)
{
	Queue Q;
	BiNode *p;
	IniteQueue(&Q);
	if(!T)
		return ;
	EnQueue(&Q,T);
	while(!isEmpty(Q))
	{
		p = getHead(Q);
		printf("%c ",p->data);
		DeQueue(&Q);
		if(p->lchild)
			EnQueue(&Q,p->lchild);
		if(p->rchild)
			EnQueue(&Q,p->rchild);
	}
}

三、数量的统计

1、树的深度
先递归左子树统计左子树深度,再递归右子树求得深度,比较出最大的深度再算上根结点,所以返回最大深度+1

int depth(BiTree T)
{
	int dep1,dep2;
	if(T)
	{
		dep1 = depth(T->lchild);
		dep2 = depth(T->rchild);
		return dep1>dep2?dep1+1:dep2+1;
	}
	return 0;
}

2、叶子结点个数
叶子结点的左右孩子指针都是指向NULL。
先搜索左子树,再搜索右子树。

void leafCount(BiTree T,int *count)
{
	if(!T)
		return ;
	else
	{
		leafCount(T->lchild,count);
		if(!T->lchild && !T->rchild)
		{
			 (*count)++;
		}
		leafCount(T->rchild,count);
	}
}

3、结点数
根节点+左子树结点+右子树结点,例如左子树,通过递归到最后一层,叶子结点的左右指针为空,均返回0,使得最后一层的返回值是1,然后再逐级返回,最终得到了左子树的结点数,可以自己画个栈图思考一下。
在这里插入图片描述

int nodeCount(BiTree T)
{
	if(!T)
		return 0;
	return 1+nodeCount(T->lchild)+nodeCount(T->rchild);
}

4、树的宽度
利用队列,和层序遍历相似,将每一层都保存到队列中,然后用width保留最大宽度,每遍历一层,就更新一次width,通过统计队列长度就可以得到这一层的宽度。

int width(BiTree T)
{
	int width = 0;
	int len = 0,i;
	Queue Q;
	BiNode *p;
	IniteQueue(&Q);
	if(!T)
		return 0;
	EnQueue(&Q,T);
	while(!isEmpty(Q))
	{
		len = getLength(Q);
		width = len > width? len:width;
		for(i=0;i<len;i++)
		{
			p = getHead(Q);
			DeQueue(&Q);
			if(p->lchild)
				EnQueue(&Q,p->lchild);
			if(p->rchild)
				EnQueue(&Q,p->rchild);
		}
	}
	return width;
}

四、凹入法输出

void dispBiTree(BiTree T,char a,int level)
{
	int i,j;
	if(!T)
		return ;
	for(i=0;i<level;i++)
		putchar(' ');
	printf("%c(%c)",T->data,a);
	for(j=i;j<30;j++)//这里可以自定义,打印'-'为了好区分,美观。
		putchar('-');//但是j的初始值必须要随着level的变化而变化。
	printf("\n");
	dispBiTree(T->lchild,'L',level+3);//level加多少也可以自定义。
	dispBiTree(T->rchild,'R',level+3);
}	
int main()
{
	BiTree T;
	int count = 0;
	Create_Edge(&T);
	dispBiTree(T,'D',0);
	getchar();
	getchar();
	return 0;
}

凹入法输出效果图如下:
在这里插入图片描述

  • 10
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
以下是二叉树基本操作C语言实现: ```c #include <stdio.h> #include <stdlib.h> //定义二叉树结构体 typedef struct TreeNode { int data; struct TreeNode* left; struct TreeNode* right; } TreeNode; //创建二叉树节点 TreeNode* createNode(int data) { TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); node->data = data; node->left = NULL; node->right = NULL; return node; } //创建二叉树 TreeNode* createTree() { int data; scanf("%d", &data); if (data == -1) { //输入-1表示该节点为空 return NULL; } TreeNode* root = createNode(data); printf("请输入%d的左子节点:", data); root->left = createTree(); printf("请输入%d的右子节点:", data); root->right = createTree(); return root; } //先序遍历 void preorderTraverse(TreeNode* root) { if (root == NULL) { return; } printf("%d ", root->data); preorderTraverse(root->left); preorderTraverse(root->right); } //中序遍历 void inorderTraverse(TreeNode* root) { if (root == NULL) { return; } inorderTraverse(root->left); printf("%d ", root->data); inorderTraverse(root->right); } //后序遍历 void postorderTraverse(TreeNode* root) { if (root == NULL) { return; } postorderTraverse(root->left); postorderTraverse(root->right); printf("%d ", root->data); } //层序遍历 void levelorderTraverse(TreeNode* root) { if (root == NULL) { return; } TreeNode* queue[1000]; int front = 0, rear = 0; queue[rear++] = root; while (front < rear) { TreeNode* node = queue[front++]; printf("%d ", node->data); if (node->left) { queue[rear++] = node->left; } if (node->right) { queue[rear++] = node->right; } } } //插入节点 void insertNode(TreeNode* root, int data) { if (root == NULL) { return; } if (root->left == NULL) { root->left = createNode(data); } else if (root->right == NULL) { root->right = createNode(data); } else { //如果当前节点的左右子节点都不为空,则递归插入左右子树 insertNode(root->left, data); insertNode(root->right, data); } } //删除节点 void deleteNode(TreeNode* root, int data) { if (root == NULL) { return; } if (root->left != NULL && root->left->data == data) { free(root->left); root->left = NULL; } else if (root->right != NULL && root->right->data == data) { free(root->right); root->right = NULL; } else { //如果当前节点的左右子节点都不为空,则递归删除左右子树 deleteNode(root->left, data); deleteNode(root->right, data); } } //查找节点 TreeNode* searchNode(TreeNode* root, int data) { if (root == NULL) { return NULL; } if (root->data == data) { return root; } TreeNode* left = searchNode(root->left, data); TreeNode* right = searchNode(root->right, data); if (left != NULL) { return left; } if (right != NULL) { return right; } return NULL; } //修改节点 void modifyNode(TreeNode* node, int newData) { if (node == NULL) { return; } node->data = newData; } int main() { printf("请输入二叉树的根节点:"); TreeNode* root = createTree(); printf("先序遍历:"); preorderTraverse(root); printf("\n中序遍历:"); inorderTraverse(root); printf("\n后序遍历:"); postorderTraverse(root); printf("\n层序遍历:"); levelorderTraverse(root); printf("\n插入节点5后:"); insertNode(root, 5); levelorderTraverse(root); printf("\n删除节点5后:"); deleteNode(root, 5); levelorderTraverse(root); printf("\n查找节点3:"); TreeNode* node = searchNode(root, 3); if (node != NULL) { printf("找到了,节点的值为%d\n", node->data); } else { printf("没找到\n"); } printf("修改节点3的值为10后:"); modifyNode(node, 10); levelorderTraverse(root); return 0; } ``` 这段代码实现二叉树的创建、遍历、插入、删除、查找和修改等基本操作

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄某人学不完了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值