樹的基本操作

今天這一章概述樹的基本操作,邊寫邊調試邊更新!!!


首先定義樹的節點結構:

#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);
	}
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章