二叉树的基本操作(C语言版)

二叉树是一种应用十分广泛的数据结构,本文所述的二叉树的基本操作主要包括创建二叉树型的数据,对二叉树进行遍历(三种遍历方式)、增加节点、删除单个节点(重点)、销毁二叉树、插入一个节点、查询节点。

#pragma once
#include<stdio.h>
#include<stdlib.h>


 typedef struct BitTree  //结构体变量
{
	 int data;
	 struct BitTree *left;
	 struct BitTree *right;
}Bst,*Bstp;

int InsertData(struct BitTree *bt,int key)  //插入一个数据,用于创建二叉树
{
    if(key>bt->data)  //判断是否插到右子树
	{
		if (bt->right == NULL)  //判断是否右子树是否可插入
		{
			bt->right = (struct BitTree *)malloc(sizeof(struct BitTree));
			bt->right->left = bt->right->right = NULL;
			bt->right->data = key;
			/*printf("%d\n", bt->right->data);*/
			return 1;
		}
		else
		{
			InsertData(bt->right, key);
		}
	}
	else if(key<bt->data)  //判断是否插到左子树
	{
		if (bt->left == NULL)
		{
			bt->left = (struct BitTree *)malloc(sizeof(struct BitTree));
			if (bt->left == NULL)
				printf("left malloc error\n");
			bt->left->left = bt->left->right = NULL;
			bt->left->data = key;
			return 1;
		}
		else
		{
			InsertData(bt->left, key);  //递归到下一个子树的“根节点”
		}
	}
	else  //如果该数值与节点值相等,不做任何处理,继续插入下一个值
	{
		return 1;
	}
}

void Previsit(Bstp a)  //先序遍历
{
	if (a != NULL)
	{
		printf(" %d ", a->data);  //先输出根节点的数据
		Previsit(a->left); //再遍历左子树
		Previsit(a->right); //最后再遍历右子树
	}
	else
		return;
}

void Inorder(Bstp a)  //中序遍历
{
	if (a != NULL)
	{
		Inorder(a->left);
		printf(" %d ", a->data);
        Inorder(a->right);
	}
	else
		return;
}

void PostOrder(Bstp a)//后序遍历
{
	if (a != NULL)
	{
		PostOrder(a->left);
		PostOrder(a->right);
		printf(" %d ", a->data);
	}
	else
		return;
}

void VisitTree(Bstp x)  //综合三种遍历方式的子函数,在主函数调用
{
	printf("先序遍历:");
	Previsit(x);
	printf("\n中序遍历:");
	Inorder(x);
	printf("\n后序遍历:");
	PostOrder(x);
	printf("\n");
}

Bstp CreatTree(void)//创建二叉树的综合性子函数,返回值为已创建二叉树的根节点地址
{
	int num;
	struct BitTree *anew=NULL; 
	printf("请输入数据:");
	scanf_s("%d", &num);  //先把输入的第一个数创建为根节点防止调用函数时传递野指针
	if (num >= 0)
	{
		anew = (Bstp)malloc(sizeof(Bst));
		anew->data = num;
		anew->left = anew->right = NULL;
	}
	else
	{
		printf("Error!\n");
		return NULL;
	}
	do  //进入循环体,开始递归单个添加数据
	{
	
		scanf_s("%d", &num);
		if(num>=0)
		  InsertData(anew, num);
	} while (num >= 0);
	return anew; //返回根节点地址
}

void InserNumber(Bstp x)  //插入函数
{
	int k;
	printf("请输入要插入的数据:");
	scanf_s("%d", &k);
	InsertData(x, k);  //递归插入
	printf("\n  插入完成! \n\n");
	return;
}

int subFind(Bstp x, int f) //查询子函数,递归查找,查找成功返回1,否则返回0
{
	if (f == x->data)
	{
		return 1;
	}
	else
	{
		if (f > x->data&&x->right!=NULL)
		{
			subFind(x->right, f);
		}
		else if(f<x->data&&x->left!=NULL)
		{
			subFind(x->left, f);
		}
		else
		{
			return 0;
		}
	}
}

void PrintFind(Bstp x,int f)// 用于打印查找成功后的祖先节点
{
	if (f == x->data)
	{
		printf("%d ", x->data);
	}
	else
	{
		if (f > x->data&&x->right != NULL)
		{
			printf("%d ", x->data);
			PrintFind(x->right, f);
		}
		else if (f < x->data&&x->left != NULL)
		{
			printf("%d ", x->data);
			PrintFind(x->left, f);
		}
	}
}

