基本數據結構 —— 二叉搜索樹(C++實現)


什么是二叉搜索樹

二叉搜索樹(英語:Binary Search Tree),(又:二叉搜索樹,二叉排序樹)它或者是一棵空樹,或者是具有下列性質的二叉樹:

  • 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
  • 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
  • 它的左、右子樹也分別為二叉搜索樹。

二叉搜索樹如何儲存數值

如圖所示:

所有的節點,都滿足左子樹上的所有節點都比自己的小,而右子樹上的所有節點都比自己大這個條件。

二叉搜索樹的操作

因為二叉搜索樹的性質,二叉搜索樹能夠高效地進行如下操作:

  • 插入一個數值;
  • 查詢是否包含某個數值;
  • 刪除某個數值

如果共有n個元素,那么平均每次操作需要O(logn)的時間。
接下來用C++來實現以上操作。首先定義節點結構體如下:

node* insert(node* p,int x)
{
	if(!p)
	{
		auto q = new node(x);
		return q;
	}
	else
	{
		if(x < p->val) p->lch = insert(p->lch,x);
		else p->rch = insert(p->rch,x);
		return p;
	}
}

插入一個數值

如圖所示:

node* insert(node* p,int x)
{
	if(!p) //空樹
	{
		auto q = new node(x);
		return q;
	}
	else
	{
		if(x < p->val) p->lch = insert(p->lch,x);
		else p->rch = insert(p->rch,x);
		return p;
	}
}

查詢是否包含某個數值

如圖所示:

bool find(node* p,int x)
{
	if(!p) return false;
	if(x == p->val) return true;
	if(x < p->val) return find(p->lch,x);
	else return find(p->rch,x);
} 

刪除某個數值

數值的刪除比起之前提到的操作要稍微麻煩一些。例如,我們要刪除數值15。如果刪除了15所在的節點,那么它的兩個兒子10和17就懸空了。於是,把11提到15所在的位置就可以解決問題。如圖所示:

一般來說,需要根據下面幾種情況分別進行處理:

  • 需要刪除的節點沒有左兒子,那么就把右兒子提上去。
  • 需要刪除的節點的左兒子沒有右兒子,那么就把左兒子提上去。
  • 以上兩種情況都不滿足的話,就把左兒子的子孫中最大的節點提到需要刪除的節點上。
node* remove(node* p,int x)
{
	if(!p) return NULL;
	if(x < p->val) p->lch = remove(p->lch,x);
	else if(x > p->val) p->rch = remove(p->rch,x);
	else
	{
		if(p->lch == NULL) //需要刪除的節點沒有左兒子 
		{
			auto q = p->rch;
			delete p;
			return q;
		}
		else if(p->lch->rch == NULL) //需要刪除的節點的左兒子沒有右兒子 
		{
			auto q = p->lch;
			q->rch = p->rch;
			delete p;
			return q; 
		}
		else
		{
			auto q = p->lch;
			while(q->rch->rch != NULL) q = q->rch;
			auto r = q->rch;
			q->rch = r->lch;
			r->lch = p->lch;
			r->rch = p->rch;
			delete p;
			return r;
		}
		return p;
	}
}

測試代碼

#include<iostream>
#include<algorithm>
#include<queue>

using namespace std;

struct node{
	int val;
	node *lch,*rch;
	node(int value): val(value),lch(NULL),rch(NULL){ }
}; 
node* insert(node* p,int x)
{
	if(!p)
	{
		auto q = new node(x);
		return q;
	}
	else
	{
		if(x < p->val) p->lch = insert(p->lch,x);
		else p->rch = insert(p->rch,x);
		return p;
	}
}
bool find(node* p,int x)
{
	if(!p) return false;
	if(x == p->val) return true;
	if(x < p->val) return find(p->lch,x);
	else return find(p->rch,x);
} 
node* remove(node* p,int x)
{
	if(!p) return NULL;
	if(x < p->val) p->lch = remove(p->lch,x);
	else if(x > p->val) p->rch = remove(p->rch,x);
	else
	{
		if(p->lch == NULL) //需要刪除的節點沒有左兒子 
		{
			auto q = p->rch;
			delete p;
			return q;
		}
		else if(p->lch->rch == NULL) //需要刪除的節點的左兒子沒有右兒子 
		{
			auto q = p->lch;
			q->rch = p->rch;
			delete p;
			return q; 
		}
		else
		{
			auto q = p->lch;
			while(q->rch->rch != NULL) q = q->rch;
			auto r = q->rch;
			q->rch = r->lch;
			r->lch = p->lch;
			r->rch = p->rch;
			delete p;
			return r;
		}
		return p;
	}
}
void printTree(node* root)
{
	queue<node*> q;
	q.push(root);
	while(!q.empty())
	{
		auto p = q.front();q.pop();
		if(p)
		{
			cout << p->val << " ";
			q.push(p->lch);
			q.push(p->rch);
		} 
	}
	cout << endl;
}
int main() {
	node* root = insert(NULL,7);
	insert(root,2);
	insert(root,15);
	insert(root,1);
	insert(root,5);
	insert(root,10);
	insert(root,17);
	insert(root,4);
	insert(root,6);
	insert(root,8);
	insert(root,11);
	insert(root,16);
	insert(root,19);
	if(find(root,15)) cout << "find 15" << endl;
	else cout << "can not find 15" << endl;
	if(find(root,3)) cout << "find 3" << endl;
	else cout << "can not find 3" << endl;
	printTree(root);
	remove(root,15);
	printTree(root);
	return 0;
};

結果:

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM