大半年前学二叉树时,看不懂删除结点的代码,对递归的理解很浅,今天把问题一并解决,加深对递归的理解。
1.删除结点的算法描述:如果待删除的结点至多只有一个子结点,那么需要删除的就是这个结点本身,通过改变指针指向即可;如果待删除的结点有2个结点,那么就需要删除这个结点在中序遍历时的直接后继结点,再通过赋值完成删除。删除后要记得释放结点空间。
2.递归:递归在非线性结构中用的非常普遍。二叉树的查找,插入,删除都可以用递归实现。以前刚学递归的时候老师说递归算法需要长时间的学习才能够入门,当时根本感觉不到,现在却越来越体会到了。递归固然能够使得程序清晰简洁,但是如果写错,debug的时候是比较痛苦的。
3.总结:《算法导论》上描述的删除结点的算法非常简洁而且清晰,逻辑非常严密,但自己再理解上花费了不少时间,说明自己的思考练习还差的很远,今后要进一步加强思维上的练习。
4.算法代码及测试代码:
代码如下: 注:前半部分函数是大半年前的代码,命名规则上与现在有所不同。
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct node//定义一个二叉树结点
{
long item;//结点数值
int count;//计数器
struct node *pLeft;
struct node *pRight;
}NODE;
//函数原型
NODE *CreatNode(long value);
NODE *AddNode(long value,NODE *pNode);
void ListNodesAsc(NODE *pNode);
NODE *tree_search(NODE *pNode,long k);
NODE *iterative_tree_search(NODE *pNode,long k);
NODE *iterative_tree_search(NODE *pNode,long k);
NODE *tree_minimum(NODE *pNode);
NODE *tree_maximun(NODE *pNode);
void parent(NODE *&pNode,NODE *&p,NODE *head);
NODE *tree_successor(NODE *&pNode,NODE *&p,NODE *head);
void tree_delete(NODE *head,long k);
//声明全局变量,存储根结点
NODE *root=NULL;
int main()
{
long value=0;
NODE *pNode=NULL;
long test[]={15,6,3,7,2,4,13,9,18,17,20};//测试数据
for(int i=0;i<11;i++)
{
value=test[i];
if(pNode==NULL)
pNode=CreatNode(value);
else
AddNode(value,pNode);
}
root=pNode;//得到根结点
ListNodesAsc(pNode);
printf("\n");
NODE *result=NULL;
//测试
//result=tree_successor(pNode->pLeft->pRight->pRight,result,pNode);
//parent(pNode->pLeft->pRight->pRight,result,pNode);
//printf("\n%ld",result->item);
tree_delete(pNode,15);
ListNodesAsc(pNode);
getch();
}
//创建结点
NODE *CreatNode(long value)
{
NODE *pNode=(NODE *)malloc(sizeof(NODE));
pNode->item=value;
pNode->count=1;
pNode->pLeft=pNode->pRight=NULL;
return pNode;
}
//添加结点
NODE *AddNode(long value,NODE *pNode)
{
if(pNode==NULL)
return CreatNode(value);
if(pNode->item==value)
{
pNode->count++;
return pNode;
}
else if(value<pNode->item)
{
if(pNode->pLeft==NULL)
{
pNode->pLeft=CreatNode(value);
return pNode->pLeft;//注意:这里不能直接写成return CreatNode(value);是因为必须要有根结点与左结点的联系
}
else //加上else看起来更加的清晰,有利于维护
return AddNode(value,pNode->pLeft);
}
else //value>pNode->item的情况
{
if(pNode->pRight==NULL)
{
pNode->pRight=CreatNode(value);
return pNode->pRight;
}
else
return AddNode(value,pNode->pRight);
}
}
//升序排列二叉树(中序遍历二叉树)
void ListNodesAsc(NODE *pNode)
{
if(pNode->pLeft!=NULL)
ListNodesAsc(pNode->pLeft);
for(int i=0;i<pNode->count;i++)
printf("%d ",pNode->item);
if(pNode->pRight!=NULL)
ListNodesAsc(pNode->pRight);
}
//查找
NODE *tree_search(NODE *pNode,long k)
{
if(pNode==NULL || k==pNode->item)
return pNode;
if(k<pNode->item)
return tree_search(pNode->pLeft,k);
else
return tree_search(pNode->pRight,k);
}
//非递归查找
NODE *iterative_tree_search(NODE *pNode,long k)
{
while(pNode!=NULL && k!=pNode->item)
{
if(k<pNode->item)
pNode=pNode->pLeft;
else
pNode=pNode->pRight;
}
return pNode;
}
//查找最小值
NODE *tree_minimum(NODE *pNode)
{
if(pNode==NULL)
return NULL;
while(pNode->pLeft!=NULL)
pNode=pNode->pLeft;
return pNode;
}
//查找最大值
NODE *tree_maximun(NODE *pNode)
{
if(pNode==NULL)
return NULL;
while(pNode->pRight!=NULL)
pNode=pNode->pRight;
return pNode;
}
//计算父亲结点
void parent(NODE *&pNode,NODE *&p,NODE *head)
{
if(head!=NULL)
{
if((head->pLeft!=NULL && head->pLeft==pNode )|| (head->pRight!=NULL && head->pRight==pNode))
{
p=head;
//return p; //问:为什么这里必须要返回一个指针? 粗心错误,把这个函数的返回值误设为void *
}
if(head->pLeft!=NULL)
parent(pNode,p,head->pLeft);
if(head->pRight!=NULL)
parent(pNode,p,head->pRight);
}
}
//查找一个结点的后继
NODE *tree_successor(NODE *&pNode,NODE *&p,NODE *head)
{
if(pNode->pRight!=NULL)
return tree_minimum(pNode->pRight);
parent(pNode,p,head);
NODE *p1=p;
while(p1!=NULL && p1->pRight==pNode)
{
pNode=p1;
parent(p1,p1,head);
}
return p1;
}
//删除一个结点
void tree_delete(NODE *head,long k)
{
NODE *target=tree_search(head,k);
NODE *y=NULL,*x=NULL;
NODE *p=NULL;
if(target->pLeft==NULL || target->pRight==NULL)
y=target;
else
y=tree_successor(target,p,head);
if(y->pLeft!=NULL)
x=y->pLeft;
else
x=y->pRight;
parent(y,p,head);
if(p==NULL)
root=x;
else if(y==p->pLeft)
p->pLeft=x;
else
p->pRight=x;
if(y!=target)
{
target->item=y->item;
free(y);//释放被删除的结点的空间
}
else
free(target);
}