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