七、【數據結構】二叉搜索樹(bst)的解析與實現

本文使用C++實現二叉搜索樹(binary search tree,bst)數據結構。

一、爲什麼要引入二叉搜索樹

通過對向量和列表這兩類線性數據結構的原理分析,我們可以發現向量結構在對靜態查詢操作的支持比較好,有序向量的二分查找能做到在O(logn)的時間,但是對刪除和插入操作在最壞情況下能達到O(n)的時間。而與向量相對立的列表結構能很好地支持動態操作,如刪除和插入只需要O(1)的時間,但是它對於靜態查找操作的支持卻很差,如有序列表的查找在最壞情況下也需要O(n)的時間。所以問題來了:若既要求對象集合的組成可以高效率地動態調整,同時也需要能夠高效率的查找,則向量和列表結構都難以勝任。

若將二分查找策略推廣到類似列表這種動態存儲的數據結構,就能既能保證靜態查找操作的高效性能,也能保證動態增刪操作的高效性能。基於這種思想,將二分查找策略進行抽象和推廣,可以定義並實現二叉搜索樹結構。

二、二叉搜索樹的特點

所謂的二叉搜索樹,處處都滿足順序性:在任一節點r的左(右)子樹中,所有節點均不大於(不小於)r。

如上圖可見,任何一棵二叉樹是二叉搜索樹,當且僅當其中序遍歷序列單調非降。

三、二叉搜索樹的性能

因爲二叉搜索樹的結構融合了二分查找的思想,所以可以通過二分查找策略對指定值進行搜索,對於深度爲h的二叉搜索樹,不難證明在最壞情況下的時間複雜度爲O(h)。然而不幸的是,對於規模爲n的二叉搜索樹,深度在最壞的情況下能達到O(n),比如當樹退化爲一條單鏈時,查找的時間複雜度就和列表結構一樣爲O(n)了。

由上可見,若要控制單次查找在最壞情況下的運行時間,必須從二叉搜索樹的高度入手,其實二叉搜索樹本身沒有多麼實際的作用,它最大的貢獻是將二分查找策略融合到樹結構中,這種樹結構使樹結構的查找操作效率大大提升。事實上,若能解決高度的問題,那麼二叉搜索樹就能真正地使用了,這就是後面要介紹的平衡二叉樹,其具有很多的變種,可解決高度的問題。

四、二叉搜索樹的實現

所以,二叉搜索樹的實現最大的意義是爲各種平衡二叉樹提供模板接口,主要包括查找,插入和刪除操作。這裏通過構造bst類實現二叉搜索樹數據結構,其由binTree類繼承而來,參見https://blog.csdn.net/qq_18108083/article/details/84727888

bst接口列表
操作 功能 對象
search(const T& e) 查找指定元素,返回命中節點,並返回其父親節點 二叉搜索樹
insert(const T& e) 按照二叉搜索樹的結構要求插入指定元素 二叉搜索樹
remove(const T& e) 按照二叉搜索樹的結構要求刪除元素 二叉搜索樹

(1) bst.h

#pragma once
#include"binTree.h"

//二叉搜索樹模板類
template<typename T> class bst :public binTree<T>
{
public:
	binNode<T>* _hot;   //所命中的節點的parent
	binNode<T>* connect34(   //按照3+4結構,聯接3個節點及4顆樹
		binNode<T>*, binNode<T>*, binNode<T>*,
		binNode<T>*, binNode<T>*, binNode<T>*, binNode<T>*
	);
	binNode<T>* rotateAt(binNode<T>* v);  //對x及父親、祖父做統一旋轉調整

public:
	static binNode<T>* & searchIn(binNode<T>* &v, const T& e, binNode<T>* &hot);   //在以v爲根的子樹中查找關鍵碼e,並將命中節點的parent返回給hot
	virtual binNode<T>* & search(const T& e);   //查找
	virtual binNode<T>* insert(const T& e);     //插入
	virtual bool remove(const T& e);            //刪除

	static binNode<T>* removeAt(binNode<T>* &x, binNode<T>* &hot);   //刪除位置x所指的節點(以判斷存在),返回值指向實際被刪除者的接替者,hot指向被實際刪除者的父親
};


template<typename T> binNode<T>* & bst<T>::searchIn(binNode<T>* &v, const T& e, binNode<T>* &hot)
{
	if (!v || (v->data == e))    //若v本身不存在或則直接命中,則返回v
		return v;
	hot = v;
	return searchIn((e < (v->data)) ? v->lc : v->rc, e, hot);
}

