数据结构(五)——二叉树

二叉树

二叉树可以说是最基础的一种树,下方代码涉及到二叉树的基本操作:
根据先序和中序序列创建二叉树,显示二叉树;
先序、中序、后序遍历的递归与非递归实现,层次遍历;
建立中序线索二叉树;
删除二叉树以及丰富的应用示例。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 100
#pragma warning(disable:4996)

typedef struct mybinarytree
{
	char data;
	struct mybinarytree *left, *right;
	struct mybinarytree *pa;
	int lflag,rflag;//用于建立线索二叉树
}BT;

BT *createBT(char *pre,char *in,int k);//根据先序和中序序列创建二叉树,k表示字符数目
void showTree(BT *T);//显示二叉树
int countLeaf(BT *T);//统计叶子节点的数量
void getpa(BT *T);//为pa指针赋值
void showpa(BT *T);//显示父节点
BT *copyTree(BT *T);//复制二叉树

void preTraverse(BT *T);//递归先序遍历
void inTraverse(BT *T);//递归中序遍历
void postTraverse(BT *T);//递归后序遍历
void layerVisit(BT *T);//层次遍历
void preVisit(BT *T);//非递归先序遍历
void inVisit(BT *T);//非递归中序遍历
void postVisit(BT *T);//非递归后序遍历

BT *inorder_thread(BT *T);//建立中序线索二叉树
void getInorder_thread1(BT *head);//利用线索二叉树输出正向中序序列
void getInorder_thread2(BT *head);//利用线索二叉树输出反向中序序列

BT *deleteTree(BT *T);//删除一棵树
BT *search(BT *T,char s);//查找数据为s的节点
BT *delSubtree(BT *T,char s);//删除一棵子树

int layerTraverse(BT *T);//层次遍历二叉树,逐行输出节点数据,并判断该二叉树是否为完全二叉树,如果是,返回1,否则返回0
void getPath(BT *T);//利用后序序列输出根节点到叶子节点的路径
BT *getcomAnce(BT *T,char s1,char s2);//寻找两个指定节点s1和s2(互相不为对方的祖先)最近的共同祖先,返回该祖先指针

main()
{
	BT *p;
	BT *T1;
	BT *head;
	char pre[]="ABDEFC";
	char in[]="BEDFAC"; 
	int k=strlen(pre);

	T1=createBT(pre,in,k);

	printf("以括号的形式显示二叉树:\n");
	showTree(T1);
	printf("\n叶子节点的数量为:%d\n",countLeaf(T1));
	printf("\n各个节点的父指针如下:\n");
	getpa(T1);
	showpa(T1);

	printf("\n先序遍历:\n");
	preTraverse(T1);
	printf("\n中序遍历:\n");
	inTraverse(T1);
	printf("\n后序遍历:\n");
	postTraverse(T1);
	printf("\n层次遍历:\n");
	layerVisit(T1);
	printf("\n先序遍历:\n");
	preVisit(T1);
	printf("\n中序遍历:\n");
	inVisit(T1);
	printf("\n后序遍历:\n");
	postVisit(T1);
	printf("\n");

	printf("\n二叉树层次遍历结果:\n");
	if(layerTraverse(T1))
		printf("该树是完全二叉树.\n");
	else
		printf("该树不是完全二叉树.\n");
	
	printf("\n根节点到各个叶子节点的路径为:\n");
	getPath(T1);


	p=getcomAnce(T1,'E','F');
	printf("最近的共同祖先为:%c\n",p->data);


	/*
	T1=delSubtree(T1,'A');
	printf("删除A之后:\n");
	showTree(T1);
	*/

	//线索化之后,指针域会发生改变,所以线索化放在最后
	head=inorder_thread(T1);
	printf("\n正向中序序列为:\n");
	getInorder_thread1(head);
	printf("\n反向中序序列为:\n");
	getInorder_thread2(head);
	printf("\n");

}


BT *createBT(char *pre, char *in, int k)//根据先序和中序序列创建二叉树,k表示字符数目
{
	int i,pos;
	char data;
	BT *p;

	if(k==0)
		return NULL;
	else
	{
		data=pre[0];
		p=(BT *)malloc(sizeof(BT));
		p->data=data;
		//确定data在中序序列中的位置
		for (i = 0; i < k && in[i]!=data; i++);
		pos=i;
		p->left=createBT(pre+1,in,pos);//创建左子树
		p->right=createBT(pre+pos+1,in+pos+1,k-pos-1);//创建右子树
		return p;
	}
}

