二叉查找樹的實現

二叉查找樹是這樣定義的:

二叉查找樹(Binary Search Tree),或者是一棵空樹,或者是具有下列性質的二叉樹:

  1. 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
  2. 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
  3. 它的左、右子樹也分別爲二叉排序樹

二叉查找樹是一種高效的搜尋樹,類似於如下:


以下是我參考網上的資源實現的:
#include <iostream>
using namespace std;

/*二叉查找樹的節點類*/
template <class T> 
class TreeNode
{
public:
	TreeNode():left(NULL),right(NULL),freq(1){}
	T data;
	unsigned int freq;
	TreeNode *left;
	TreeNode *right;
};

/*二叉查找樹類*/
template <class T> 
class BST
{
public:
	BST():root(NULL){}
	void insert(T x);//提供的公共接口
	void traversal();
	TreeNode<T> *find(T x);
	void Delete(T x);
private:
	TreeNode<T> *root;
	void insert_node(TreeNode<T>* &node, T x);
	void InOrder(TreeNode<T> *node);
	TreeNode<T> *findnode(TreeNode<T> *node, T x);
	void delete_node(TreeNode<T> *&node, T x);
};

template<class T> 
void BST<T>::insert_node(TreeNode<T>* &node, T x)
{
	if(node == NULL)
	{
		node  = new TreeNode<T>();
		node->data = x;
		return;
	}
	if(node->data > x)
		insert_node(node->left, x);
	else if(node->data < x)
		insert_node(node->right, x);
	else
		(node->freq)++;//頻數加1
} 


template<class T> 
void BST<T>::insert(T x)
{
	insert_node(root, x);
}

template<class T>
void BST<T>::traversal()
{
	InOrder(root);
}

template <class T>
void BST<T>::InOrder(TreeNode<T> *node)
{
	if(node != NULL)
	{
		InOrder(node->left);
		cout<<node->data<<endl;
		InOrder(node->right);
	}
}

template <class T>
TreeNode<T>* BST<T>::find(T x)
{
	return findnode(root, x);
}
template <class T>
TreeNode<T>* BST<T>::findnode(TreeNode<T>* node, T x)
{
	if(node == NULL)  return NULL;//類似於二分法查找

	if(node->data == x)
		return node;
	else if(node->data < x)
		return findnode(node->right, x);
	else
		return findnode(node->left, x);
}

template<class T>
void BST<T>::Delete(T x)
{
	delete_node(root, x);
}

template<class T>
void BST<T>::delete_node(TreeNode<T> *&node, T x)
{
	if(node == NULL) return;
	if(node->data > x)
		delete_node(node->left, x);
	else if(node->data < x)
		delete_node(node->right, x);

	else
	{
		if(node->left != NULL && node->right != NULL)//有兩個孩子節點
		{
			TreeNode<T>*temp = node->right;
			while(temp->left != NULL) temp = temp->left;//找到右子樹中最小的孩子節點
			node->data = temp->data;
			node->freq = temp->freq;
			delete_node(node->right, node->data);//刪除右子樹中最小的孩子節點
		}
		else//有1個或者0個孩子節點
		{
			TreeNode<T>* temp = node;
			if(node->left == NULL)
				node = node->right;
			else if(node->right == NULL)
				node= node->left;
		}
	}
}

int main()
{
	int a[] = {3,2,5,6,4,1};
	BST<int> *bst = new BST<int>();
	
	for(int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
		bst->insert(a[i]);
	bst->traversal();

	TreeNode<int> *node = bst->find(a[3]);
	if(node != NULL)
		cout<<"find the data "<<node->data<<endl;
	else
		cout<<"can't find the data"<<endl;

	bst->Delete(a[0]);
	cout<<"after delete "<<a[0]<<endl;
	bst->traversal();

	return 0;
}

下面對算法進行解釋:
定義BST的時候我們使用了一個私有的接口和一個公有的接口,是一個小技巧,體現了C++的封裝性

insert函數:
想要構造一個二叉查找樹,每一次都是把一個節點插入到現有的二叉查找樹中,在插入的過程中,遞歸比較,如果比該節點大就往右插入,否則往左插入

traversal()函數:
中序遍歷二叉樹能夠得到一個有序的數列,因爲我們二叉查找樹就是定義成node->left->data i小於node->data小於node->right->data,如果按照中序遍歷自然是有序的了

delete()函數:
刪除一個節點比較複雜,因爲要考慮到這個節點是否有孩子,以及孩子的個數,可以分類來考慮,其實不難:
1.如果沒有孩子節點,直接刪除這節點就是了
2.如果有一個孩子節點,就用這個孩子節點來代替這個節點
3.如果有兩個孩子節點,約定俗成的用右子樹中最小的節點來代替這個節點,其實就是右子樹的最最左子節點y


二叉查找樹有其弊端,我們說其查找,插入的時間複雜度是O(logn)但是但我們的測試數組是{1,2,3,4,5,6}的時候,構造的二叉查找樹就是這樣的了
1
  2  
   3
     4
       5
         6
那麼我們的搜尋時間就變爲了O(n)線性的了這個就不能體現它的優勢了,所以我們可以優化,優化的方法可以有紅黑樹和AVL樹等


發佈了39 篇原創文章 · 獲贊 7 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章