template<typename T> binNode<T>* & bst<T>::search(const T& e)
{
	return searchIn(_root, e, _hot = nullptr);
}  //返回時,返回值指向命中節點或假想的哨兵節點,hot指向其parent

template<typename T> binNode<T>* bst<T>::insert(const T& e)
{
	//首先search查詢是否存在或待插入的位置
	binNode<T>* &x = search(e);
	if (x)   //若已經存在則返回
		return x;
	//否則直接插在x上,設置好前後連接關係
	x = new binNode<T>(e, _hot);
	_size++;
	updateHeightAbove(x);
	return x;
}

template<typename T> bool bst<T>::remove(const T& e)
{
	binNode<T>* succ=nullptr;  //緩存替代者節點   
	//首先搜索是否存在
	binNode<T>* &x = search(e);  //緩存將要被刪除的節點
	binNode<T>* temp = x;
	if (!x)   //不存在則直接返回
		return false;
	//case 1 :命中節點至多隻有一個孩子,則直接刪除,孩子頂上
	if (!(x->lc))  //左孩子爲空,則右孩子頂上
	{
		succ = x = x->rc;
	}
	else if(!(x->rc))
	{
		succ = x = x->lc;
	}
	//case 2 :命中節點有兩個孩子,則通過succ找直接後繼節點
	else
	{
		temp = temp->succ();
		swap(x->data, temp->data);   //交換數據後temp成爲實際要刪除的點
		binNode<T>* u = temp->parent;
		((u == x) ? u->rc : u->lc) = succ = temp->rc;  //跨過節點succ(只有一種情況是左孩子)
	}	
	_hot = temp->parent;	
	if (succ)
		succ->parent = _hot;
	delete temp;
	_size--;
	updateHeightAbove(_hot);
	return true;
}

template<typename T> binNode<T>* bst<T>::removeAt(binNode<T>* &x, binNode<T>* &hot)
{
	binNode<T>* w = x;   //實際被摘除的節點
	binNode<T>* succ = nullptr;  //實際被刪除的節點的接替者
	if (!(x->lc))  //左孩子爲空,則右孩子頂上
	{
		succ = x = x->rc;
	}
	else if (!(x->rc))
	{
		succ = x = x->lc;
	}
	//case 2 :命中節點有兩個孩子,則通過succ找直接後繼節點
	else
	{
		w = w->succ();  //中序遍歷的直接後繼
		swap(x->data, w->data);  //交換數據
		binNode<T>* u = w->parent;
		((u == x) ? u->rc : u->lc) = succ = w->rc;   //隔離實際刪除點
	}
	hot = w->parent;
	if (succ) succ->parent = hot;
	delete w;
	return succ;
}

template<typename T> binNode<T>* bst<T>::connect34(              //按照3+4結構,聯接3個節點及4顆樹
	binNode<T>* a, binNode<T>* b, binNode<T>* c,
	binNode<T>* T0, binNode<T>* T1, binNode<T>* T2, binNode<T>* T3)
{
	a->lc = T0; if (T0) T0->parent = a;
	a->rc = T1; if (T1) T1->parent = a;
	updateHeight(a);   //更新a的高度
	
	c->lc = T2; if (T2) T2->parent = c;
	c->rc = T3; if (T3) T3->parent = c;
	updateHeight(c);

	b->lc = a; a->parent = b;
	b->rc = c; c->parent = b;
	updateHeight(b);
	return b;
}


template<typename T> binNode<T>* bst<T>::rotateAt(binNode<T>* v)
{
	binNode<T>* p = v->parent; binNode<T>* g = p->parent;   //首先根據v找到p和g節點
	if (g->lc == p)  //失衡是由節點刪除導致的(p是g的左孩子)
	{
		if (p->lc == v) //進一步,v是p的左孩子
		{
			p->parent = g->parent;
			return connect34(v, p, g, v->lc, v->rc, p->rc, g->rc);
		}
		else
		{
			v->parent = g->parent;
			return connect34(p, v, g, p->lc, v->lc, v->rc, g->rc);
		}
	}
	else   //失衡是由節點插入造成的
	{
		if (p->rc == v)
		{
			p->parent = g->parent;
			return connect34(g, p, v, g->lc, p->lc, v->lc, v->rc);
		}
		else
		{
			v->parent = g->parent;
			return connect34(g, v, p, g->lc, v->lc, v->rc, p->rc);
		}
	}
}

 

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