void showTree(BT *T)//显示二叉树
{
	if (T)
	{
		printf("%c",T->data);
		if (T->left || T->right)
		{
			printf("(");
			showTree(T->left);
			printf(",");
			showTree(T->right);
			printf(")");
		}
	}
}

int countLeaf(BT *T)//统计叶子节点的数量
{
	if(!T)
		return 0;
	else if((!T->left)&&(!T->right))
		return 1;
	else
		return countLeaf(T->left)+countLeaf(T->right);
}

void getpa(BT *T)//为pa指针赋值
{
	if (T)//进行后序处理
	{
		getpa(T->left);
		if(T->left)
			T->left->pa=T;
		getpa(T->right);
		if(T->right)
			T->right->pa=T;

		T->pa=NULL;
	}
}

void showpa(BT *T)//显示父节点
{
	if (T)
	{
		if(T->pa)
			printf("%c--%c\n",T->data,T->pa->data);
		else
			printf("%c--NULL\n",T->data);
		showpa(T->left);
		showpa(T->right);
	}
}

BT *copyTree(BT *T)//复制二叉树
{
	BT *T1;
	if(!T)
		return NULL;
	else
	{
		T1=(BT *)malloc(sizeof(BT));
		T1->data=T->data;
		T1->left=copyTree(T->left);
		T1->right=copyTree(T->right);
		return T1;
	}
}


void preTraverse(BT *T)//递归先序遍历
{
	if (T)//如果是空树,什么都不输出
	{
		printf("%c",T->data);
		//递归遍历左右子树
		preTraverse(T->left);
		preTraverse(T->right);
	}
}

void inTraverse(BT *T)//递归中序遍历
{
	if (T)
	{
		inTraverse(T->left);
		printf("%c",T->data);
		inTraverse(T->right);
	}
}

void postTraverse(BT *T)//递归后序遍历
{
	if (T)
	{
		postTraverse(T->left);
		postTraverse(T->right);
		printf("%c",T->data);
	}
}

void layerVisit(BT *T)//层次遍历
{
	BT *q[N];//建立队列
	int front,rear;
	BT *p;

	if (!T)
	{
		printf("树为空.\n");
		return;
	}
	//初始化
	q[0]=T;
	front=0;
	rear=1;
	
	while (front != rear)
	{
		p=q[front];
		printf("%c",p->data);
		if (p->left)
		{
			q[rear]=p->left;
			rear++;
		}
		if (p->right)
		{
			q[rear]=p->right;
			rear++;
		}
		front++;
	}
	//最后不能释放q,因为内部存放的是指向树中节点的指针
}

void preVisit(BT *T)//非递归先序遍历
{
	BT *t[N];//建立栈
	int top;
	BT *p;

	if (!T)
	{
		printf("树为空.\n");
		return;
	}

	//初始化
	t[0]=T;
	top=0;
	while (top >= 0)
	{
		p=t[top];
		printf("%c",p->data);
		top--;
		//孩子按照先右后左的顺序入栈
		if (p->right)
		{
			top++;
			t[top]=p->right;
		}
		if (p->left)
		{
			top++;
			t[top]=p->left;
		}
	}
}

void inVisit(BT *T)//非递归中序遍历
{
	BT *t[N];
	int top=-1;
	BT *p,*q;

	q=T;
	while (top >= 0 || q)
	{
		while (q)//一直向左走
		{
			top++;
			t[top]=q;
			q=q->left;
		}
		//弹栈(此时栈非空)
		p=t[top];
		printf("%c",p->data);
		top--;
		q=p->right;//对弹出节点的右子树进行中序遍历,可能为空
	}
}

void postVisit(BT *T)//非递归后序遍历
{
	BT *t[N];
	int top=-1;
	BT *p,*q,*pre;

	q=T;//将要进行后序遍历的树的根节点
	do
	{
		while (q)//一直向左走
		{
			top++;
			t[top]=q;
			q=q->left;
		}
		pre=NULL;//表示最新弹出的节点
		//此时q==NULL
		while (top >= 0)//弹栈
		{
			p=t[top];
			if (p->right == pre)
			{
				printf("%c",p->data);
				top--;
				pre=p;
			}
			else
			{
				q=p->right;
				break;
			}
		}
	}while(top>=0);//若栈弹空,肯定遍历完毕
}


