从二叉排序树------浅谈C指针

来源:互联网 发布:抓取淘宝数据包 编辑:程序博客网 时间:2024/05/18 17:59

先看一段构造二叉排序树的代码

typedef int Tree_style;typedef struct Tree_node{Tree_style data;struct Tree_node *left_child;struct Tree_node *right_child;}Tree_node;typedef struct Tree{int num;//数据个数Tree_node *root;   //指向树的根结点}Tree;


void Creat_Node(Tree_node **t, Tree_style data){//此处需要使用指针的指针...然后才能为结点的孩子指针赋值...if(*t == NULL){*t = Malloc_Null();//将孩子结点赋值为NULL的malloc函数(*t)->data = data;return;}if((*t)->data > data){Creat_Node(&(*t)->left_child, data);}else{Creat_Node(&(*t)->right_child, data);}}

void Creat_Node(Tree_node **t, Tree_style data)
函数参数t使用了指向指针的指针类型..这样做的好处是,在进行当前节点的操作时不需要到父节点去修改parent->child 的指向..因为我们在递归调用的时候. 使用的是指向指针也就是说..我们得到的是 t = &parent->child的指针的指针..而不仅仅是当前节点的指针t = &node.  这样我们在调用Malloc_Null()函数的时候就不需要到父节点中 去修改parent->child.. 而是直接利用函数参数t直接完成...只需对t解除一次引用 *t即可直接获得对parent->child的操作权. 大大方便了操作...


再看一个没有充分利用指针的情况..

/*---------------------------------------------------------------------------------------*没有充分利用C语言指针的优越性.情况下的烂代码**在函数中若要对 一维指针的 操作仅仅是对 该指针的拷贝*Pv 的操作...只要不对Pv解除引用(*Pv..Pv->..Pv[]) 对于他指向的那块内存 不会发生任何变化我们可以取得只个Pv指针的地址..从而直接控制他所指向的内存...因为指针型的量也是 需要存放到内存中的..只要在内存中..就有地址...*当我们要对一个 一个链表..进行修改的时候...最好的方法就是将 指针的地址作为 参数 传递到函数中.或者直接在函数的 声明处..声明使用 引用*------------------------------------------------------------------------------------Tree_node * Find_Tree_Lastnode(Tree_node *t, Tree_style data){Tree_node *p = t;   //---p将是相对于q..指向q的下一个结点的指针Tree_node *q = t;//---q是当前查找到的结点的 双亲结点...while (1){if(p->data == data){return q;}else{//---若p不是要查找的结点..去分析p是否是要查找结点的 双亲结点...if(p->data < data){//---利用排序树的性质..分析该结点会在什么位置...if(t->right_child == NULL){printf("没有该结点..查找失败!\n");return NULL;}p = (t->right_child);//---p指向孩子..并在循环时候分析该孩子是否是 要查找的结点..q = t;//---让q是指向p的双亲..t = t->right_child;//---移动t..为下一次查找做准备..}else{if(p->data > data){if(t->left_child == NULL){printf("没有该结点..查找失败!\n");return NULL;}p = (t->left_child);q = t;t = t->left_child;}}}}}void Tree_Del(Tree_node * p, Tree_style data){Tree_node *N;   //N 表示被删掉的结点int L = 0, R = 0;if(p->left_child->data == data){N = p->left_child;L = 1;}else{N = p->right_child;R = 1;}if(N->left_child == NULL){if(L){p->left_child = N->right_child;}else{p->right_child = N->right_child;}free(N);}else{if(N->right_child == NULL){if(L){p->left_child = N->left_child;}else{p->right_child = N->left_child; }}else{.....}}}

以上是一个二叉排序树的删除结点的操作. .本想写完的..发现在没用指针的操作下..太繁琐..即要获得被删除结点的父节点..然后才能对parent->child 进行操作..如果我们充分利用指针的优越性..就可用简洁的代码完成. ..当然对于大神..肯定会有很好的方法.甚至不需要指针就能完成. ..

修改后的代码:

/*---------------------------------------------------------------------------------------------------------------------------*充分利用指针..以及递归形式 的 排序树删除函数*Tree_Del()使用 指针的指针作为参数..这样在找到当前要删除的结点时..相当于找到了 指向该结点的指针的指针...这才是内存中该树形链表的真实 数据段...*不然都仅仅是指针的一份拷贝...对其的修改 不会影响到 链表的链接情况..因为链接靠的是地址..* ----------------------------------------------------------------------------------------------------------------**一维指针, 只能修改其指向的内存位置中的数据..却不能修改指向内存中的位置..要想修改 指针指向内存位置. .必须求得指针的地址.****此时便是指针的指针..然后才能修改 一维指针所指向的内存位置.******-----------------------------------------------------------------------------------------------------------------*/void Tree_Mov(Tree_node **t){Tree_node *q;Tree_node *movnode, *movnode_parent;if((*t)->left_child == NULL){q = *t;*t = (*t)->right_child;free(q);}else{if((*t)->right_child == NULL){q = *t;*t = (*t)->left_child;free(q);}else{//---从左进入,向右遍历最大结点作为移动节点..movnode = (*t)->left_child;movnode_parent = *t;/*移动节点后需要为 被移动了的结点的双亲 改变孩子状态..除第一次向左转入后的结点作为移动结点的情况外.* 需要更改的双亲结点指针域肯定是 右孩子域..因为右孩子是最大的(被移动结点)...*/while(movnode->right_child != NULL){//开始往右遍历...movnode_parent = movnode;//记录可能是被移动结点的双亲结点..movnode = movnode->right_child;}(*t)->data = movnode->data;//并非是直接释放掉被删除释放结点的内存..而是将它的数据域替换..指针域留下.减少操作..最后释放movnode即可if(movnode_parent == *t){/*若第一次向左转入的结点 就是要被移动的结点..即被移动结点的双亲是 被删除的结点..就不是需要更改 其右孩子域* 而是其左孩子域的指向.令其指向 movnode结点的左孩子.*/(*t)->left_child = movnode->left_child;}else{movnode_parent->right_child = movnode->left_child;  /*只可能是movnodej的左孩子..右孩子若存在会继续遍历..**没有右孩子了.则只剩下左孩子(若有的情况下)*/}free(movnode);}}}void Tree_Del(Tree_node **t, Tree_style data){//使用 指针的指针作为参数..if(*t != NULL){return;}else{if((*t)->data == data){Tree_Mov(t);}else{if((*t)->data < data){Tree_Del(&(*t)->right_child, data);}else{Tree_Del(&(*t)->left_child, data);}}}}
=====================================================

以上存储结构都可以看成一个广义的链表... 如果我们要对链表进行操作..最好的方法一定要充分利用指针...因为其中必然涉及到 对一个指针所指向的内存区域的释放和对指针所指向的内存区域的改变...对所指向区域的 释放指向得到 指向该区域的指针即可..然而对于 改变指针所指向的区域并且保证不影响上一个与它相联系的结点不变..链表中往往在上一个结点中记录其后继结点的位置...所以在高效率的情况下..只需得到当前指向要修改的区域的指针的指针p..而该指针还是 parent->next的地址..即p == &parent->next;




2013年12月1日13:26:13















原创粉丝点击