二叉树节点的插入与删除

1、先定义一个二叉树节点:

template<typename T>
class treenode{
public:
T ele;
treenode<T>*left;
treenode<T>*right;
treenode(){left=NULL;
right=NULL;}
treenode(T ele){
	this->ele=ele;
left=NULL;
right=NULL;}
};
2、再进行二叉树的插入以创建一个二叉树:

若二叉树为空,直接创建根节点保存数据即可;若不为空,当新元素小于父节点时,根据二叉搜索树特点,需要作为父节点左子树,反之作为右子树:

bool insert(T ele){
if(root==NULL) root=new treenode<T>(ele);
else{
treenode<T>*parent=NULL;
treenode<T>*p=root;
while(p!=NULL){
	if(ele<p->ele){parent=p;
	p=p->left;}//新元素小于父节点,则做父节点左孩纸
	else if(ele>p->ele){parent=p;p=p->right;}//新元素大于父节点,则做父节点右边孩纸
	else return false;}
	if(ele<parent->ele)parent->left=new treenode<T>(ele);
	else parent->right=new treenode<T>(ele);
}
size++;
return true;}

3、进行二叉树的删除:

总体思想:分多种情况讨论 

1.被删除节点没有子树的情况,直接删除,并修改对应父节点的指针为空。

 2.对于只有一个子树的情况,考虑将其子树作为其父节点的子树,关于是左还是右,根据被删除的节点确定。 

3.最复杂的是有两个子数的情况,可以考虑两种方法,都是同样的思想:用被删除节点A的左子树的最右节点或者A的右子树的最左节点作为替代A的节点,并修改相应的最左或最右节点的父节点的指针,修改方法类似2 ,不做细致讨论

如果,是空树返回
 否则
   {
      如果,P的左右指针都为空                                    
        {        
  直接删除节点;     
 }
       否则,如果,P的左孩子节点为空,右孩子节点非空
      {
   右孩子节点赋值给p
  }
      否则,如果p的左孩子节点非空,右孩子节点为空
  {
   左孩子节点赋值给p
  }
 
             否则,如果,p的左右孩子都不空
  {
   如果,p的左孩子节点的左右孩子节点为空
     {
    直接把p的右孩子节点赋值给P的左孩子结点;
    并且把p的左孩子节点赋值给p的前驱节点;
     }
    否则,如果,p的左孩子节点右孩子节点为空,左孩子节点非空
            {
     p的右孩子节点赋值给p的左孩子节点的右孩子节点;
     p的左孩子节点赋值给p的前驱结点;
            }
          否则,如果,p的左孩子节点的左孩子节点为空,右孩子节点为非空
            {
     p的左孩子节点赋值给p的左孩子节点的做孩子节点;
     p的右孩子节点赋值给p的做孩子节点的右孩子节点;
     p的左孩子节点的右孩子节点赋值给p的前驱结点;
            }
  }
   }
 递归的删除左右孩子节点

讲解之前我先把查找的代码附上,以为删除过程需要用到这段儿查找的代码:

treenode<T>* search_node(T ele){return search_node(ele,root);}
treenode<T>* search_node(T ele,treenode<T>*root){
	int f=0;treenode<T>*p=root;
	if(root==NULL) return root;
else{while(root!=NULL){
	if(ele==root->ele) {f=1;return root;}
   else if(ele<root->ele) 
	return search_node(ele,root->left);
   else return search_node(ele,root->right);}
if(f==0)return NULL;}}
首先第一种情况: (删除没有子节点的节点)    

  删除没有子节点的节点只需要将被删除节点的父节点指向空即可

treenode<T>*pa=NULL;
treenode<T>*p=root;
if(root==NULL) throw runtime_error("empty");
if(p->left==NULL&&p->right==NULL){
   if(p==root) root=NULL;//p是根节点,删除整棵树
   else if(p==pa->left) pa->left=NULL;//p是左子树,左子树置空
   else pa->right=NULL;}}//p是右子树,右子树置空
第二种情况:(删除只有一个子节点的节点)      

删除有一个子节点的节点,只需要将被删除节点的父节点指向删除节点的子节点即可

//删除的节点只含右节点

//删除的节点只含右节点
if(p->left==NULL&&p->right!=NULL){
	if(p==root) root=root->right;//当前节点是根节点右节点
	else if(p==pa->left) pa->left=p->right;//当前节点是普通父节点的右节点
	else pa->right=p->right;}//当前节点是普通父节点的