BT *inorder_thread(BT *T)//建立中序线索二叉树
{
	BT *head;
	BT *t[N];
	int top=-1;
	BT *p,*q,*pre;

	//创建奇怪的头结点
	//头结点的左指针指向根节点
	//头结点的右指针指向中序序列最后一个节点
	head=(BT *)malloc(sizeof(BT));
	head->lflag=0;
	head->rflag=1;
	head->right=head;

	if (!T)
	{
		head->left=head;
		return head;
	}
	
	head->left=T;
	pre=head;
	q=T;//表示将要进行中序遍历的根节点

	while (top >= 0 || q)
	{
		while (q)
		{
			top++;
			t[top]=q;
			q=q->left;
		}
		p=t[top];
		top--;
		//弹出之后,对pre和p进行线索化
		if (!p->left)
		{
			p->lflag=1;
			p->left=pre;
		}
		else
			p->lflag=0;
		if (!pre->right)
		{
			pre->rflag=1;
			pre->right=p;
		}
		else
			pre->rflag=0;
		pre=p;//更新pre

		q=p->right;
	}
	//对于最后一个输出的节点,现在pre指向它
	//最后一个节点的左指针已经赋值
	//最后一个节点的右指针指向头结点
	pre->rflag=1;
	pre->right=head;
	head->right=pre;

	return head;
}

void getInorder_thread1(BT *head)//利用线索二叉树输出正向中序序列
{
	BT *p=head->left;//p初始值为根节点
	
	while (p != head)
	{
		while (p->lflag == 0)
			p = p->left;
		while (p->rflag == 1 && p->right != head)
		{
			printf("%c ", p->data);
			p = p->right;
		}
		//将其看作是某个根节点,其左子树已全部输出
		//输出根节点,转向右子树
		printf("%c ", p->data);
		p = p->right;//如果p是最后一个输出节点,p->right==head;
	}
}

void getInorder_thread2(BT *head)//利用线索二叉树输出反向中序序列
{
	BT *p=head->right;

	while (p != head)
	{
		while (p->rflag == 0)
			p=p->right;
		while (p->lflag == 1 && p->left != head)
		{
			printf("%c ",p->data);
			p=p->left;
		}
		//将其看作是某个根节点,其右子树已全部输出
		//输出根节点,转向左子树
		printf("%c ",p->data);
		p=p->left;//如果是第一个节点,p->left==head;
	}
}


BT *deleteTree(BT *T)//删除一棵树
{
	if (T)
	{
		T->left=deleteTree(T->left);
		T->right=deleteTree(T->right);
		free(T);
	}
	return NULL;
}

BT *search(BT *T, char s)//查找数据为s的节点
{
	BT *left,*right;
	if(!T)
		return NULL;
	else if(T->data==s)
		return T;
	else
	{
		left=search(T->left,s);
		right=search(T->right,s);
		if(left)
			return left;
		else
			return right;
	}
}

BT *delSubtree(BT *T, char s)//删除一棵子树
{
	BT *obj,*pa;

	obj=search(T,s);
	if(!obj)
		return T;
	else if(obj==T)
		return deleteTree(T);
	else
	{
		pa=obj->pa;
		if(pa->left==obj)
			pa->left=deleteTree(obj);
		else
			pa->right=deleteTree(obj);
		return T;
	}
}


int layerTraverse(BT *T)//层次遍历二叉树,逐行输出节点数据,并判断该二叉树是否为完全二叉树,如果是,返回1,否则返回0
{
	BT *q[N];
	int front,rear,layerfir;//记录每层的起始位置(从第二层开始)
	BT *p;

	int n0;//指示是否有空节点入队
	int flag;//指示是否为完全二叉树

	if (!T)
	{
		printf("树为空.\n");
		return 1;
	}

	n0=0;
	flag=1;//假设为完全二叉树
	q[0]=T;
	front=0;
	rear=1;
	layerfir=1;

	while (front != rear)
	{
		p=q[front];
		front++;
		printf("%c ",p->data);

		if (p->left)
		{
			if(n0==1)//如果之前有空节点入队,表示不是完全二叉树
				flag=0;
			q[rear]=p->left;
			rear++;
		}
		else
			n0=1;
		if (p->right)
		{
			if(n0==1)
				flag=0;
			q[rear]=p->right;
			rear++;
		}
		else//表示有空节点入队
			n0=1;

		if (front == layerfir)
		{
			printf("\n");
			layerfir=rear;
		}
	}
	return flag;
}

