树的基本操作(C语言)

一、必要准备

1、存储结构
采用孩子兄弟表示法进行存储,数据的类型是字符串。这样的存储结构就像是把树转化成了二叉树。

typedef char ElemType[30];
//树
typedef struct CSTNode
{
	ElemType data;
	struct CSTNode *firstchild;
	struct CSTNode *nextsibling;
}CSTNode,*CSTree;
//队列
typedef struct QNode
{
	CSTNode *data;
	struct QNode *next;
}QNode,*Qptr;
typedef struct 
{
	Qptr front,rear;
}Queue;
//栈
typedef struct SNode 
{
	CSTNode *data;
	struct SNode *next;
}SNode,*Stack;

2、队列和栈的操作

void IniteQueue(Queue *Q)
{
	Q->front = Q->rear = (QNode*)malloc(sizeof(QNode));
	Q->front->next = NULL;
}

void EnQueue(Queue *Q,CSTNode *e)
{
	QNode *p = (QNode*)malloc(sizeof(QNode));
	p->data = e;
	p->next = Q->rear->next;
	Q->rear->next = p;
	Q->rear = p;
}

void DeQueue(Queue *Q)
{
	QNode *p = Q->front->next;
	if(Q->front == Q->rear)
		return ;
	Q->front->next = p->next;
	if(Q->front->next == Q->rear)
		Q->rear = Q->front;
	free(p);
}

int isEmpty_Q(Queue Q)
{
	if(Q.front == Q.rear)
		return 1;
	return 0;
}

CSTNode* getHead(Queue Q)
{
	if(!isEmpty_Q(Q))
		return Q.front->next->data;
	return NULL;
}

void IniteStack(Stack *S)
{
	*S = NULL;
}

void Push(Stack *S,CSTNode *e)
{
	SNode *p = (SNode*)malloc(sizeof(SNode));
	p->data = e;
	p->next = *S;
	*S = p;
}

void Pop(Stack *S)
{
	SNode *p = *S;
	if(*S == NULL)
		return ;
	*S = p->next;
	free(p);
}

int isEmpty_S(Stack S)
{
	if(S == NULL)
		return 1;
	return 0;
}

void PrintStack(Stack S)
{
	SNode *p = S;
	while(p)
	{
		printf("%s\n",p->data->data);
		p = p->next;
	}

}

CSTNode* getTop(Stack S)
{
	if(!isEmpty_S(S))
		return S->data;
	return NULL;
}

二、基本操作

1、读边按层创建

void Create_CSTree(CSTree *T)
{
	CSTNode *p,*s,*r;
	ElemType fa,ch;
	Queue Q;
	IniteQueue(&Q);
	printf("请输入父节点和子节点\n");
	scanf("%s",fa);
	getchar();
	scanf("%s",ch);
	getchar();
	while(strcmp(ch,"#") != 0)
	{
		p = (CSTNode*)malloc(sizeof(CSTNode));
		strcpy(p->data,ch);
		p->firstchild = NULL;
		p->nextsibling = NULL;
		EnQueue(&Q,p);//入队
		if(strcmp(fa,"#") == 0)
			*T = p;
		else
		{
			s = getHead(Q); //得到队头
			while(strcmp(s->data,fa) != 0)
			{
				DeQueue(&Q);
				s = getHead(Q);
			}
			if(!s->firstchild)
			{
				s->firstchild = p;
				r = p; //临时指针记录这个结点
			}
			else
			{
				r->nextsibling = p;//对于兄弟结点,利用临时指针串联
				r = p;
			}
		}
		scanf("%s",fa);
		getchar();
		scanf("%s",ch);
		getchar();
	}
}

2、先根遍历和后根遍历
树的先根遍历类似二叉树的前序遍历,后根遍历类似于二叉树的中序遍历。
先根遍历先访问根结点,再访问孩子结点,所以就是先访问根节点,再访问第一个孩子结点,再访问兄弟结点。
后根遍历,先访问孩子结点再访问兄弟结点最后访问根结点。将树转换为二叉树时,根节点的第一个孩子结点就相当于它兄弟结点的根结点,兄弟结点相当于它的右孩子。所以这就相当于二叉树的中序遍历。

void pre_order(CSTree T)
{
	if(T)
	{
		printf("%s\n",T->data);
		pre_order(T->firstchild);
		pre_order(T->nextsibling);
	}
}

void post_order(CSTree T)
{
	if(T)
	{
		post_order(T->firstchild);
		printf("%s\n",T->data);
		post_order(T->nextsibling);
	}
}

3、查找结点

