二叉排序樹--查詢--插入--刪除操作

首先是二叉排序樹的概念:

  • 二叉排序樹是一個二叉樹,對每一個節點來說,右子樹上的值都比根節點大,左子樹上的值都比根節點小。

那麼在查詢某個值key時,就需要遵循以下的步驟

  • 首先和當前節點的值比較,相等則退出,當前節點的地址就是要查詢的地址。
  • key比當前節點值小,那麼key如果存在的話一定在當前節點的左子樹中,繼續查詢左子樹。
  • key比當前節點值大,那麼key如果存在的話一定在當前節點的右子樹中,繼續查詢右子樹。

非常類似二叉樹遍歷的描述,可以用遞歸來實現,注意設置退出條件:

typedef struct A
{
	int data;
	struct A *lchild;
	struct A *rchild;
}BSTNode;

bool SearchBST(BSTNode* T,int key,BSTNode* pTreePre,BSTNode *&pResult)
{
	if (T==NULL)
	{
		//如果當前查詢的節點是空的,那麼返回當前節點的父節點,方便進行插入操作
		pResult=pTreePre;
		return false;
	}
	if (T->data==key)
	{
		pResult=T;
		return true;
	}
	if (key<T->data)
	{
		//比當前節點小,繼續查詢當前節點的左子樹
		return SearchBST(T->lchild,key,T,pResult);
	}
	if (key>T->data)
	{
		//比當前節點大,繼續查詢當前節點右子樹
		return SearchBST(T->rchild,key,T,pResult);
	}
}
在查詢函數中,除了樹的根節點和要查詢的值key,還傳入了兩個參數,一個當前查詢節點的父節點pTreePre和查詢後用於返回地址的pResult,根節點的父節點設置爲NULL,爲了以後插入方便,pResult在調用時加了引用,這是因爲函數中要修改pResult的值,當然也可以用指針的指針,只是那樣看着就不舒服了。


可以看出來查詢函數在查詢成功的時候,pResult的值就是當前節點的值,那麼在查詢失敗的時候呢,返回的就是最後一次查詢的節點的地址。如果key比這個節點小,這個節點肯定沒有左兒子,如果key比這個節點大,這個節點肯定沒有右兒子,否則查詢函數會繼續查詢下去。那麼如果key是要插入的值呢,直接把key放在這個節點的左兒子或右兒子中就可以了。

代碼:

bool InsertBST(BSTNode* &T,int key)
{
	BSTNode *pFind=NULL;
	BSTNode *pInsert=NULL;

	//SearchBST()操作後,pFind的值就是當最後一次查詢的節點的地址
	if (!SearchBST(T,key,NULL,pFind))
	{
		pInsert=(BSTNode*)malloc(sizeof(BSTNode));
		pInsert->data=key;
		pInsert->lchild=pInsert->rchild=NULL;
		if (!pFind)
			T=pInsert;
		else if (key<pFind->data)
			pFind->lchild=pInsert;
		else if (key>pFind->data)
			pFind->rchild=pInsert;
		return true;
	}
	return false;
}

那麼刪除操作呢,和查詢類似,首先要查詢到當前的節點,然後刪除,所以刪除函數的代碼和查詢時很像的,關鍵是刪除節點比較麻煩,後面再講

代碼:

bool DeleteBST(BSTNode* &T,int key)
{
	if (T==NULL)
		return false;
	if (key==T->data)
		return Delete(T);
	if (key<T->data)
		return DeleteBST(T->lchild,key);
	if (key>T->data)
		return DeleteBST(T->rchild,key);
}

下面講解刪除當前節點的操作,即delete函數的流程。
  • 如果沒有左子樹,那麼直接把當前節點去掉,右子樹根節點代替當前節點
  • 如果沒有右子樹,那麼直接把當前節點去掉,左子樹根節點代替當前節點
  • 如果左右子樹都存在,那麼就要比較麻煩了,可以找到以當前節點爲根節點的樹中,左子樹中最大的點或者右子樹的最小的點來代替當前的節點的值,同時刪除左子樹中最大的點(無右兒子)或者右子樹的最小的點(無左兒子),同時要注意左子樹中最大的點右子樹的最小的點爲子樹根節點的情況。
  • 具體看代碼

bool Delete(BSTNode* &T)
{
	
	BSTNode* p=NULL;
	BSTNode* rLittle=NULL;
	//沒有右子樹,直接將左子樹根節點當作T。
	if (T->rchild==NULL)
	{
		p=T;
		T=T->lchild;
		free(p);
	}
	//沒有左子樹,直接將右子樹根節點當作T。
	else if (T->lchild==NULL)
	{
		p=T;
		T=T->rchild;
		free(p);
	}
	//左右子樹都有,將右子樹中最小的節點代替T,並刪除右子樹中最小的節點(這個最小節點肯定沒有左子樹)
	else
	{
		p=T;
		rLittle=T->rchild;
		while (rLittle->lchild)
		{
			p=rLittle;
			rLittle=rLittle->lchild;
		}
		//找到最小節點,替換
		T->data=rLittle->data;
		//刪除最小節點,要注意最小節點是右子樹根節點的情況。
		if (p!=T)
			p->lchild=rLittle->rchild;
		else
			p->rchild=rLittle->rchild;
		free(rLittle);
	}
	return true;
}

最後是測試函數

int main(void)
{
	BSTNode *T=NULL;
	char a[10]={0};
	int t=0;
	while (1)
	{
		printf("輸入要插入的結點:(n表示結束插入):");
		gets(a);
		if (*a=='n')
		{
			break;
		}
		t=atoi(a);
		InsertBST(T,t);
		printf("當前樹:");
		InTraverseBST(T);
		printf("\n");
	}

	while (1)
	{
		printf("輸入要刪除的結點:(n表示結束刪除):");
		gets(a);
		if (*a=='n')
		{
			break;
		}
		t=atoi(a);
		DeleteBST(T,t);
		printf("當前樹:");
		InTraverseBST(T);
		printf("\n");
	}
	return 0;
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章