树的基本操作

今天这一章概述树的基本操作,边写边调试边更新!!!


首先定义树的节点结构:

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

typedef char ElemType;

/*树的结点结构*/
typedef struct BiTNode
{
	ElemType data;
	struct BiTNode * lchild;
	struct BiTNode * rchild;
}BiTNode,* BiTree;

前序遍历构造一颗树,当遇到输入‘#’时表示此结点的前驱为叶子结点:

void CreateTree(BiTree * T)
{
	ElemType ch;
	scanf("%c",&ch);

	if (ch == '#')
	{
		*T = NULL;
	}
	else
	{
		*T = (BiTree)malloc(sizeof(BiTNode));
		if(*T ==NULL)abort();
		(*T)->data = ch;
		CreateTree(&(*T)->lchild);
		CreateTree(&(*T)->rchild);
	}
}

三种简单的遍历方法,利用递归做非常简单,树把递归用到了极致:

/*前序遍历*/
void PreOrderTraverse(BiTree T)
{
	if (!T)
	{
		return;
	}
	printf("%c ",T->data);
	PreOrderTraverse(T->lchild);
	PreOrderTraverse(T->rchild);
}

/*中序遍历*/
void InOrderTraverse(BiTree T)
{
	if (!T)
	{
		return;
	}
	InOrderTraverse(T->lchild);
	printf("%c ",T->data);
	InOrderTraverse(T->rchild);
}

/*中序遍历*/
void PostOrderTraverse(BiTree T)
{
	if (!T)
	{
		return;
	}
	PostOrderTraverse(T->lchild);
	PostOrderTraverse(T->rchild);
	printf("%c ",T->data);
}

层序遍历需要利用之前学过的一个数据结构——队列帮助我们完成,所以我就简单的写了一个队列,只有Push和Pop两个方法:

/******************************简单的队列***********************************/
typedef BiTree QueElem;
typedef struct Queue
{
	int rear;
	int front;
	QueElem * queue;
	int quesize;
}Queue, * pQueue;
void Init(pQueue * que, int size)
{
	*que = (pQueue)malloc(sizeof(Queue));
	(*que)->queue=(QueElem*)malloc(sizeof(QueElem)*size);
	(*que)->front = (*que)->rear = 0;
	(*que)->quesize=size+1;
}
void Push(pQueue * que,QueElem e)
{
	(*que)->rear=((*que)->rear+1)%(*que)->quesize;
	(*que)->queue[(*que)->rear] = e;
}
void Pop(pQueue * que,QueElem * e)
{
	(*que)->front=((*que)->front+1)%(*que)->quesize;
	*e = (*que)->queue[(*que)->front];
}
/*******************************************************************************/

接下来就是层序遍历的实现,层序遍历比之前增加了点难度,不过很好的帮助我们复习了一下队列的用法,利用队列FIFO的特性,我们也将层序遍历实现了!

/*层序遍历*/
void LevelOrderTraverse(BiTree T)
{
	if (!T)
	{
		return;
	} 
	else
	{
		pQueue one;
		Init(&one,20);
		Push(&one,T);
		while (one->rear != one->front)
		{
			BiTree temp =NULL;
			Pop(&one,&temp);
			printf("%c",temp->data);
			if (temp->lchild)
			{
				Push(&one,temp->lchild);
			} 
			if (temp->rchild)
			{
				Push(&one,temp->rchild);
			}
		}
	}
}

下面这个方法是计算树的度,树的度说白了就是树的层数,当然是是从根结点开始遍历加上左子树和右子树中度比较大的数:

/*求树的度*/
int Depth(BiTree T)
{
	if (!T)
	{
		return 0;
	}

	int left = Depth(T->lchild)+1;
	int right = Depth(T->rchild)+1;

	return left > right ? left : right;
}

有是一个简单的操作——销毁树,这里有个地方值得注意一下, 为什么参数要用BiTree * T ? 如果不加 * ,最后的*T=NULL 就要改为 T=NULL,这样能不能传递出去呢?

起到防止失控指针的作用了吗?读者可以自己进行调试看看!!!

/*销毁树*/
void DestoryTree(BiTree * T)
{
	if(*T == NULL)
	{
		return;
	}
	if ((*T)->lchild)
	{
		DestoryTree(&(*T)->lchild);
	}
	if((*T)->rchild)
	{
		DestoryTree(&(*T)->rchild);
	}
	free(*T);
	*T=NULL; /*注意*/
}


这个方法是获取数据域等于e的结点的双亲结点,挺纠结的一个方法,大半夜了脑袋有点不好使,不过经得起测试,结果正确。

为什么 要用参数返回结果呢,因为有大神告诉我,返回值是用来返回调试错误的,而且返回值只能返回一个结果,而参数我们可以返回很多结果,

这也正是我们学习C语言指针的精髓所在!

/*获取e的前驱*/
void getParent(BiTree T, ElemType e,BiTree * RES)
{
	if ( * RES != NULL || !T)
	{
		return;
	}

	if (T->lchild && * RES == NULL)
	{
		if (T->lchild->data == e)
		{
			*RES = T;
			return;
		}
		getParent(T->lchild, e,RES);
	}
	if (T->rchild  && * RES == NULL)
	{
		if (T->rchild->data == e)
		{
			*RES = T;
			return;
		}
		getParent(T->rchild, e,RES);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值