//无法搜索重复的结点
void Search(CSTree T,CSTNode **p,ElemType a)
{
	if(T)
	{
		if(strcmp(T->data,a) == 0)
		{
			*p = T;
			return ;
		}
		Search(T->firstchild,p,a);
		Search(T->nextsibling,p,a);
	}
}
//可搜索重复的结点保存在数组中
void Search_upgrade(CSTree T,CSTNode *p[],int *k,ElemType a)
{
	if(T)
	{
		if(strcmp(T->data,a) == 0)
			p[(*k)++] = T;
		Search_upgrade(T->firstchild,p,k,a);
		Search_upgrade(T->nextsibling,p,k,a);
	}
}

4、插入结点
先搜索父节点,然后创建一个结点,若父节点没有第一个孩子,那么这个结点就成为第一个孩子;反之,不断往第一个孩子的兄弟结点迭代,找到最后一个兄弟结点,然后与之连接。

int Insert(CSTree *T,ElemType fa,ElemType ch)
{
	CSTNode *p,*q,*s;
	if(!T)
		return 0;
	Search(*T,&p,fa);
	if(!p)
		return 0;
	q = (CSTNode*)malloc(sizeof(CSTNode));
	strcpy(q->data,ch);
	q->firstchild = NULL;
	q->nextsibling = NULL;
	if(!p->firstchild)
		p->firstchild = q;
	else
	{
		s = p->firstchild;
		while(s->nextsibling)
			s = s->nextsibling;
		s->nextsibling = q;
	}
	return 1;
}

5、删除以某结点为根的子树
采用后序删除的方式。

void PostDelete(CSTNode *T)
{
	if(T)
	{
		PostDelete(T->firstchild);
		PostDelete(T->nextsibling);
		free(T);
	}
}

在删除的结点处重新连接

void Reconnect(CSTNode *pfa,CSTNode *pch)
{
	if(pfa->firstchild == pch)
	{
		pfa->firstchild = pch->nextsibling;
		pch->nextsibling = NULL;
		PostDelete(pch);
	}
	if(pfa->nextsibling == pch)
	{
		pfa->nextsibling = pch->nextsibling;
		pch->nextsibling = NULL;
		PostDelete(pch);
	}
}

删除树中以某结点为根的子树。先进行查找,再进行删除

void DeleteTree(CSTree *T,ElemType fa,ElemType ch)
{
	CSTNode *pfa,*pch;
	if(strcmp("#",fa) == 0)//删除整棵树
	{
		PostDelete(*T);
		return ;
	}
	Search(*T,&pfa,fa);
	Search(*T,&pch,ch);
	if(!pfa || !pch)
		return ;
	if(pfa->firstchild == pch)
		Reconnect(pfa,pch);
	else
	{
		pfa = pfa->firstchild;
		while(pfa->nextsibling != pch)
			pfa = pfa->nextsibling;
		Reconnect(pfa,pch);
	}
}

6、凹入法输出

void dispTree(CSTree T,int level)
{
	int i,j;
	if(!T)
		return ;
	for(i = 1; i < level; i++)
		putchar(' ');
	printf("%s+",T->data);
	for(j = i+1; j < 40; j++) //j小于多少可自定义
		putchar('-');
	putchar('\n');
	dispTree(T->firstchild,level+3);// level加多少可自定义
	dispTree(T->nextsibling,level);//因为兄弟结点与第一个孩子是同一层的,所以level不用变。
}

7、输出所有路径
先向第一个孩子结点搜索,遇见结点就进栈,直到结点为叶子结点时输出并出栈,然后再往兄弟结点搜索。

void AllPathTree(CSTree T,Stack *S)
{
	while(T)
	{
		Push(S,T);
		if(!T->firstchild)
			PrintStack(*S);
		else
			AllPathTree(T->firstchild,S);
		Pop(S);
		T = T->nextsibling;
	}
}

8、求树的深度
最后比较的是d1+1和d2,因为d2是往兄弟结点搜索的,而与二叉树求深度不同的是,兄弟结点虽然相当于第一个孩子结点的右孩子,但是它实际上和第一个孩子结点是位于同一层的,因此树转换为二叉树之后,求得”右子树“的深度为d2,而求得"左子树"的深度为d1+1。

int depth(CSTree T)
{
	int d1,d2;
	if(T)
	{
		d1 = depth(T->firstchild);
		d2 = depth(T->nextsibling);
		return d1+1 > d2? d1+1:d2;
	}
	return 0;
}

你可能感兴趣的:(数据结构,算法,c语言,树结构)