樹的實現與操作(C語言實現)
首先來簡單說下一些關於的基本概念。
樹是一種非線性的資料結構
1,樹是由 n(n>=0)個結點組成的有限集合
如果n = 0 ,稱為空樹
如果n > 0,則:
有一個特定的稱之為根(root)的結點,它只有直接後繼,但沒有直接前驅
除了根以外的其他結點劃分為:m(m>=0)個互不相交的有限集合,T0,T1,T2…Tn-1,每個集合又是一棵樹,並且稱之為根的子樹
2,樹中的概念:
樹的結點包括一個數據及若干指向子樹的分支
結點擁有的子樹樹稱為結點的度
度為0的結點稱為葉結點
度不為0的結點稱為分支結點
樹的度的定義為所有結點中的度的最大值
3,樹中結點之間的關係
(1)結點的直接後繼稱為該結點的孩子
(2)相應的該結點稱為孩子的雙親
(3)結點的孩子的孩子的……稱為該結點的子孫
(4)相應的該結點稱為子孫的祖先
(5)同一個雙親的孩子之間互稱兄弟
4,樹的深度或高度
(1)結點的層次
(2)根為第1層
(3)根的孩子為第2層
(4)……
(5)樹中結點的最大層次稱為樹的深度或高度
5,如果樹中結點的各子樹從左向右是有次序的,子樹間不能互換位置,則稱該樹為有序樹,否則為無序樹。
6,森林是由 n(n >=0)棵互不相交的樹組成的集合
7,樹的相關操作
樹的操作
樹的一些常用操作
->建立樹
->銷燬樹
->清空樹
->插入結點
->刪除結點
->
->獲取根結點
->獲取樹的結點數
->獲取樹的高度
->獲取樹的度
樹在程式中表現為一種特殊的資料型別
樹的操作在程式中的表現為一組函式
Tree:
Tree*Tree_Create();
voidTree_Destroy(Tree* tree);
voidTree_Clear(Tree* tree);
intTree_Insert(Tree* tree, TreeNode* node, int pos);
TreeNode*Tree_Delete(Tree* tree, int pos);
TreeNode*Tree_Get(Tree* tree, int pos);
TreeNode*Tree_Root(Tree* tree);
intTree_Height(Tree* tree);
intTree_Count(Tree* tree);
intTree_Degree(Tree* tree);
再來說下,這裡樹的基本儲存結構。假定我們初始有一棵樹,那麼我們如何來規定他的儲存結構呢?
首先,我們規定每個樹結點中有三個屬性(1)表示其父親的指標(2)表示結點中的資料(3)表示孩子的連結串列,這裡為什麼是個連結串列呢?因為一個結點的的孩子可能有一個,也有可能有多個,所以用一個連結串列表示最為合適了。
第二,每個樹之間的關係,我們可以模仿二叉樹中的先序遍歷思想,對這顆樹進行遍歷,我們知道,遍歷的結果肯定是個序列,那麼我們此時就可以果斷的把這種序列認為是一種連結串列結構,那麼後期對於樹的操作也就可以移植到連結串列上來了。
最後,關於樹的深度、度、刪除、根結點的獲取與計算,都是在表示那顆樹的結點上來操作的。那麼這裡特別說明一下,由於整個樹存在兩個連結串列,所以對於每次刪除,插入都要向兩個連結串列中刪除和插入。(一個結點既要存在於他父親的孩子連結串列中,又要存在於表示整棵樹的連結串列中)
這裡我們用到了連結串列的知識,如果對於連結串列不熟悉,可以參閱
原始碼:
#ifndef _GTREE_H_
#define _GTREE_H_
typedef void GTree;
typedef void GTreeData;
typedef void (GTree_Printf)(GTreeData*);
GTree* GTree_Create();
void GTree_Destroy(GTree* tree);
void GTree_Clear(GTree* tree);
int GTree_Insert(GTree* tree, GTreeData* data, int pPos);
GTreeData* GTree_Delete(GTree* tree, int pos);
GTreeData* GTree_Get(GTree* tree, int pos);
GTreeData* GTree_Root(GTree* tree);
int GTree_Height(GTree* tree);
int GTree_Count(GTree* tree);
int GTree_Degree(GTree* tree);
void GTree_Display(GTree* tree, GTree_Printf* pFunc, int gap, char div);
#endif
CPP實現部分:
#include "stdafx.h"
#include <malloc.h>
#include "GTree.h"
#include "LinkList.h"
//樹中的結點
typedef struct _tag_GTreeNode GTreeNode;
struct _tag_GTreeNode
{
GTreeData* data;
GTreeNode* parent;
LinkList* child;
};
//樹
typedef struct _tag_TLNode TLNode;
struct _tag_TLNode
{
LinkListNode header;
GTreeNode* node;
};
//列印樹
static void recursive_display(GTreeNode* node, GTree_Printf* pFunc, int format, int gap, char div)
{
int i = 0;
if( (node != NULL) && (pFunc != NULL) )
{
for(i=0; i<format; i++)
{
printf("%c", div);
}
pFunc(node->data);
printf("\n");
for(i=0; i<LinkList_Length(node->child); i++)
{
TLNode* trNode = (TLNode*)LinkList_Get(node->child, i);
recursive_display(trNode->node, pFunc, format + gap, gap, div);
}
}
}
static void recursive_delete(LinkList* list, GTreeNode* node)
{
if( (list != NULL) && (node != NULL) )
{
GTreeNode* parent = node->parent;
int index = -1;
int i = 0;
//將結點從表示樹的連結串列中刪除
for(i=0; i<LinkList_Length(list); i++)
{
TLNode* trNode = (TLNode*)LinkList_Get(list, i);
if( trNode->node == node )
{
LinkList_Delete(list, i);
free(trNode);
index = i;
break;
}
}
//如果index>0,則表明他有父親
if( index >= 0 )
{
if( parent != NULL )
{
//將他從他父親的孩子連結串列中刪除
for(i=0; i<LinkList_Length(parent->child); i++)
{
TLNode* trNode = (TLNode*)LinkList_Get(parent->child, i);
if( trNode->node == node )
{
LinkList_Delete(parent->child, i);
free(trNode);
break;
}
}
}
//如果他有兒子,將他的兒子們都殺死
while( LinkList_Length(node->child) > 0 )
{
TLNode* trNode = (TLNode*)LinkList_Get(node->child, 0);
recursive_delete(list, trNode->node);
}
LinkList_Destroy(node->child);
free(node);
}
}
}
static int recursive_height(GTreeNode* node)
{
int ret = 0;
if( node != NULL )
{
int subHeight = 0;
int i = 0;
for(i=0; i<LinkList_Length(node->child); i++)
{
TLNode* trNode = (TLNode*)LinkList_Get(node->child, i);
subHeight = recursive_height(trNode->node);
if( ret < subHeight )
{
ret = subHeight;
}
}
ret = ret + 1;
}
return ret;
}
static int recursive_degree(GTreeNode* node)
{
int ret = -1;
if( node != NULL )
{
int subDegree = 0;
int i = 0;
ret = LinkList_Length(node->child);
for(i=0; i<LinkList_Length(node->child); i++)
{
TLNode* trNode = (TLNode*)LinkList_Get(node->child, i);
subDegree = recursive_degree(trNode->node);
if( ret < subDegree )
{
ret = subDegree;
}
}
}
return ret;
}
GTree* GTree_Create()
{
return LinkList_Create();
}
void GTree_Destroy(GTree* tree)
{
GTree_Clear(tree);
LinkList_Destroy(tree);
}
void GTree_Clear(GTree* tree)
{
GTree_Delete(tree, 0);
}
int GTree_Insert(GTree* tree, GTreeData* data, int pPos)
{
LinkList* list = (LinkList*)tree; //傳進被插入的樹,表示的實質為連結串列
//合法性判斷
int ret = (list != NULL) && (data != NULL) && (pPos < LinkList_Length(list)); //所插入的結點必須在樹當中,
//故而是pPos < LinkList_Length(list)
if( ret )
{
TLNode* trNode = (TLNode*)malloc(sizeof(TLNode)); //建立一個結點,用於記錄儲存樹的連結串列中的結點
TLNode* cldNode = (TLNode*)malloc(sizeof(TLNode)); //孩子(是個連結串列)
TLNode* pNode = (TLNode*)LinkList_Get(list, pPos); //從表示樹的連結串列中獲取要插入結點父母親
GTreeNode* cNode = (GTreeNode*)malloc(sizeof(GTreeNode)); //要插入的結點,用於接收傳進來的data
ret = (trNode != NULL) && (cldNode != NULL) && (cNode != NULL); //樹中結點不能為空,該結點
if( ret )
{
cNode->data = data; //儲存資料
cNode->parent = NULL; //現在還弄不清他的父母親是誰
cNode->child = LinkList_Create(); //要插入的結點的孩子是個連結串列
trNode->node = cNode; //將要插入的結點賦值給表示樹的連結串列
cldNode->node = cNode; //將要插入的結點賦值給樹結點中的孩子連結串列
LinkList_Insert(list, (LinkListNode*)trNode, LinkList_Length(list)); //向表示樹的連結串列中插入
if( pNode != NULL ) //如果要插入的結點有父節點,就向父節點的子結點連結串列插入該結點
{
cNode->parent = pNode->node;//認親的過程
//正式加入大家庭
LinkList_Insert(pNode->node->child, (LinkListNode*)cldNode, LinkList_Length(pNode->node->child));
}
}
else
{
free(trNode);
free(cldNode);
free(cNode);
}
}
return ret;
}
//刪除結點
GTreeData* GTree_Delete(GTree* tree, int pos)
{
TLNode* trNode = (TLNode*)LinkList_Get(tree, pos);
GTreeData* ret = NULL;
if( trNode != NULL )
{
ret = trNode->node->data;
recursive_delete(tree, trNode->node);
}
return ret;
}
//獲得指定位置的結點
GTreeData* GTree_Get(GTree* tree, int pos)
{
TLNode* trNode = (TLNode*)LinkList_Get(tree, pos);
GTreeData* ret = NULL;
if( trNode != NULL )
{
ret = trNode->node->data;
}
return ret;
}
//獲得根結點
GTreeData* GTree_Root(GTree* tree)
{
return GTree_Get(tree, 0);
}
//求樹的高度
int GTree_Height(GTree* tree)
{
TLNode* trNode = (TLNode*)LinkList_Get(tree, 0);
int ret = 0;
if( trNode != NULL )
{
ret = recursive_height(trNode->node);
}
return ret;
}
//求樹的結點個數
int GTree_Count(GTree* tree)
{
return LinkList_Length(tree);
}
//求樹的度
int GTree_Degree(GTree* tree)
{
TLNode* trNode = (TLNode*)LinkList_Get(tree, 0);
int ret = -1;
if( trNode != NULL )
{
ret = recursive_degree(trNode->node);
}
return ret;
}
void GTree_Display(GTree* tree, GTree_Printf* pFunc, int gap, char div)
{
TLNode* trNode = (TLNode*)LinkList_Get(tree, 0);
if( (trNode != NULL) && (pFunc != NULL) )
{
recursive_display(trNode->node, pFunc, 0, gap, div);
}
}
主函式操作部分:
// 樹.cpp : 定義控制檯應用程式的入口點。
//
#include "stdafx.h"
#include "GTree.h"
#include <stdlib.h>
void printf_data(GTreeData* data)
{
printf("%c", (int)data);
}
int _tmain(int argc, _TCHAR* argv[])
{
GTree* tree = GTree_Create();
int i = 0;
GTree_Insert(tree, (GTreeData*)'A', -1);
GTree_Insert(tree, (GTreeData*)'B', 0);
GTree_Insert(tree, (GTreeData*)'C', 0);
GTree_Insert(tree, (GTreeData*)'D', 0);
GTree_Insert(tree, (GTreeData*)'E', 1);
GTree_Insert(tree, (GTreeData*)'F', 1);
GTree_Insert(tree, (GTreeData*)'H', 3);
GTree_Insert(tree, (GTreeData*)'I', 3);
GTree_Insert(tree, (GTreeData*)'J', 3);
printf("Tree Height: %d\n", GTree_Height(tree));
printf("Tree Degree: %d\n", GTree_Degree(tree));
printf("Full Tree:\n");
GTree_Display(tree, printf_data, 2, ' ');
printf("Get Tree Data:\n");
for(i=0; i<GTree_Count(tree); i++)
{
printf_data(GTree_Get(tree, i));
printf("\n");
}
printf("Get Root Data:\n");
printf_data(GTree_Root(tree));
printf("\n");
GTree_Delete(tree, 3);
printf("After Deleting D:\n");
GTree_Display(tree, printf_data, 2, '-');
GTree_Clear(tree);
printf("After Clearing Tree:\n");
GTree_Display(tree, printf_data, 2, '.');
GTree_Destroy(tree);
system("pause");
return 0;
}
執行結果:
Tree Height: 3
Tree Degree: 3
Full Tree:
A
B
E
F
C
D
H
I
J
Get Tree Data:
A
B
C
D
E
F
H
I
J
Get Root Data:
A
After Deleting D:
A
--B
----E
----F
--C
After Clearing Tree:
請按任意鍵繼續. . .
如有錯誤,望不吝指出。