数的表示:1 (顺序表) 用数组的下标表示双亲与孩子之间的关系
2 (链表) 将树转换成二叉树,右孩子代表兄弟结点,左孩子代表孩子结点
链式结构的树以及 顺序结构的树 的定义如下:
typedef char elemtype;
// 链式存储
typedef struct tree
{
elemtype data;
struct tree *firstchild;
struct tree *brother;
}tree;
// 顺序存储
typedef struct list_tree
{
elemtype data;
int parent;
}list_tree;
如上图所示的一棵树,如果用链式的结构定义则为:
如果用顺序结构定义,则表示为:
将一颗链式的树 转换成 数组形式的树:
我的思想是:
先得到此链式树共有多少个结点,然后calloc开辟这么多个连续的空间 用做存放顺序结构的树。这样的动态开辟避免了数组空间的浪费,
(如果链式结构新插入了一个结点元素,需要重新调用此函数,重新开辟一块连续的空间)
然后先创建根节点。
调用make 函数,在每一次make函数中,需要传的参数是当前遍历到的链式树的结点,顺序树的首元素位置,此结点的双亲结点在数组中的下标,以及数组的指针(游标,以记录数组的位置);
在一次函数中,会递归当前结点的第一个孩子,(因为此结点以下也是一棵树),
然后将此结点第一个孩子的所有兄弟结点(即此结点的所有孩子)都填充到数组中,他们具有相同的父节点,所以parent中记录的位置是一致的。
并且 每一个兄弟结点都要当做一颗树来看待,继续递归。
list_tree *make_listtree(tree *root, int count)
//根据链式的树创建 顺序结构的树
list_tree *list_root = (list_tree*)calloc(count, sizeof(list_tree));
list_root->parent = -1;
int index = 0;
make(root, list_root, 0,&index);
}
void make(tree *ptr, list_tree *list_ptr, int list_ptr_index,int *list_index)
{
//
ptr = ptr->firstchild;
if (ptr == NULL)
{
return ;
}
*list_index += 1;
list_ptr[*list_index].data = ptr->data;
list_ptr[*list_index].parent = list_ptr_index;
if (ptr->firstchild != NULL)
{
make(ptr, list_ptr, *list_index, list_index);
}
while (ptr->brother != NULL)
{
ptr = ptr->brother;
*list_index += 1;
list_ptr[*list_index].data = ptr->data;
list_ptr[*list_index].parent = list_ptr_index;
make(ptr, list_ptr, *list_index, list_index);
//
}
}
其中的关键是需要记录数组的下标,以及此结点的双亲结点在数组中的下标。
将链式结构的树转换成顺序结构的树的目的在于:
链式结构的树没有办法存放到文件中,
(树中结点间的关系用指针来表示,而指针中记录的是结点在当前进程中逻辑地址,当进程结束后,文件中链式结构的树间的关系也变得无意义。)
没有办法存放到文件中,同样也没有办法传输给他人使用。
而顺序结构的树中,通过一个整型值记录孩子结点与双亲结点在数组中的位置关系,这个是不会变的。
以下为结果截图:
(包含两个函数,tree *find_value(tree *ptr, elemtype x);
tree *find_parent(tree *ptr, tree *child);
如果将 顺序结构的树存放到文件中,是非常简单的事了,如下图:
树的遍历:1 先根遍历 (先遍历根节点 再依次先根遍历每颗子树)
2 后根遍历 (先依次后根遍历每颗子树 再访问根节点)
针对最上面那棵树:
先根遍历序列:ABCDEFGHIJKLM
后根遍历序列:EFBKLGCHIMJDA
先根遍历的代码如下:(需要用到队列)
我的算法思想: 将根节点入队列,在队列不为空的前提下,
出队列首元素,打印其值并且将此元素的第一个孩子结点以及第一个孩子结点的所有兄弟结点 入队列(即将出队的这个元素的所有孩子结点入队列)
利用了队列的先入先出,来进行树的先根遍历
void print_first_tree(tree *root, sq_queue *q) //先根遍历
{
root = root->firstchild;
if (root == NULL)
{
return;
}
in_squeue(q,root);
while (root->brother != NULL)
{
root = root->brother;
in_squeue(q, root);
}
}
void first_order_ltree(tree *root)
{
if (root == NULL)
return;
sq_queue q;
init_squeue(&q);
in_squeue(&q, root);
while (lenth_squeue)
{
tree *temp=out_squeue(&q);
printf("%c ", temp->data);
print_first_tree(temp, &q);
}
}
所有源码在:https://github.com/xuatcc/tree.git
包括以下函数、;
tree *make_root(elemtype x);
tree *insert_child(tree *ptr, elemtype x);
tree *insert_brother(tree *ptr, elemtype x);
tree *find_value(tree *ptr, elemtype x);
tree *find_parent(tree *ptr, tree *child);
tree *parent(tree *ptr, tree *child);
int size_tree(tree *ptr, int *count); //统计数的结点个数
list_tree *make_listtree(tree *root, int count); //根据链式的树创建 顺序结构的树
void make(tree *ptr, list_tree *list_ptr, int list_ptr_index, int *list_index);
void save_file(tree *ptr); //把一个链式的树存在文件中
tree *make_linktree(list_tree *root, int count); //根据顺序树 创建 链式结构的树
void make_link(list_tree *root, tree*link_root, int count);
int find_index(list_tree *root, elemtype data); //查询一个元素在顺序树中的index
void print_first_tree(tree *root, sq_queue *q) //先根遍历
void first_order_ltree(tree *root);