数据结构习题
王道书上的一个习题,题目如下:
已知二叉树以二叉链表存储,编写算法完成:对于树中每个元素值为x的结点,删去以它为根的子树,并释放相应的空间
分析
最近死磕数据结构,在链表的那一节,遇到一个题目,要求用递归的方法删除链表中值为x的所有节点,刚接触到递归,整的一头雾水,书上的答案感觉不是很好,甚至还觉得有点儿莫名其妙,于是在网上找了找,看到如下的一篇文章
c语言设计一个递归算法,删除不带头结点的单链表L中所有值为x的节点–五月的天气
根据这篇文章,再结合《C Primer Plus》中关于递归的讲解,理解了递归的特性之后,终于看懂了博主的代码
一个很重要的思路就是利用return返回指针,由尾结点指针开始,逐级返回,一直到头结点
刚开始看的时候:可以,这很信条!
在搞懂程序之后,发现博主的评论区很迷惑,不是自己动手研究代码,而是上来说就说程序有什么什么样的问题,光动嘴能学会数据结构?
本题要删除值为x的结点,以及以x为根结点的后续所有结点,则只要发现x,就把x的左右子树,以及左右子树的左右子树全部删除【套娃环节,马上开始】
删除当然还是要从叶节点开始,逐层往上删除,那么当然就少不了递归,用牺牲空间的方法换取逻辑结构的简洁明了,我觉得很划算,并且递归中所用的栈都是系统自动创建和回收,省事儿!
采用先序遍历的方法,根-左-右 当发现root->data 等于 x,则说明root的左右子树均需要删除,利用一个tag变量,辅助表示当前结点是否是x的子孙结点
如果tag等于1 则本层root的左右结点均需要删除,如果左右子树均存在,则将 1传递到左右子树上去,当当前结点为叶结点时,递归停止,此时检查tag是否为1,如果为1,则删除当前结点,释放空间,并且返回一个空指针
如果tag等于0,则只对当前根节点做前序遍历即可
代码
听王道的老师说,数据结构的学习,对于考研来说,没必要上机自己实操,太花时间了,对此我也深有体会,今天之所以会自己上机跑一下,实在是二叉树这块儿一下20个大题,我自己的方法往往跟课后答案思路很不相同,特别是这个题,我只写了11行代码,翻开课后答案一看,好家伙 满满一页纸,还借助了一个队列。
自己验算了好几遍,感觉我的方法没问题,但是Jian不自信,加上这节的大题实在变态,大量的花式递归,太搞心态了,抱着找找信心的目的,拿出电脑,敲上代码。
是骡子是马 拉出来溜溜!
//借鉴了之前那个博主提到过的思路
//利用递归 逐级返回子树的结点指针
BiTree PreOrderDe(BiTree T,ElemType e,int tag){
if(T)
if(T->data!=e&&tag==0){
T->lchild=PreOrderDe(T->lchild,e,0);
T->rchild=PreOrderDe(T->rchild,e,0);
}else{
T->lchild=PreOrderDe(T->lchild,e,1);
T->rchild=PreOrderDe(T->rchild,e,1);
free(T);
T=NULL;
}
return T;
运行结果:
利用先序和中序序列,创建一个唯一的二叉树
要删除的结点,既x的值为 ‘B’
采用先序遍历的方式 分别打印输出删除前后的二叉树
二叉树创建完成:
A B D G H C E I F
已经完成删除:
A C E I F
Press any key to continue
完整的代码如下:
#include<stdlib.h>
#include<malloc.h>
#include<stdio.h>
#include<stack>
//typedef int ElemType;
typedef char ElemType;
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild;
struct BiTNode *rchild;
}*BiTree,BiTNode;
//购买结点
BiTNode *Buynode()
{
BiTNode *p=(BiTNode *)malloc(sizeof(BiTNode));
if(NULL==p) exit(1);
// memset(p,0,sizeof(BiTNode));
return p;
}
void *Freenode(BiTNode *ptr)
{
free(ptr);
return 0;
}
BiTNode* CreateTreePI(char ps[],char is[],int n )
{
if(n<=0)
{
return NULL;
}
BiTNode* p=(BiTNode*)malloc(sizeof(BiTNode));
int i=0;
int m;
while(i<n)
{
if(ps[0]==is[i])
{
m=i;
break;
}
++i;
}
if(i>=n)
{
return NULL;
}
p->data=ps[0];
p->lchild=CreateTreePI(ps+1,is,m);
p->rchild=CreateTreePI(ps+m+1,is+m+1,n-m-1);
return p;
}
///显示二叉树,用前序遍历
void PreOrderPrint(BiTNode *T){
BiTNode *p=T;
if(p){
printf(" %c",p->data);
PreOrderPrint(p->lchild);
PreOrderPrint(p->rchild);
}
}
BiTree PreOrderDe(BiTree T,ElemType e,int tag){
//tag作为辅助标志,判断当前结点是否需要删除
if(T)//递归终止条件
if(T->data!=e&&tag==0){//用来判断当前结点值是否为x或者是x的子孙结点
//T不是值为x的结点,且不是x结点的子孙结点
//只进行先序遍历
T->lchild=PreOrderDe(T->lchild,e,0);
T->rchild=PreOrderDe(T->rchild,e,0);
}else{//当前结点为要删除的结点
//T的子孙结点都需要删除,从这里开始,后续传入的tag值都将为1
T->lchild=PreOrderDe(T->lchild,e,1);
T->rchild=PreOrderDe(T->rchild,e,1);
free(T);
T=NULL;
}
return T; //设置这个返回当前结点主要是为了将x的父结点指向x的指针设置为NULL
}
int main()
{
BiTNode *T;
char ps[]="ABDGHCEIF";
char is[]="GDHBAEICF";
ElemType e='B';
int len=strlen(ps);
T=CreateTreePI(ps,is,len);
printf("二叉树创建完成:\n");
PreOrderPrint(T);
printf("\n");
printf("已经完成删除:\n");
T=PreOrderDe(T,e,0);
PreOrderPrint(T);
printf("\n");
}
题外话
这里的生成代码我用的别人,同样的通过先序中序建立二叉树的程序,不知道为什么我的就报错了,很郁闷
希望有大佬能给解答一下!
(代码同样是王道课后习题的答案,不知道为什么上机就报错Process returned -1073741819 (0xC0000005) 的错误了,折腾了好一会儿 也能没找到问题,直接用别人的创建代码了)
//Pre In分别为先序 中序遍历 顺序数组
BiTree PreInCreat(ElemType Pre[],ElemType In[],int l1,int h1,int l2,int h2){
///l1,l2分别为前序,中序遍历的 第一个结点
///h1,h2分别为前序,中序遍历的最后一个结点
int llen,rlen,i;
BiTNode *root=(BiTree)malloc(sizeof(BiTNode));
root->data=Pre[l1];
for(i=l2;In[i]!=root->data;i++) ;//找到中序遍历的根结点 此时i停在根节点上
llen=i-l2;
rlen=h2-i;
if(llen){
root->lchild=PreInCreat(Pre,In,l1+1,l1+llen,l2,l2+llen-1);
}else
root->lchild=NULL;
if(rlen){
root->rchild=PreInCreat(Pre,In,h1-rlen+1,h1,h2-rlen+1,h2);
}else
root->rchild=NULL;
return root;
}