void getPath(BT *T)//利用后序序列输出根节点到叶子节点的路径
{
	int i;
	BT *t[N];
	int top=-1;
	BT *p,*q,*pre;

	//初始化
	q=T;
	do {
		while (q)
		{
			top++;
			t[top]=q;
			q=q->left;
		}
		pre=NULL;
		while (top >= 0)
		{
			p=t[top];
			if (p->right == pre)//表示可以弹栈
			{
				top--;
				if ((!p->left) && (!p->right))
				{
					for(i=0;i<=top;i++)
						printf("%c-->",t[i]->data);
					printf("%c\n",p->data);
				}				
				pre=p;				
			}
			else
			{
				q=p->right;
				break;
			}
		}

	}while(top>=0);
}

BT *getcomAnce(BT *T, char s1, char s2)//寻找两个指定节点s1和s2(互相不为对方的祖先)最近的共同祖先,返回该祖先指针
{
	BT *t[N],*t1[N];
	int top=-1;
	BT *p,*q,*pre;
	int i,k;
	int flag=0;

	q=T;
	do {
		while (q)
		{
			top++;
			t[top]=q;
			q=q->left;
		}
		pre=NULL;
		while (top >= 0)
		{
			p=t[top];			
			if (p->right == pre)
			{
				top--;
				if (p->data == s1 || p->data == s2)
				{
					flag++;
					if (flag == 1)
					{
						k=top;
						for(i=0;i<=top;i++)//转移祖先
							t1[i]=t[i];
					}
					if (flag == 2)//可以开始比较
					{
						for(i=0;i<=top && i<=k && t[i]==t1[i];i++);
						return t[i-1];
					}
				}
				pre=p;
			}
			else
			{
				q=p->right;
				break;
			}
		}
	}while(top>=0);
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 什么是二叉树二叉树是一种树形结构,其中每个节点最多有两个子节点。一个节点的左子节点比该节点小,右子节点比该节点大。二叉树通常用于搜索和排序。 2. 二叉树的遍历方法有哪些? 二叉树的遍历方法包括前序遍历、中序遍历和后序遍历。前序遍历是从根节点开始遍历,先访问根节点,再访问左子树,最后访问右子树。中序遍历是从根节点开始遍历,先访问左子树,再访问根节点,最后访问右子树。后序遍历是从根节点开始遍历,先访问左子树,再访问右子树,最后访问根节点。 3. 二叉树的查找方法有哪些? 二叉树的查找方法包括递归查找和非递归查找。递归查找是从根节点开始查找,如果当前节点的值等于要查找的值,则返回当前节点。如果要查找的值比当前节点小,则继续在左子树中查找;如果要查找的值比当前节点大,则继续在右子树中查找。非递归查找可以使用栈或队列实现,从根节点开始,每次将当前节点的左右子节点入栈/队列,直到找到要查找的值或者栈/队列为空。 4. 二叉树的插入与删除操作如何实现? 二叉树的插入操作是将要插入的节点与当前节点的值进行比较,如果小于当前节点的值,则继续在左子树中插入;如果大于当前节点的值,则继续在右子树中插入。当找到一个空节点时,就将要插入的节点作为该空节点的子节点。删除操作需要分为三种情况:删除叶子节点、删除只有一个子节点的节点和删除有两个子节点的节点。删除叶子节点很简单,只需要将其父节点的对应子节点置为空即可。删除只有一个子节点的节点,需要将其子节点替换为该节点的位置。删除有两个子节点的节点,则可以找到该节点的后继节点(即右子树中最小的节点),将其替换为该节点,然后删除后继节点。 5. 什么是平衡二叉树? 平衡二叉树是一种特殊的二叉树,它保证左右子树的高度差不超过1。这种平衡可以确保二叉树的查找、插入和删除操作的时间复杂度都是O(logn)。常见的平衡二叉树包括红黑树和AVL树。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值