二叉搜索树--基础篇

   二叉搜索树Binary Search Tree(BSTs)又名二叉排序树,二叉查找树。而能够自平衡的叫平衡搜索树,包括AVL trees, 2-3 trees, 2-3-4 trees, B-trees, Red-Black Trees红黑树,skip lists跳跃表。

  在二叉搜索树的基础上,又有最优二叉搜索树

  在此之前先说说三种遍历方式。

 中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树。即:
若二叉树为空则结束返回
否则:
(1)中序遍历左子树。
(2)访问根结点。
(3)中序遍历右子树。



中序遍历
注意的是:遍历左右子树时仍然采用中序遍历方法。
如右图所示二叉树
中序遍历结果:DBEAFC
中序遍历的时间复杂度为:O(n)。
如果一棵二叉排序树的节点值是数值,中序遍历的结果为升序排列的数组。可以利用该性质检测一棵树是否为二叉排序数。
已知前序遍历和后序遍历,不能确定唯一的中序遍历。

void BinTree::preOrder(Node *r)//递归实现先序遍历  
{  
   if(r==NULL)  
    {  
        return ;  
   }  
   else  
   {  
        cout<<r->data<<" ";  
       preOrder(r->left);  
        preOrder(r->right);  
    }  
} 

void BinTree::InOrder(Node *r)//递归实现中序遍历  
{  
   if(r==NULL)  
   {  
       return ;  
  }  
    else  
    {  
        InOrder(r->left);  
        cout<<r->data<<" ";  
        InOrder(r->right);  
    }  
}   
void BinTree::PostOrder(Node *r)//递归实现后序遍历  
{  
    if(r==NULL)  
   {  
        return ;  
    }  
    else  
   {  
        PostOrder(r->left);  
        PostOrder(r->right);  
        cout<<r->data<<" ";  
    }  
}  

一、二叉搜索树的定义

    二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树:

    每个结点都有一个作为搜索依据的关键码(key),所有结点的关键码互不相同

    左子树(如果非空)上 所有结点的关键码都小于根结点的关键码。
    右子树(如果非空)上所有结点的关键码都大于根结点的关键码
    左子树和右子树也是二叉搜索树。(构建的二叉搜索树和输入节点的排序有关,排序不同,构造的二叉树也不同)
 
    如果对一棵二叉搜索树进行中序遍历,可以按从小到大的顺序,将各结点关键码排列起来,所以也称二叉搜索树为二叉排序树。

二、查询操作

    在二叉搜索树中,查询操作时很核心的一个操作,在插入节点的时候,需要查看改节点是否已经存在,如果存在那么插入操作失败。并且在删除节点而不调整二叉树时,使用的策略和步骤是和查询操作一样的。

   查询步骤如下:

    假设想要在二叉搜索树中搜索关键码为 x 的元素,搜索过程从根结点开始
    如果 根指针(或者迭代的子树的根)为 NULL ,则 搜索不成功 ;并报告该空节点的父亲节点,即最后停留在的叶子节点。
    否则用给定值 x 与根结点的关键码进行比较:
          给定值等于根结点关键码 ,则 搜索成功 ,返回搜索成功; 并报告搜索到结点地址
         若给定值小于根结点的关键码,则继续递归搜索根结点的左子树;
         否则。递归搜索根结点的右子树。

/********************************************************************
Method:      SearchBST
Description: 
Access:      public 
Returns:     int
Parameter:   T: the tree that will be searched, f:the father tree of the
             tree that will be searched       p:point to the elements 
			 that =key, or when result =0; point to the last leaf node
********************************************************************/
void SearchBST(BiTree T,int key,BiTree f,BiTree &p,int& result)
{
	result = 0;
	if(T == NULL)
	{
		p=f;
		result = 0;
	}
	else if(key>T->data)
	{
	    SearchBST(T->rchild,key,T,p,result);
	}
	else if(key<T->data)
	{
		SearchBST(T->lchild,key,T,p,result);
	}
	else if(key == T->data)
	{
		p=T;
		result = 1;
	}
}

