数据结构之查找(三)--二叉排序树(BST)的查找、插入和删除

    之前讲了二叉排序树是如何建立的,今天来聊一聊二叉排序树的查找、插入和删除。

一、二叉排序树的查找

        结合之前所讲的二叉排序树建立,查找操作只需要从根节点开始判断与key值的大小关系,key值大,这沿着右孩子找,反之沿着左孩子找。

实现代码:

int searchBST(tNode *tree,int key)
{
	if( (*tree) == NULL )
	{
		return 0;
	}

	else if( (*tree)->data == key )
	{
		return 1;                                

	}

	else if( (*tree)->data < key )
		searchBST(&((*tree)->rchild), key);
	
	else
		searchBST(&((*tree)->lchild), key);
}

二、二叉排序树的插入

    在之前讲解二叉排序树创建一节就讲了二叉排序树的插入,在此不重复。(二叉排序树的建立

三、二叉排序树的删除

    俗话说得好:“请神容易送神难”,删除算法就比较复杂了,得分如下几种情况考虑:

1、删除叶结点(直接删掉就可以了)

 

2、删除只有左孩子或只有右孩子的结点(相对简单:直接将结点去掉,然后将他的孩子连接到原来的父结点上去)

                        

3、删除既有左孩子又有右孩子的结点(相对比较复杂)


处理这种情况的最好方法是在此结点的所有孩子中找到这个结点的直接前驱(或者直接后继),用前驱(或者后继)来代替此结点:


代码实现:

在代码中*fathertree来指向要删除结点的父结点(此代码思想是知道父结点才能知道在删除结点后子结点连接到那儿),当然还有一种简单的方法,不需要知道父结点,直接用子结点代替要删除的结点。

另一种不需要知道父结点的方法

 if(NULL == (*tree)->lchild)
{
       q = *tree; *p = (*tree)->rchild; q = NULL; 
}
		
//情况2,删除只有一个左孩子
else
{
	q = *tree; *tree = (*tree)->lchild; q = NULL;
}

注意:在情况3中将直接前驱(或直接后继)插入到要删除节点的位置后要在调用一次deletenode函数删除直接前驱(或直接后继

#include <stdio.h>
#include <malloc.h>

#include <iostream>
using namespace std;
typedef struct treeNode node;

typedef struct treeNode
{
	int data;
	
	node *rchild, *lchild;
	
}node, *tNode;

void insertChild(tNode *tree, int c);

void creatBST(tNode *tree);

int searchBST(tNode *tree,int key);

tNode findPrecursor(tNode *tree, tNode *fatherTree);

 //要删除一个结点,先要找到这个结点的位置
void deleteNode(tNode *tree, tNode *fathertree, int position)  
{
	//定义一个临时的结点来减少代码的冗余
	tNode T ;
//情况1和情况2
	if( (NULL == (*tree)->lchild) || (NULL == ((*tree)->rchild)) )
	{
		//情况1,删除叶结点
		if( (NULL == (*tree)->lchild) && (NULL == ((*tree)->rchild)) )
		{
			T = NULL;	
		}
		
		//情况2,删除只有一个右孩子
		else if(NULL == (*tree)->lchild)
		{
			T = (*tree)->rchild;
		}
		
		//情况2,删除只有一个左孩子
		else
		{
			T = (*tree)->lchild;
		}
		
		//要删除的结点在父结点的什么位置
		if(0 == position)  //表示删除根结点
		{
			*fathertree = T;
		}
		else if(1 == position)  //表示删除的是左孩子
		{
			(*fathertree)->lchild = T;
		}
		else  //表示删除的是右孩子
		{
			(*fathertree)->rchild = T;	
		}		
	}
		
//情况3,要删除的结点既有左孩子又有右孩子
	if( (NULL != (*tree)->lchild) && (NULL != ((*tree)->rchild)) )
	{
		T = (*tree)->lchild;
		tNode a ;
		//先找到此结点的直接前驱 (或直接后继),代码以直接前驱为例
		a = findPrecursor(&T, tree);  //规定a->lchild指向结点的直接前驱,a->rchild指向前驱的父结点
		
		(*tree)->data = (a->lchild)->data; 
		if(NULL == T->rchild )
			deleteNode(&(a->lchild), &(a->rchild), 1);
		else
			deleteNode(&(a->lchild), &(a->rchild), 2);
	}
	
}

tNode findPrecursor(tNode *tree, tNode *fatherTree)
{
	if(NULL == (*tree)->rchild)
	{
		tNode a = (tNode)malloc(sizeof(node));
		a->lchild = *tree;
		a->rchild = *fatherTree;
		return a;
	}
	else
	{
		findPrecursor(&((*tree)->rchild), tree);
	}
}


//要删除一个结点,先要找到这个结点的位置
//规定fathertree为父结点,根结点的父结点为其本身,position为结点在父结点中的位置(即使1表示左孩子,2表示右孩子),根结点的position为0
int deleteBST(tNode *tree, tNode *fathertree, int position, int key)
{
	if( (*tree) == NULL )
	{
		return 0;
	}

	else if( (*tree)->data == key )
	{
		//在这儿加入删除算法
		deleteNode(tree,fathertree, position);
		
		return 1;                                

	}

	else if( (*tree)->data < key )
		deleteBST(&((*tree)->rchild), tree, 2, key);
	
	else
		deleteBST(&((*tree)->lchild), tree, 1, key);
}


void creatBST(tNode *tree)   
{
	*tree = (tNode)malloc(sizeof(node));  //这已经给他的左右孩子分配了内存地址,所以要将他的左右孩子只想NULL
	(*tree)->lchild = NULL;
	(*tree)->rchild = NULL;
	
	int c ;
	scanf("%d",&c);
	
	if((*tree) != NULL && 65535 != c)   //以65535为结束标志
		(*tree)->data = c;   //创建根节点
		

	while(65535 != c)
	{
		scanf("%d",&c);
		if((*tree) != NULL && 65535 != c)	
			insertChild(tree, c);
	}

}

//写一个函数专门插入子孩子,采用递归,从根节点开始寻找合适的插入点
void insertChild(tNode *tree, int c)
{
	if(c < (*tree)->data)  
	{
		if( (*tree)->lchild == NULL )   //如果没有左孩子,直接将值赋给左孩子
		{
			(*tree)->lchild = (tNode)malloc(sizeof(node));  //这已经给他的左右孩子分配了内存地址,所以要将他的左右孩子只想NULL
			((*tree)->lchild)->lchild = NULL;
			((*tree)->lchild)->rchild = NULL;

			(*tree)->lchild->data = c;
		}
			
	
		else
		{
			insertChild(&((*tree)->lchild),c);		
		}
	}
	
	else
	{
		if( (*tree)->rchild == NULL )   //如果没有左孩子,直接将值赋给左孩子
		{
			(*tree)->rchild = (tNode)malloc(sizeof(node));   //这已经给他的左右孩子分配了内存地址,所以要将他的左右孩子只想NULL
			((*tree)->rchild)->lchild = NULL;
			((*tree)->rchild)->rchild = NULL;

			(*tree)->rchild->data = c;
		}
			
			
		else
		{
			insertChild(&((*tree)->rchild),c);		
		}
	}
}

void PreOrderTraverse(tNode T)   /*前序遍历算法*/
{
	if( NULL == T )
		return;
	printf("%d-->", T->data);	      //先显示根结点
	PreOrderTraverse(T->lchild);   //再遍历左子树
	PreOrderTraverse(T->rchild);   //最后遍历右子树
}

int main()
{
	tNode tree = NULL;

	creatBST(&tree);
	PreOrderTraverse(tree);

	printf("\n\n");

	int deleteData = 1;

	while(deleteData != 65535)
	{
		scanf("%d",&deleteData);

		deleteBST(&tree, &tree, 0, deleteData);
		PreOrderTraverse(tree);

		printf("\n\n");
	}

	system("pause");
	return 0;
}

运行结果:

猜你喜欢

转载自blog.csdn.net/qq_31820761/article/details/80671234