二叉树的插入与删除

本文介绍了二叉树的插入、删除操作,包括判断空树、寻找插入位置、删除节点的处理。对于删除操作,详细讨论了叶子节点、单支节点和双支节点的情况。此外,还探讨了非递归和递归的中序遍历二叉树的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序规则:定义一个vector数组,将数组中的第一个值作为根节点,此后将后面的值与根节点的值比较,如果小于根节点的值,则放在根节点的左边,如果大于根节点的值,放在根的右边。以此类推,根节点的左子树(右子树)又是一棵二叉树。此外,在二叉树排序中不允许出现两个相同的值。当二叉树完成之后,可以用中序遍历的方式将它从小到达排序。

在这里插入图片描述
在这里插入图片描述
定义结构体BstNode和BSTree分别定义了它们的属性和根节点,这里会增加一个双亲指针,它可以让我们的子节点指向它的父节点,但是这样也会降低它的存储密度。即原来12个字节中可以存储4个字节的有效数据,现在需要16个字节的空间来存放4个字节的有效数据。

typedef int ElemType;
typedef struct BstNode{
	BstNode* left;//左节点
	BstNode* parent;//双亲
	BstNode* right;//右节点
	ElemType data;//数据域
}BtNode;
typedef struct{
	BstNode* root;//根节点
	int cursize;//节点个数
};

插入元素
思想1:首先判断我们要插入的这颗二叉树是否为空树,如果为空树,我们就需要为其购买一个节点,然后将val值赋值给它。这时这个val值就是它的根节点。定义两个指针,用来指向树的根节点和双亲。**注意指向根节点的指针一定要在确认不为空树的情况下定义。**当根节点的数据域不等于val值时,让双亲指针指向根节点。然后将val值判断,如果val大于根节点指向的数据,就放在右子树,否则放在左子树。这是一个循环,目的是让指针指向空,也就是val的藏身之所。找到val要存放的位置后,让它和它的双亲指针比较大小来做最后的定夺。

bool Insert_BSTree(BSTree& myt,ElemType val)
//这里要对数组进行插入,不会改变这个元素,所以对它进行引用
{
	if(myt.root==NULL)
	{
		myt.root=buynode();
		myt.root->data=val;
		myt.cursize+=1;
		return true;
	}
	BstNode* p=myt.root;
	BstNode* pa=NULL;
	while(p!=NULL&&p->data!=val)//这是一个循环,目的是找到val的双亲
	{
		pa=p;
		p=val<p->data?p->left:p->right;
	}
	if(p!=NULL&&p->data==val)//找到了val值,这在二叉树中是不允许的,直接return false
	{
		return false;
	}
	p=buynode();
	p->data=val;
	p->parent=pa;
	if(p->data<pa->data)
	{
		pa->left=p;
	}
	else
	{
		pa->right=p;
	}
	myt.cursize+=1;
	return true;
}

思想2:刚进入函数时先不用判空,定义p和pa,其中p指向根节点。然后让p根据val值往后跑,找到它的双亲节点。如果找到val值,直接return。否则购买一个节点让p指向pa,这里需要做一个判断,如果pa为空,说明这是一个空树,此时我们让刚申请好的节点作为根节点,如果这不是一个空树,进行判断。

bool Insert_BSTree(BSTree& myt,ElemType val)
{
	BstNode* p=myt.root;
	BstNode* pa=NULL;
	while(p!=NULL&&p->data!=val)
	{
		pa=p;
		p=val<p->data?p->left:p->right;
	}
	if(p!=NULL&&p->data==val)
	{
		return false;
	}
	p=buynode();
	p->data=val;
	p->parent=pa;
	if(pa==NULL)
	{
		myt.root=p;
	}
	else{
		if(p->data<pa->data)
		{
			pa->left=p;
		}
		else{
			pa->right=p;
		}
	}
	myt.cursize+=1;
	return true;
}

二叉树的删除
思想:首先如果我们要删除的是一个空树,那就直接return。如果不是一个空树,我们首先要去寻找这个要删除的值。这里分三种情况:
删除的是叶子节点
删除的是单支节点
删除的是双支节点