使用返回值的版本如下:

int SearchBST(BiTree T,int key,BiTree f,BiTree &p)
{
	int tmp1,tmp2;
	tmp1=tmp2=0;
	if(!T) {p=f;return 0;} //查找不成功
	else if(key==T->data) {p=T;return 1;} //查找成功
	else if(key<T->data) tmp1=SearchBST(T->lchild,key,T,p); //在左子树中继续查找
	else tmp2=SearchBST(T->rchild,key,T,p); //在右子树中继续查找
	if(tmp1||tmp2) return 1; //若在子树中查找成功,向上级返回1
	else return 0; //否则返回0*/
}



三、插入操作

    每次结点的插入,都要先进行搜索操作,(搜索不成功;报告该空节点的父亲节点,最后停留在的叶子节点)。然后把新结点作为改叶结点的子节点插入。

int InsertBST(BiTree &T,int key)
{
	BiTree p,s;
	int result = 0;
	SearchBST(T,key,NULL,p,result);
	if(result == 0) //查找不成功,插入
	{
		s = new BiTNode;
		s->data=key;
		s->lchild=s->rchild=NULL;
		if(p == NULL) T=s; //被插结点*s为新的根结点
		else if(key<p->data) p->lchild=s; //被插结点*s为左孩子
		else p->rchild=s; //被插结点*s为右孩子
		return 1; //成功插入
	}
	else return 0; //树中已存在关键字为e的数据元素
}

 四、删除操作

    在二叉搜索树中删除一个结点时,必须将因删除结点而断开的二叉链表重新链接起来,同时确保二叉搜索树的性质不会失去。
    为保证在删除后树的搜索性能不至于降低,还需要防止重新链接后树的高度增加。
    删除叶结点 ,只需将其双亲结点指向它的指针清零,再释放它即可。如a所示
    被删结点右子树为空 ,可以拿它的左子女结点顶替它的位置,再释放它。
    被删结点左子树为空 ,可以拿它的右子女结点 顶替它的位置,再释放它。如b所示
   被删结点左、右子树都不为空 ,可以在它的右子树中寻找中序下的第一个结点 ( 关键码最小 ), 用它的值填补到被删结点中,再来处理这个结点的删除问题。(这个点需要一个比他大的最少的数)


在B中,16的左子树空,16用他的右子树填补


在C中,5的左右子树都不为空,采取找到右子树最小值(中序查找到的第一个值),将6填入5,然后6变成了一个hole,递归执行直到最后一个取代的值的左右子树有一个为空,结束。原则是这样子,但是一般都不会使用递归实现。

取代的是,p是5,q从5开始,s是5的右子树,即q一直是s的前驱。然后s向左走到尽头,q一直保存前驱,最后s变为6,q为10, 然后将p上的值存储为6,将6的右子树接入到10的左子树


第一步,做类似查找的操作:

void DeleteBST(BiTree &T,int key, int& result)
{
	result = 0;
	if(T==NULL) 
	{
		result = 0; //要搜索的子树为空
	}
	else
	{
		if(key==T->data)//找到关键字等于key的数据元素并删除它	
		{
			Delete(T); //在二叉排序树中删除结点p,并重接它的左或右子树
			result = 1;
		} 
		else if(key<T->data) //继续在左子树中删除
		{
			DeleteBST(T->lchild,key,result);
		}
		else //继续在右子树中删除
		{
			DeleteBST(T->rchild,key,result); 
		}
	}
}

第二步、实现Delete(T),删除节点p,重接它的 左右子树。

方法一:找到右子树最小的值。

方法二、找到左子树最大的值。

