算法导论--二叉搜索树

二叉搜索树

   每个节点除关键字外,还包含属性left,right,p,分别指向其左孩子,右孩子,和双亲

且对任何节点x,其左子树的最大关键字不超过x.key,其右子树中的关键字最小不低于x.key。

本程序中的结点定义:

typedef struct Node
{
  struct Node * p;
  struct Node * left;
  struct Node * right;
  int key;
  
}Node;


中序遍历

  中序递归遍历二叉搜索树,能够按序(小到大)输出二叉树中的所有关键字,根的关键字位于左右孩子关键字之间。

  中序遍历耗费的是一个线性的时间。

void Inorder_Tree_Walk(Node * T)  //中序遍历树T,输出
{
	if (T != NULL)
	{
           Inorder_Tree_Walk(T->left);   //递归其左孩子
	   printf("%d\n",T->key);        //输出根的关键字
	   Inorder_Tree_Walk(T->right);  //递归其右孩子
	}
}

查询

 查询的几个操作都能在O(h)内完成,h为二叉树的高度。

查找指定元素

  在二叉树中查找指定元素,若存在,返回其结点地址,若不存在,返回空;   T为树根  
Node * Tree_Search(Node *T ,int k)  //寻找数k是否在T树中,且返回数k的结点地址
{	
    while(T !=NULL && T->key != k)  //按大小遍历树
	{
		if ( k < T->key)    //若小于其T的关键字,则遍历其左孩子
			T=T->left;
		else
	        T=T->right;
	}
	if ( T == NULL)            //没找到数k,返回空
	{     
		return NULL;
	}
	
   else 
	{
		return T;
	}
	
}

查找最大/小关键字元素

 查找二叉树中最大或最小的元素,按照二叉树的定义:
最大元素一定是沿着right孩子指针不断向下查找,直到遇到第一个NULL;
同理最小元素一定是沿着left孩子指针不断向下查找,直到遇到第一个NULL;
Node * Tree_Minimum(Node * T)  //给定树根T,查找树的最小元素结点,并返回其地址
{
	while(T->left != NULL)
		T=T->left;
	return T;
}
int Tree_Maximum(Node * T)   //给定树根T,查找树的最大元素结点,并返回其值
{
	while(T->right != NULL)
		T=T->right;
	return T->key;
}

查找指定元素的后继和前驱

1.后继:即查找指点元素按序的下一个结点位置,即下一个比它大的数。
  按照搜索二叉树的定义,若其存在右孩子,则它的下一个结点一定在其右孩子的子树中最小的一个,即查找Tree_Minimum(x->right);若不存在右孩子,则位于它的一个祖先结点上,且这个祖先一定是最底层有左孩子的祖先,且这个左孩子也是它的一个祖先...(好晦涩) 。可以这样简单理解,要查x的后继,既然它不存在右孩子,那么说明它是这个子树的最大数,那么它的后继一定要往祖先上找,那就去往上层层找祖先,这个祖先必须满足有左孩子,且这个x必须也要位于祖先的左子树中。这样x就为祖先的左子树中最大的一个了。
int Tree_Successor(Node * T,int t)  //找数t的后继,T为树根 
{
	Node * x=Tree_Search(T,t);  //找到元素t的位置
	Node * y=NULL;
	if ( x->right != NULL)
	{
		y=Tree_Minimum(x->right);
		return y->key;
	}
	y = x->p;
	while( y!=NULL && x==y->right)
	{
       x=y;
	   y=y->p;
	}
	return y->key;
    
}
2.前驱:同上去理解。
int Tree_Predecessor(Node * T,int t)  //找数t的前驱,T为树根 
{
	Node * x=Tree_Search(T,t);    //找到元素t的位置
	Node * y=NULL;
	if ( x->left!= NULL)
	{
		return Tree_Maximum(x->left);
	}
	y = x->p;
	while( y!=NULL && x==y->left)
	{
		x=y;
		y=y->p;
	}
	return y->key;
}

插入结点

 即向一个二叉搜索树中插入一个x结点,只需要不断比较x->key与当前结点z->key的大小,若小于,则肯定向z的左子树插入,否则向z的右子树插入,循环比较,直到遇到当前结点的左/右子树为空为止。
Node *Tree_Insert(Node *Root,Node * z)   //二叉搜索树插入,返回树的根
{
   Node * y=NULL;
   Node * x=Root;
   while( x != NULL)                 
   {
	   y=x;
	   if (z->key < x->key)
		   x = x->left;
	   else
		   x = x->right;
   }
   z->p = y;
   if ( y == NULL)    //建立第一个根节点
   {   
	   Root = z;
   }
   else if (z->key < y->key)
   {
	   y->left = z;
   }
   else 
   {
	   y->right = z;
   }
   return Root; 
}

删除结点

删除的情况稍复杂,因为要保证二叉搜索树的性质:

没有孩子节点

若要删除的结点z没有孩子节点,则可以直接删除z结点,只要令其z->p->left\right = NULL即可;

只有一个孩子节点