如果我们要删除的是一个双分支节点,那么我们首先要找到这个节点的下一个节点(中序遍历),找到这个节点后,将这个节点覆盖掉我们要删除的节点
如果是单分支节点或者叶子节点,首先要找到它的双亲节点和它的一个子节点,我们首先判断它的子节点是否为空,如果不为空,那么这是一个单分支节点,让子节点指向父节点,否则这是一个叶子节点,判断父节点的左孩子是否等于要删除的节点,如果是,让父节点的左边指向空,否则父节点的右边指向空,然后将这个节点释放掉。

bool ReMove_Value(BSTree& myt,ElemType val)
{
	if(myt.root==NULL) return false;
	BstNode*p=FindValue(myt,val)//查找要删除的val值
	if(p==NULL) return false//没有找到,直接return
	if(p->left!=NULL&&p->right!=NULL)//如果命中,说明这是一个双分支节点
	{
		BstNode* tmp=Next(p->right)//找到它的下一个节点
		p->data=tmp->data;//覆盖
		p=tmp;
	}
	BstNode* pa=p->parent;//定义父指针
	BstNode* child=p->left!=NULL?p->left:p->right;//找到它唯一的子节点
	if(child!=NULL) child->parent=pa;//如果命中,说明这是一个单分支节点,让child直接指向pa
	if(pa==NULL)//如果p的双亲节点为空
	{
		myt.root=p;//那么这个p指针就是当前的根节点
	}
	if(pa->left==p)
	{
		pa->left=NULL;
	}
	else{
		pa->right=NULL;
	}
	FreeNode(p);
	myt.cursize-=1;
	return true;
}

二叉树的双向链表
非递归中序遍历
思想:利用中序遍历的思想,如果ptr不为空,栈不满且ptr->left不为空,压栈。直到ptr为空,说明找到了中序的头节点,(如果head此时为空)让head和nt指针同时指向头节点。否则将栈中元素出栈,此时nt指向的是出栈元素的左孩子,让nt的右孩子指向出栈元素,出栈元素的左孩子指向nt。此时nt和出栈元素相互指向,将ptr赋值给nt,遍历它的右孩子。

BtNode* NiceInOrder(BtNode* ptr)
{
	if(ptr==NULL) return NULL;
	stack<BtNode*> st;
	BtNode* head=NULL;
	BtNode* nt=NULL;
	while(ptr!=NULL||!st.empty())
	{
		while(ptr!=NULL)
		{
		st.push(ptr);
		ptr=ptr->left();
		}
	ptr=st.top();st.pop();
	if(head==NULL)
	{
		head=ptr;
		nt=ptr;
	}
	else{
		nt->right=ptr;
		ptr->left=nt;
		nt=ptr;
	}
	ptr=ptr->right;
	}
	return head;
}

递归中序遍历

void InOrder_DbList(BtNode* ptr,BtNode* pre,BtNode* head)
{
	if(ptr!=NULL)
	{
		InOrder_DbList(ptr->left,pre,head);
		if(pre!=NULL)
		{
			pre->right=ptr;
			ptr->left=pre;
		}
		else{
			head=ptr;
		}
		pre=ptr;
		InOrder_DbList(ptr->right,pre,head);
	}
}
BtNode* InOrder_Lt(BtNode* ptr)
{
	BtNode* pre=NULL;
	BtNode* head=NULL;
	InOrder_DbList(ptr,pre,head);
	return head;
}

非递归逆向中序遍历

BtNode* HNiceInorder(BTNode* ptr)
{
	if(ptr==NULL) return NULL;
	stzck<BtNode*> st;
	BtNode* head=NULL;
	BtNode* nt=NULL;
	while(ptr!=NULL||!st.empty())
	{
		while(ptr!=NULL)
		{
			st.push(ptr);
			ptr=ptr->left;
		}
		ptr=st.top();st.pop();
		if(head==NULL)
		{
			head=ptr;
		}
		else{
			nt=ptr->right;
			head->left=ptr;
			ptr->right=head;
			head=ptr;
		}
		ptr=nt;
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值