void Delete(BiTree &p)
{//在二叉排序树中删除结点p,并重接它的左或右子树
	BiTree s,q;
	if(p->rchild == NULL) //右子树空,只需重接它的左子树
	{
		cout<<"p value"<<p->data<<endl;
		q=p;
		p=p->lchild;
		free(q);
	}
	else if(p->lchild == NULL) //左子树空,只需重接它的右子树
	{
		cout<<"p2 value"<<p->data<<endl;
		q=p;
		p=p->rchild;
		free(q);
	}
	else //左右子树均不空
	{
		q=p;//q一直是s的前驱,负责保存s游走后的位置
		s=p->rchild;//s不断向左游走,转右,向左走到尽头
		while(s->lchild)
		{
			q=s;s=s->lchild;
		} 
		p->data=s->data; //q的值也被改变
		cout<<"s value"<<s->data<<endl;
		cout<<"q value"<<q->data<<endl;
		cout<<"p value"<<p->data<<endl;
		if(q!=p) q->lchild=s->rchild; 
		else //删除78, 左右为65,94,q没有前进过地址没变,但是值变为了94
		{
			q->rchild=s->lchild; 
		}
		free(s);
	}
}



整个可执行程序的代码如下:

演示的二叉堆为:



</pre><p></p><div style="top: 576px;"><pre name="code" class="cpp">#include <iostream>
using namespace std;
#define Maxsize 100
typedef struct BiTNode //定义二叉树节点结构
{
	int data; //结点的数据域
    struct BiTNode *lchild,*rchild; //左右孩子指针域
}BiTNode,*BiTree;
BiTNode *CreatBST(int A[],int n);
void SearchBST(BiTree,int,BiTree,BiTree&,int& result); //在二叉排序树中查找元素
int InsertBST(BiTree &,int); //在二叉排序树中插入元素 
int DeleteBST(BiTree &,int); //在二叉排序树中删除元素
void Delete(BiTree &); //删除二叉排序树的根结点
void InorderBST(BiTree); //中序遍历二叉排序树,并显示
void inorder(BiTree T);
int A[Maxsize];

BiTNode *CreatBST(int A[],int n)   //由数组A中的关键字建立一棵二叉排序树 
{
	BiTNode *bt=NULL;                  //初始bt 为空树
	int i=0;
	while(i<n)
		if(InsertBST(bt,A[i])==1)      //将数组A[i]插入二叉排序树bt中
		{
			printf("  Step%d,Insert:%d:",i+1,A[i]);
			InorderBST(bt);
			printf("\n");
			i++;
		}
		return bt;                      //返回建立的二叉排序树的根指针
}

void InorderBST(BiTree T)
{//以中序方式遍历二叉排序树T,并显示
	if(T!=NULL)
	{
		printf("%d",T->data);
		if(T->lchild!=NULL||T->rchild!=NULL)
		{
			printf("(");
			InorderBST(T->lchild);//递归调用中序遍历函数
			if(T->rchild!=NULL)
				printf(",");
			InorderBST(T->rchild); //递归调用中序遍历函数
			printf(")");
		}
	}

}
void inorder(BiTree T)
{
	if(T!=NULL)
	{
		inorder(T->lchild);
		cout<<" "<<T->data;
		inorder(T->rchild);
	}
}

/********************************************************************
Method:      SearchBST
Description: 
Access:      public 
Returns:     int
Parameter:   T: the tree that will be searched, f:the father tree of the
             tree that will be searched       p:point to the elements 
			 that =key, or when result =0; point to the last leaf node
********************************************************************/
void SearchBST(BiTree T,int key,BiTree f,BiTree &p,int& result)
{
	result = 0;
	if(T == NULL)
	{
		p=f;
		result = 0;
	}
	else if(key>T->data)
	{
	    SearchBST(T->rchild,key,T,p,result);
	}
	else if(key<T->data)
	{
		SearchBST(T->lchild,key,T,p,result);
	}
	else if(key == T->data)
	{
		p=T;
		result = 1;
	}
}

int InsertBST(BiTree &T,int key)
{
	BiTree p,s;
	int result = 0;
	SearchBST(T,key,NULL,p,result);
	if(result == 0) //查找不成功,插入
	{
		s = new BiTNode;
		s->data=key;
		s->lchild=s->rchild=NULL;
		if(p == NULL) T=s; //被插结点*s为新的根结点
		else if(key<p->data) p->lchild=s; //被插结点*s为左孩子
		else p->rchild=s; //被插结点*s为右孩子
		return 1; //成功插入
	}
	else return 0; //树中已存在关键字为e的数据元素
}