若结点z只有一个孩子,无论是其左孩子还是右孩子,直接替代z结点位置即可;
举个例子:假如z只有一个右孩子,且z是其双亲的左孩子:
z->p->left = z->right;
z->right->p = z->p   ;

有两个孩子节点

找出要删除结点z的后继结点y,这个结点y一定位于结点z的右子树中且是z的右子树中最小的一个元素,所以结点y一定没有左孩子。
分两种情况:
1.结点y是结点z的右孩子:这时用y替换z的位置,结点y之前的右子树还是它的右子树,结点z之前的左子树作为y的左子树。

2.结点y不是结点x的右孩子:先用y的右子树x替换y位置,接着用y替换z的位置。


例程

#include <STDIO.H>
#include <STDLIB.H>
typedef struct Node
{
  struct Node * p;
  struct Node * left;
  struct Node * right;
  int key;
  
}Node;
Node *Tree_Insert(Node *Root,Node * z)   //二叉搜索树插入,返回树的根
{
   Node * y=NULL;
   Node * x=Root;
   while( x != NULL)                 
   {
	   y=x;
	   if (z->key < x->key)
		   x = x->left;
	   else
		   x = x->right;
   }
   z->p = y;
   if ( y == NULL)    //建立第一个根节点
   {   
	   Root = z;
   }
   else if (z->key < y->key)
   {
	   y->left = z;
   }
   else 
   {
	   y->right = z;
   }
   return Root; 
}
Node * Establish_Tree(int *A,int Length) //建立一个节点数为Length的二叉搜索树
{
	int i;
	Node * node,*Root=NULL;
	for (i=0;i<Length;i++)
	{
		node = (Node *)malloc(sizeof(Node));
		node->key=A[i];
		node->p=NULL;
		node->left=NULL;
		node->right=NULL;
		Root=Tree_Insert(Root,node);        //调用插入函数建立树
	}
  return Root;
}
void Inorder_Tree_Walk(Node * T)  //中序遍历树T,输出
{
	if (T != NULL)
	{
       Inorder_Tree_Walk(T->left);
	   printf("%d\n",T->key);
	   Inorder_Tree_Walk(T->right);
	}
}
Node * Tree_Minimum(Node * T)
{
	while(T->left != NULL)
		T=T->left;
	return T;
}
int Tree_Maximum(Node * T)
{
	while(T->right != NULL)
		T=T->right;
	return T->key;
}
Node * Tree_Search(Node *T ,int k)  //寻找数k是否在树中,且返回数k的地址
{	
    while(T !=NULL && T->key != k)
	{
		if ( k < T->key)
			T=T->left;
		else
	        T=T->right;
	}
	if ( T == NULL)
	{     
		return NULL;
	}
	
   else 
	{
		return T;
	}
	
}

int Tree_Successor(Node * T,int t)  //找数t的后继,T为树根 
{
	Node * x=Tree_Search(T,t);
	Node * y=NULL;
	if ( x->right != NULL)
	{
		y=Tree_Minimum(x->right);
		return y->key;
	}
	y = x->p;
	while( y!=NULL && x==y->right)
	{
       x=y;
	   y=y->p;
	}
	return y->key;
    
}
int Tree_Predecessor(Node * T,int t)  //找数t的前驱,T为树根 
{
	Node * x=Tree_Search(T,t);
	Node * y=NULL;
	if ( x->left!= NULL)
	{
		return Tree_Maximum(x->left);
	}
	y = x->p;
	while( y!=NULL && x==y->left)
	{
		x=y;
		y=y->p;
	}
	return y->key;
}
void Transplant(Node **T,Node *u,Node *v)  //用节点v替换节点u
{
	
	if (u->p == NULL)               //若u为根节点,赋值给T
	   *T = v;
	else if (u == u->p->left)
       u->p->left = v;
	else 
	   u->p->right = v;
	if ( v!= NULL)
	   v->p = u->p;	
}
Node* Tree_Delete(Node *T,int x)  //删除元素 x
{
   Node * z=Tree_Search(T,x);
   Node * y=NULL;
   if (z->left == NULL)
      Transplant(&T,z,z->right);
   else if (z->right == NULL)
      Transplant(&T,z,z->left);
   else
   {
      y = Tree_Minimum(z->right);
	  if ( y->p != z)
	  {
         Transplant(&T,y,y->right);
         y->right = z->right;
		 y->right->p = y;
	  }
	  Transplant(&T,z,y);
	  y->left = z->left;
	  y->left->p = y;
   }
   
   return T;
}
int main(void)
{
	int A[]={3,10,2,5,9,7,4,1,6,8};
    int Length = sizeof(A)/sizeof(A[0]);
	Node * T;
	T = Establish_Tree(A,Length);                    //建立二叉搜索树
	Inorder_Tree_Walk(T);                            //中序遍历输出 
	printf("4 Successor is %d\n",Tree_Successor(T,4));//后继函数
    printf("7 Predecessor is %d\n",Tree_Predecessor(T,7));//前驱函数
	printf("_____________________________________________\n");
	T = Tree_Delete(T,3);    //删除节点函数
	Inorder_Tree_Walk(T);	
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值