//如果要删除的节点只有左节点
if(p->left!=NULL&&p->right==NULL){
	if(p==root) root=root->left;//当前节点是根节点的左节点
	else if(p==pa->left) pa->left=p->left;
	else pa->right=p->left;}

第三种情况:(删除有两个子节点的节点,即左右子树都非空)      

删除有两个子节点的节点,到底谁来替代被删除的节点的位置呢?是左节点,还是右节点,代替以后这个子节点的子节点应该怎么安排?一系列的问题都出来了。。。简便的方法就是要找一个节点代替这个被删除的节点,这就要从二叉搜索树的定义来看。因为二叉搜索树是有序的,我们要找的节点在这棵树上,而且这个节点要比被删除的左节点大,比右节点小。先看看这个已被删除节点的右节点为根的子树的所有节点的值都要比被删除节点大,这是二叉搜索树定义的,但是要在这个集合中找到最小的一个,来代替被删除的节点,那就要在这棵子树上一直往左找。这个节点比被删除的节点的右节点小,且比左节点大,那这个节点就叫做被删除节点的后继节点,用这个节点来代替被删除节点。也就是说用被删除节点A的左子树的最右节点或者A的右子树的最左节点作为替代A的节点,并修改相应的最左或最右节点的父节点的指针。

// 如果要删除的节点有两个子节点,即左右子节点都非空
// 方法是用要删除的节点的后续节点代替要删除的节点,并且删除后续节点(删除后续节点的时候同样的递归操作)
// 其实,这里要用到的最多也就会发生两次,即后续节点不会再继续递归的删除下一个后续节点了
// 因为,要删除的节点的后续节点肯定是 要删除的那个节点的右子树的最小关键字,而这个最小关键字肯定不会有左节点
 // 所以,在删除后续节点的时候肯定不会用到( 两个节点都非空的判断 ),如有有子节点,肯定就是有一个右节点。

if(p->left!=NULL&&p->right!=NULL){
  treenode<T>*parent=p;
  treenode<T>*child=p->left;
  while(child->right!=NULL)
  {
   parent=child;
   child=child->right;
  }//获取后续点
  p->ele=child->ele;
  if(parent->left==child)
   parent->left=child->left;
  else
   parent->right=child->left;
  delete(child);
}
}
综述:

void deleteele(treenode<T>*current){
treenode<T>*pa=getparent(current);
treenode<T>*p=current;
// 第一种情况:删除没有子节点的节点
if(root==NULL) throw runtime_error("empty");
if(p->left==NULL&&p->right==NULL){
   if(p==root) root=NULL;
   else if(p==pa->left) pa->left=NULL;
   else pa->right=NULL;}


//删除的节点只含右节点
if(p->left==NULL&&p->right!=NULL){
	if(p==root) root=root->right;
	else if(p==pa->left) pa->left=p->right;
	else pa->right=p->right;}
//如果要删除的节点只有左节点
if(p->left!=NULL&&p->right==NULL){
	if(p==root) root=root->left;
	else if(p==pa->left) pa->left=p->left;
	else pa->right=p->left;}

 // 如果要删除的节点有两个子节点,即左右子节点都非空
// 方法是用要删除的节点的后续节点代替要删除的节点,并且删除后续节点(删除后续节点的时候同样的递归操作)
// 其实,这里要用到的最多也就会发生两次,即后续节点不会再继续递归的删除下一个后续节点了
// 因为,要删除的节点的后续节点肯定是 要删除的那个节点的右子树的最小关键字,而这个最小关键字肯定不会有左节点
 // 所以,在删除后续节点的时候肯定不会用到( 两个节点都非空的判断 ),如有有子节点,肯定就是有一个右节点。

if(p->left!=NULL&&p->right!=NULL){
  treenode<T>*parent=p;
  treenode<T>*child=p->left;
  while(child->right!=NULL)
  {
   parent=child;
   child=child->right;
  }
  p->ele=child->ele;
  if(parent->left==child)
   parent->left=child->left;
  else
   parent->right=child->left;
  delete(child);
}
}
void deleteele(T ele){
	treenode<T>*p=search_node(ele);
	if(p==NULL) cout<<"no the value:"<<endl;
   else deleteele(p);
}
运行结果:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值