已知二叉树以二叉链表存储,编写算法完成:对于树中每个元素值为x的结点,删去以它为根的子树,并释放相应的空间

【数据结构】已知二叉树以二叉链表存储,编写算法完成:对于树中每个元素值为x的结点,删去以它为根的子树,并释放相应的空间

数据结构习题

王道书上的一个习题,题目如下:

已知二叉树以二叉链表存储,编写算法完成:对于树中每个元素值为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;
}

  • 32
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值