更新一下树的学习笔记
首先树不同于二叉树,它可以增加任意多的子节点,更符合我们日常的使用习惯。
本文建立的树使用以下结构:
存放数据的结构体:
typedef struct _data{
int id;
char name[20];
}data;
存放节点的结构体:
typedef struct _node{
data *d;
struct _node *parent,*first_child,*last_child,*next_sibling,*prev_sibling;
}node;
树的控制块:
typedef struct _tree{
node *root;
}tree;
然后导入数据用的一个数组:
typedef struct _bom{
int id;
char name[20];
int pid;
}bom;
树的子节点很多关系也复杂,但是如果能像二叉树那样获取一个节点的下一个节点事实上操作就会明了很多。所以要先编写获取前一个节点和后一个节点以及获取子树最后一个节点的函数。这些是后续操作的基础。
node *tree_get_next(node *n){
if(n==NULL){
return NULL;
}
if(n->first_child!=NULL){
return n->first_child;
}else if(n->next_sibling!=NULL){
return n->next_sibling;
}else{
node *t = n->parent;
while(t!=NULL&&t->next_sibling==NULL){
t = t->parent;
}
if(t != NULL){
return t->next_sibling;
}else{
return NULL;
}
}
}
node *tree_get_prev(node *n){
if(n->prev_sibling!=NULL){
if(n->prev_sibling->last_child!=NULL){
node *p = n->prev_sibling->last_child;
while(p->last_child!=NULL){
p=p->last_child;
}
return p;
}else{
return n->prev_sibling;
}
}else{
if(n->parent!=NULL){
return n->parent;
}else{
return NULL;
}
}
}
node *tree_get_last_child(node *n){
while(n!=NULL&&n->last_child!=NULL){
n=n->last_child;
}
return n;
}
有了这些函数作支持,很容易写出查找函数。
node *tree_find_node(tree *t,int id){
node *n = t->root;
while(n!=NULL){
if(n->d->id==id){
return n;
}
n = tree_get_next(n);
}
return NULL;
}
写出查找函数之后也就可以继续完成添加节点的操作了:
int tree_add_node(tree *t,int pid,data *d){
//1.search target of this pid
node *n = tree_find_node(t,pid);
if(n==NULL&&t->root!=NULL){
return 0;
}
//2.create the node of data
node *p = (node *)malloc(sizeof(node));
if(p==NULL){
printf("Overflow");
return 0;
}
p->d = d;
p->first_child=NULL;
p->last_child=NULL;
p->next_sibling=NULL;
p->parent=NULL;
p->prev_sibling=NULL;
//3.create or modify the parent node's child pointer
p->parent=n;
if(t->root==NULL){
t->root=p;
}else if(n->first_child==NULL){
n->first_child=p;
n->last_child=p;
}else{
n->last_child->next_sibling=p;
p->prev_sibling=n->last_child;
n->last_child=p;
}
return -1;
}
遍历操作:
void tree_iterate(tree *t){
node *n = t->root;
while(n!=NULL){
printf("\n%d",n->d->id);
n=tree_get_next(n);
}
}
从树上面取出子树也是一个常用的操作,对后面的子树进行取出操作:
node *tree_extract(tree *t,int id){
//1.查找节点->判断节点是否为空或根节点
node *n = tree_find_node(t,id);
if(n==NULL){
printf("节点没有找到");
return NULL;
}
if(n->parent==NULL){
printf("不能删除根节点");
return NULL;
}
//2.调整指针,分离节点(子树)
if(n->next_sibling==NULL&&n->prev_sibling==NULL){
n->parent->first_child=NULL;
n->parent->last_child=NULL;
}else if(n->prev_sibling==NULL){
n->parent->first_child=n->next_sibling;
n->next_sibling->prev_sibling=NULL;
}else if(n->next_sibling==NULL){
n->parent->last_child=n->prev_sibling;
n->prev_sibling->next_sibling=NULL;
}else{
n->prev_sibling->next_sibling=n->next_sibling;
n->next_sibling->prev_sibling=n->prev_sibling;
}
n->parent=NULL;
n->next_sibling=NULL;
n->prev_sibling=NULL;
return n;
}
剪枝操作:
//剪枝删除
int tree_cut(tree *t,int id){
//extract函数分离要剪枝的节点(子树)
node *n = tree_extract(t,id);
if(n==NULL){
return 0;
}
//2.析构子树,释放空间。此处注意倒序析构
n=tree_get_last_child(n);
while(n!=NULL){
node *t=tree_get_prev(n);
free(n->d);
free(n);
n=t;
}
return -1;
}
清空树操作:
int tree_empty(tree *t){
node *n=tree_get_last_child(t->root);
while(n!=NULL){
node *t = tree_get_prev(n);
free(n->d);
free(n);
n=t;
}
return -1;
}
有时候需要判断两个节点间是否存在父子关系(这个实际上可以用于上下级关系的查询?):
int tree_has_as_parent(tree *t,int id,int pid){
if(id==pid){
return 0;
}
//判断节点是否存在
node *n=tree_find_node(t,id);
if(n==NULL){
return 0;
}
node *pn=tree_find_node(t,pid);
if(pn==NULL){
return 0;
}
n=n->parent;
while(n!=NULL){
if(n->d->id==id){
return -1;
}
n=n->parent;
}
return 0;
}
最后完成的是父节点重构,稍微有点复杂,但是和前面的操作很类似,分为5步进行:
int tree_reparent(tree *t,int id,int pid){
//1.判定是否重构到自己
if(id==pid){
return(0);
}
//2.判定id,pid是否存在
node *n=tree_find_node(t,id);
if(n==NULL){
return(0);
}
node *pn=tree_find_node(t,pid);
if(pn==NULL){
return(0);
}
//3.判断是否重构到子节点上或者id为根节点
/*
if(tree_has_as_parent(t,id,pid)){
return 0;
}
*/
if(n->parent==NULL||n->parent->d->id==pid){
return 0;
}
node *p=pn->parent;
while(p!=NULL){
if(p->d->id==pid){
return 0;
}
p=p->parent;
}
//4.提取id节点(子树)
if(n->prev_sibling==NULL&&n->next_sibling==NULL){
n->parent->first_child=NULL;
n->parent->last_child=NULL;
}else if(n->prev_sibling==NULL){
n->parent->first_child=n->next_sibling;
n->next_sibling->prev_sibling=NULL;
}else if(n->next_sibling==NULL){
n->parent->last_child=n->prev_sibling;
n->prev_sibling->next_sibling=NULL;
}else{
n->prev_sibling->next_sibling=n->next_sibling;
n->next_sibling->prev_sibling=n->prev_sibling;
}
n->parent=pn;
n->next_sibling=NULL;
n->prev_sibling=NULL;
//5.将id节点加入pid节点
if(pn->first_child==NULL){
pn->first_child=n;
pn->last_child=n;
}else{
//加到尾部
pn->last_child->next_sibling=n;
n->prev_sibling=pn->last_child;
pn->last_child=n;
}
return -1;
}
最后编写一个main函数测试一下:
int main() {
bom bs[]={
{1,"",-1},
{2,"",1},
{3,"",1},
{4,"",1},
{5,"",2},
{6,"",2},
{7,"",2},
{8,"",3},
{9,"",3},
{10,"",3},
{11,"",5},
{12,"",5},
{13,"",5},
{14,"",9},
{15,"",9},
{16,"",9}
};
//create a new tree
data *d;
tree *t=(tree *)malloc(sizeof(tree));
if(t==NULL){
printf("overflow");
return;
}
t->root=NULL;
//add nodes, golang JSON->dimension
int i;
for(i=0;i<16;i++){
d=(data *)malloc(sizeof(data));
d->id=bs[i].id;
tree_add_node(t,bs[i].pid,d);
}
//dfs(depth first search): iterate through the tree,bfs(breadth first search)
tree_iterate(t);
node *n=tree_get_last_child(t->root);
if(n!=NULL){
printf("\nlast child:%d",n->d->id);
}else{
printf("\nnot found!");
}
printf("\nreverse iterate:");
if(n!=NULL){
printf("\n%d",n->d->id);
n=tree_get_prev(n);
while(n!=NULL){
printf("\n%d",n->d->id);
n=tree_get_prev(n);
}
}
n=tree_find_node(t,3);
if(n==NULL){
exit(0);
}
n=tree_get_last_child(n);
if(n!=NULL){
printf("\nlast child:%d",n->d->id);
}else{
printf("\nnot found!");
}
printf("\nreverse iterate:");
if(n!=NULL){
printf("\n%d",n->d->id);
n=tree_get_prev(n);
while(n!=NULL){
printf("\n%d",n->d->id);
if(n->d->id==3){
break;
}
n=tree_get_prev(n);
}
}
printf("\n=======================");
//tree_cut(t,3);
tree_reparent(t,5,4);
tree_iterate(t);
//suspend
getch();
}
//END
完成。