int DeleteBST(BiTree &T,int key)
{//若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素结点
//并返回1,否则返回0
	int tmp1,tmp2;
	tmp1=tmp2=0;
	if(!T) return 0; //不存在关键字等于key的数据元素
	else
	{
		if(key==T->data)	{Delete(T); return 1;} //找到关键字等于key的数据元素并删除它
		else if(key<T->data) tmp1=DeleteBST(T->lchild,key); //继续在左子树中删除
		else tmp2=DeleteBST(T->rchild,key); //继续在右子树中删除
		if(tmp1||tmp2) return 1; //在子树中删除成功,返回1
		else return 0; //不存在该元素,返回0
	}
}

void Delete(BiTree &p)
{//在二叉排序树中删除结点p,并重接它的左或右子树
	BiTree s,q;
	if(p->rchild == NULL) //右子树空,只需重接它的左子树
	{
		cout<<"p value"<<p->data<<endl;
		q=p;
		p=p->lchild;
		free(q);
	}
	else if(p->lchild == NULL) //左子树空,只需重接它的右子树
	{
		cout<<"p2 value"<<p->data<<endl;
		q=p;
		p=p->rchild;
		free(q);
	}
	else //左右子树均不空
	{
		q=p;//q一直是s的前驱,负责保存s游走后的位置
		s=p->rchild;//s不断向左游走,转右,向左走到尽头
		while(s->lchild)
		{
			q=s;s=s->lchild;
		} 
		p->data=s->data; //q的值也被改变
		cout<<"s value"<<s->data<<endl;
		cout<<"q value"<<q->data<<endl;
		cout<<"p value"<<p->data<<endl;
		if(q!=p) q->lchild=s->rchild; 
		else //删除78, 左右为65,94,q没有前进过地址没变,但是值变为了94
		{
			q->rchild=s->lchild; 
		}
		free(s);
	}
}

void main()
{
	BiTree T,p;
    int ch,keyword;
    char j='y';//控制程序结束与否
	int temp; 

   printf("Creat the BST!\n");
   const int n = 10;
   int A[n]={53,17,78,9,45,65,94,23,81,88};
    T=CreatBST(A,n);//调用建树函数建树
	inorder(T);
	while(j!='n')
	{
	  printf("1.display\n");
      printf("2.search\n");
      printf("3.insert\n");
      printf("4.delete\n");
      printf("5.exit\n");
      scanf(" %d",&ch); //输入操作选项
      switch(ch)
	  { case 1:
	           if(!T) printf("The BST has no elem.\n");
               else {InorderBST(T);printf("\n");}//中序遍历并显示
               break;
       case 2:
		   printf("Input the keyword of elem to be searched(a number):");
           scanf(" %d",&keyword); //输入要查找元素的关键字
           SearchBST(T,keyword,NULL,p,temp);//查找
           if(!temp) printf("%d isn't existed!\n",keyword); //没有找到
           else printf("%d has been found!\n",keyword); //成功找到
           break;
	   case 3:
		   printf("Input the keyword of elem to be inserted(a number):");
		   scanf(" %d",&keyword); //输入要插入元素的关键字
		   temp=InsertBST(T,keyword);//插入
		   if(!temp) printf("%d has been existed!\n",keyword); //该元素已经存在
		   else printf("Sucess to inert %d!\n",keyword); //成功插入
		   break;
	   case 4:
		   printf("Input the keyword of elem to be deleted(a number):");
		   scanf(" %d",&keyword); //输入要删除元素的关键字
		   temp=DeleteBST(T,keyword);//删除
		   InorderBST(T);
		   if(!temp) printf("%d isn't existed!\n",keyword); //该元素不存在
		   else printf("Sucess to delete %d\n",keyword); //成功删除
		   break;
	   case 5: j='n';//跳出循环,结束程序
	  }
	}
	printf("The program is over!\nPress any key to shut off the window!\n");
	getchar();getchar();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值