void FindAnum(Bstp x)  //查询函数
{
	int f;
	printf("请输入你要查找的数据:");
	scanf_s("%d", &f);
	if (subFind(x,f))
	{
		printf("\n查找成功!\n");
		printf("\n祖先及其该节点为:");
		PrintFind(x,f);
		printf("\n");
	}
	else
	{
		printf("\n查找失败!\n");
	}
	return;
}

void myfree(Bstp x)  //销毁函数
{
	extern int Node;
	if (x!= NULL)
	{
		myfree(x->left); //按照左右的先后顺序释放内存
		myfree(x->right);
		free(x);
		Node++; //每释放一块内存,该数就会自增一次
		return;
	}
}

//锁定要删除的数据的位置,返回该数据的首地址
Bstp LocaTar(Bstp x, int k)  
{
	Bstp loca = x;
	Bstp fath;
	fath = x;
	/*如果目标节点已经找到返回该节点指针*/
	if (loca->data == k)  
	{
		return loca;
	}
	/*否则进入递归环节*/
	else
	{
		/*先判断在左子树还是在右子树*/
		if (loca->data < k) //左
		{
			fath = loca;
			loca = loca->right;
			LocaTar(loca, k); //递归
		}
		else //右
		{
			fath = loca;
			loca = loca->left;
			LocaTar(loca, k); //递归
		}
	}
}

//用于锁定待删除数据的双亲首地址
Bstp LocaFath(Bstp x, int k)
{
    Bstp fath=x; //初始化双亲地址为根节点
	Bstp local=NULL, locar=NULL;
	if (fath->data == k)
	{
		return fath;
	}	
	local = fath->left;  //为了进入循环
	locar = fath->right;
	//当不是叶子节点时,说明还可以往下寻找目标节点
	while (local != NULL || locar != NULL) 
	{
		local = fath->left;
		locar = fath->right;
		//一旦找到目标节点就终止循环
		if (local != NULL && locar != NULL) 
		{
			if (local->data == k)
			{
				break;
			}
			if (locar->data == k)
			{
				break;
			}
		}
		else if (local == NULL && locar != NULL)
		{
			if (locar->data == k)
			{
				break;
			}
		}
		else if (local != NULL && locar == NULL)
		{
			if (local->data == k)
			{
				break;
			}
		}
		if (fath->data > k)
		{
			fath = local;
		}
		else
		{
			fath = locar;
		}
	}
	return fath;
}

Bstp delenode(Bstp t)  //t为待删除节点 ,函数返回删除后的子树的根节点的首地址
{
	Bstp s = NULL, q = NULL;  //用作中间变量
	if (t->left == NULL && t->right == NULL) //当t为叶子节点时 直接把t的内存释放掉
	{
		free(t);
		return NULL;
    }
	if (t->left != NULL) //t有左子树
	{
		if (t->left->right == NULL) //t的左子树的右子树为空
		{
			q = t->left;  
			s = q->left;
			t->data = q->data; 
			t->left = s; //将t的左子树的左子树与修改后的t的左子树关联
			free(q); //释放内存
			return t;
		}
		else  //t的左子树有右子树
		{
			q = t;
			s = t->left;
			while (s->right != NULL) //先循环到右子树的末节点
			{
				q = s;
				s = s->right;
			}
			q->right = s->left; //更新,替换
			t->data = s->data;
			free(s); //释放内存
			return t;
		}
	}
	else if (t->right!=NULL) //思路与前面的一样
	{
		if (t->right->left == NULL)
		{
			q = t->right;
			s = q->right;
			t->data = q->data;
			t->right = s;
			free(q);
			return t;
		}
		else
		{
			q = t;
			s = t->right;
			while (s->left != NULL)
			{
				q = s;
				s = s->left;
			}
			q->left = s->right;
			t->data = s->data;
			free(s);
			return t;
		}
	}
}

void Delete_aNum(Bstp x) //删除一个数据函数
{
	int k = 0;
	Bstp locat=NULL;
	Bstp fath = x;
	printf("请输入要删除的数据:");
	scanf_s("%d", &k);
	printf("\n");
	if (subFind(x, k)) //目标节点在该二叉树中
	{
		locat=LocaTar(x, k); //寻找目标节点
		fath = LocaFath(x, k); //寻找目标节点的双亲节点
		if (fath == locat) //如果目标节点为根节点
			x=delenode(locat);
		else if (fath->left == locat) //双亲节点的左子树为目标节点
			fath->left = delenode(locat);
		else  //双亲节点的右子树为目标节点
			fath->right = delenode(locat);
		
	}
	else  
	{
		printf("\n错误!要删除的节点不在该二叉树中!\n\n");
		return;
	}
}




搜索公众号“24K纯学渣”,回复“二叉树”即可获取完整的VS2017 C-project!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值