1. 程式人生 > >二叉查詢樹【資料結構】

二叉查詢樹【資料結構】

                                                二叉查詢樹

 

 

 

一、介紹

二叉查詢樹(Binary Search Tree),也被稱作二叉搜尋樹。設x是二叉查詢樹中的任意一個結點,則x的鍵值大於等於它的左子樹中任意一個結點的鍵值,小於等於它的右子樹中任意一個結點的鍵值。

二、原理

1、結點的前驅和後繼

結點的前驅:二叉樹中鍵值小於該結點的最大結點。
結點的後繼:二叉樹中鍵值大於該結點的最小結點

查詢前驅結點的方法:
如果x存在左孩子,則x的前驅結點為以其左孩子為根的子樹的最大結點  
如果x不存在左孩子:
1>x是一個右孩子,前驅結點就是它的父結點
2>x是一個左孩子,查詢x的最低父結點,並且x位於該父結點的右子樹中,前驅結點就是這個父結點 

查詢後繼結點的方法
如果x存在右孩子,則x的後繼結點為以其右孩子為根的子樹的最小結點  
如果x不存在右孩子:
1>x是一個左孩子,後繼結點就是它的父結點
2>x是一個右孩子,查詢x的最低父結點,並且x位於該父結點的左子樹中,後繼結點就是這個父結點 

 

2、插入結點

插入操作均是在葉結點處進行的,因此,只要按照插入結點的值找到相應的葉結點就OK了,程式中x、y表示兩個結點,x初始化為根結點,通過這兩個變數尋找新結點 z 的插入位置,根據x結點的值與z結點的值的比較結果選擇對應的路徑,直到x等於NULL,此時y就是我們想尋找的葉節點,這時只需將y和z連結起來就行。

//插入鍵值為key的結點(內部介面)
template <class T>
void BSTree<T>::insert(Node<T>* &tree,Node<T> *z)
{
	Node<T> *y=NULL;
	Node<T> *x=tree;
	//尋找z的插入位置 
	while(x)
	{
		y=x;
		if(z->key<x->key)
		  x=x->l;
		else
		  x=x->r;
	}
	if(!y)//樹為空 
	  tree=z;
	else
	{
		z->p=y;//z的父結點為y 
		if(z->key<y->key)//z是y的左孩子 
		  y->l=z;
		else
		  y->r=z;//z是y的右孩子 
	}
}

//插入鍵值為key的結點(外部介面)
template<class T>
void BSTree<T>::insert(T key)
{
	Node<T> *z=new Node<T>(key,NULL,NULL,NULL);
	if(z) insert(root,z);
} 

3、刪除結點

根據鍵值進行刪除操作,刪除結點後還要維持二叉查詢樹的特點,真正被刪除的結點其實是這一類結點:最多隻有一個孩子結點,刪除這類結點非常簡單,只需要將它的孩子結點和它的父結點進行連結,再把自己釋放掉就行。當被刪除結點就是這類結點時,直接按上面的操作進行即可。當被刪除結點不是這類結點時,就需要找到一個代替的點,這就是該結點的後繼結點,將該結點的鍵值變為後繼結點的鍵值,然後刪除後繼結點即可,容易知道後繼結點是上面描述的那類結點,因此照著描述進行操作即可。

//刪除結點(內部介面)
template<class T>
Node<T>* BSTree<T>::remove(Node<T>* &tree,Node<T> *z)
{
	Node<T> *x=NULL,*y=NULL;//y是真正要被刪除的結點,x是y的孩子結點 
	if(z->l&&z->r)//如果z的左右孩子都在,y就等於z的後繼結點 
	  y=successor(z); 
	else//否則,y=z 
	  y=z;
	
	//此時對於 y有3種情況  1.只存在左孩子  2.只存在右孩子  3.不存在孩子 
	if(y->l)//情況一 
	  x=y->l; 
	else //情況二、三 
	  x=y->r;
	  
	if(x) x->p=y->p;//如果y存在孩子結點,那麼就將孩子結點的父結點變為y的父結點 
	
	if(!y->p) 
	  tree=x;//如果y是根結點,就讓根結點變為x
	else if(y==y->p->l)
	  y->p->l=x;
	else
	  y->p->r=x;
	if(y!=z)
	  z->key=y->key; 
	return y;
}
 
//刪除結點,並返回結點(外部介面)
template<class T>
void BSTree<T>::remove(T key)
{
	Node<T> *z,*node;
	if((z=search(root,key)))//查詢到鍵值為key的結點 
	{
		if((node=remove(root,z)))//刪除成功 
		  delete node;
	}
}

三、C++實現

注意:程式使用了內部介面和外部介面,這樣做主要是因為資料成員 root 為私有變數

#include<iostream>

using namespace std;

template<class T>
class Node{
	public:
		T key;//鍵值 
		Node *p;//父節點
		Node *l;//左孩子 
		Node *r;//右孩子 
		Node(T key,Node *p,Node *l,Node *r):key(key),p(p),l(l),r(r){} 
}; 

template<class T>
class BSTree{
	private:
		Node<T> *root;//根結點
	public:
		BSTree();
		~BSTree();
		void preOrder();//前序遍歷 
		void inOrder();//中序遍歷 
		void postOrder(); //後序遍歷 
		
		Node<T>* search(T key); //查詢二叉樹中鍵值為key的結點 (遞迴實現)
		Node<T>* iterativeSearch(T key); //查詢二叉樹中鍵值為key的結點(非遞迴實現)
		
		T minimum();//查詢最小結點:返回最小結點的鍵值
		T maximum();//查詢最大結點:返回最大結點的鍵值
		
		//查詢結點x的前驅結點,即查詢二叉樹中鍵值小於該結點的最大結點
		Node<T>* predecessor(Node<T> *x); 
		//查詢結點x的後繼結點,即查詢二叉樹中鍵值大於該結點的最小結點
		Node<T>* successor(Node<T> *x);
		
		void insert(T key);//將鍵值為key的結點插入到二叉樹中
		void remove(T key);//刪除鍵值為key的結點 
		
		void destroy();//銷燬二叉樹
		void print();//列印二叉樹
		
	private:
		void preOrder(Node<T> *tree);//前序遍歷
		void inOrder(Node<T> *tree);//中序遍歷
		void postOrder(Node<T> *tree);//後序遍歷
		Node<T>* search(Node<T>* x,T key);//查詢鍵值為key的結點(遞迴實現)
		Node<T>* iterativeSearch(Node<T>* x,T key);//查詢鍵值為key的結點(非遞迴實現)
		
		//查詢最小結點:返回tree為根結點的二叉樹的最小結點 
		Node<T>* minimum(Node<T>* tree);
		//查詢最大結點:返回tree為根節點的二叉樹的最大結點 
		Node<T>* maximum(Node<T>* tree);
		
		void insert(Node<T>* &tree,Node<T> *z);//插入結點z
		Node<T>* remove(Node<T>* &tree,Node<T> *z);//刪除並返回結點z
		
		void destroy(Node<T>* &tree);//銷燬二叉樹
		void print(Node<T>* tree,T key,int direction);//列印二叉樹 
};

//建構函式
template<class T>
BSTree<T>::BSTree()
{
	root=NULL;
} 

//解構函式
template<class T>
BSTree<T>::~BSTree()
{
	destroy();
} 

//前序遍歷(內部介面) 
template<class T>
void BSTree<T>::preOrder(Node<T>* tree)
{
	if(tree)
	{
		cout<<tree->key<<" ";
		preOrder(tree->l);
		preOrder(tree->r); 
	}
}

//前序遍歷(外部介面) 
template<class T>
void BSTree<T>::preOrder()
{
	preOrder(root);
}

//中序遍歷(內部介面) 
template<class T>
void BSTree<T>::inOrder(Node<T>* tree)
{
	if(tree)
	{
		inOrder(tree->l);
		cout<<tree->key<<" ";
		inOrder(tree->r);
	}
}

//中序遍歷(外部介面) 
template<class T>
void BSTree<T>::inOrder()
{
	inOrder(root);
}

//後序遍歷(內部介面) 
template<class T>
void BSTree<T>::postOrder(Node<T> *tree)
{
	if(tree)
	{
		postOrder(tree->l);
		postOrder(tree->r);
		cout<<tree->key<<" ";
	}
}

//後序遍歷(外部介面) 
template<class T>
void BSTree<T>::postOrder()
{
	postOrder(root);
}


//查詢鍵值為key的結點(遞迴實現) 內部介面 
template<class T>
Node<T>* BSTree<T>::search(Node<T>* x,T key)
{
	if(!x||x->key==key)
	  return x;
	if(key<x->key)
	  return search(x->l,key);
	else
	  return search(x->r,key);
}

//查詢鍵值為key的結點(遞迴實現) 外部介面 
template<class T>
Node<T>* BSTree<T>::search(T key)
{
	search(root,key);
}


//查詢鍵值為key的結點(非遞迴實現) 內部介面 
template<class T>
Node<T>* BSTree<T>::iterativeSearch(Node<T>* x,T key)
{
	while(x)
	{
		if(key==x->key)
		  return x;
		else if(key<x->key)
		  x=x->l;
		else
		  x=x->r;
	}
	return x;
}

//查詢鍵值為key的結點(非遞迴實現) 外部介面
template<class T>
Node<T>* BSTree<T>::iterativeSearch(T key)
{
	iterativeSearch(root,key);
}

//查詢最大值(內部介面) 
template<class T>
Node<T>* BSTree<T>::maximum(Node<T>* tree)
{
	if(!tree) return NULL;
	while(tree->r) tree=tree->r;
	return tree;
} 
//查詢最大值(外部介面)
template<class T>
T BSTree<T>::maximum()
{
	Node<T>* p=maximum(root);
	if(p)
	  return p->key;
	else
	  return (T)NULL;
} 

//查詢最小值(內部介面)
template<class T>
Node<T>* BSTree<T>::minimum(Node<T>* tree)
{
	if(!tree) return  NULL;
	while(tree->l) tree=tree->l;
	return tree;
}

//查詢最小值(外部介面) 
template<class T>
T BSTree<T>::minimum()
{
	Node<T>* p=minimum(root);
	if(p)
	  return p->key;
	else
	  return (T)NULL;
}

//結點的前驅:該結點的左子樹的最大結點
//結點的後繼:該結點的右子樹的最小結點

//查詢結點的前驅結點,即二叉樹中鍵值小於該結點的最大結點
/*
如果x存在左孩子,則x的前驅結點為以其左孩子為根的子樹的最大結點  
如果x不存在左孩子:
1>x是一個右孩子,前驅結點就是它的父結點
2>x是一個左孩子,查詢x的最低父結點,並且x位於該父結點的右子樹中,前驅結點就是這個父結點 
*/
template<class T> 
Node<T>* BSTree<T>::predecessor(Node<T> *x)
{
	if(x->l) return maximum(x->l);
	Node<T>* y=x->p;
	while(y&&(x==y->l))
	{
		x=y;
		y=y->p;
	}
	return y;
} 

//查詢結點的後繼結點,即二叉樹中鍵值大於該結點的最小結點
/*
如果x存在右孩子,則x的後繼結點為以其右孩子為根的子樹的最小結點  
如果x不存在右孩子:
1>x是一個左孩子,後繼結點就是它的父結點
2>x是一個右孩子,查詢x的最低父結點,並且x位於該父結點的左子樹中,後繼結點就是這個父結點 
*/
template<class T>
Node<T>* BSTree<T>::successor(Node<T> *x)
{
	if(x->r) return maximum(x->r);
	Node<T> *y=x->p;
	while(y&&(x==y->r))
	{
		x=y;
		y=y->p;
	}
	return y;
}
 
//插入鍵值為key的結點(內部介面)
template <class T>
void BSTree<T>::insert(Node<T>* &tree,Node<T> *z)
{
	Node<T> *y=NULL;
	Node<T> *x=tree;
	//尋找z的插入位置 
	while(x)
	{
		y=x;
		if(z->key<x->key)
		  x=x->l;
		else
		  x=x->r;
	}
	if(!y)//樹為空 
	  tree=z;
	else
	{
		z->p=y;//z的父結點為y 
		if(z->key<y->key)//z是y的左孩子 
		  y->l=z;
		else
		  y->r=z;//z是y的右孩子 
	}
}

//插入鍵值為key的結點(外部介面)
template<class T>
void BSTree<T>::insert(T key)
{
	Node<T> *z=new Node<T>(key,NULL,NULL,NULL);
	if(z) insert(root,z);
} 

//刪除結點(內部介面)
template<class T>
Node<T>* BSTree<T>::remove(Node<T>* &tree,Node<T> *z)
{
	Node<T> *x=NULL,*y=NULL;//y是真正要被刪除的結點,x是y的孩子結點 
	if(z->l&&z->r)//如果z的左右孩子都在,y就等於z的後繼結點 
	  y=successor(z); 
	else//否則,y=z 
	  y=z;
	
	//此時對於 y有3種情況  1.只存在左孩子  2.只存在右孩子  3.不存在孩子 
	if(y->l)//情況一 
	  x=y->l; 
	else //情況二、三 
	  x=y->r;
	  
	if(x) x->p=y->p;//如果y存在孩子結點,那麼就將孩子結點的父結點變為y的父結點 
	
	if(!y->p) 
	  tree=x;//如果y是根結點,就讓根結點變為x
	else if(y==y->p->l)
	  y->p->l=x;
	else
	  y->p->r=x;
	if(y!=z)
	  z->key=y->key; 
	return y;
}
 
//刪除結點,並返回結點(外部介面)
template<class T>
void BSTree<T>::remove(T key)
{
	Node<T> *z,*node;
	if((z=search(root,key)))//查詢到鍵值為key的結點 
	{
		if((node=remove(root,z)))//刪除成功 
		  delete node;
	}
}

//列印(內部介面) 
template<class T>
void BSTree<T>::print(Node<T> *tree,T key,int direction)
{
	if(tree)
	{
		if(direction==0)
		  cout<<tree->key<<" is root"<<endl;
		else
		  cout<<tree->key<<" is "<<key<<"'s"<<(direction==1?"right child":"left child")<<endl;
		print(tree->l,tree->key,-1);
		print(tree->r,tree->key,1);  
	}
}

//列印(外部介面) 
template<class T>
void BSTree<T>::print()
{
	if(root) print(root,root->key,0);
}

//銷燬(內部介面)
template<class T>
void BSTree<T>::destroy(Node<T>* &tree)
{
	if(tree)
	{
		if(tree->l) destroy(tree->l);
		if(tree->r) destroy(tree->r);
		delete tree;
		tree=NULL;
	}
}

//外部介面
template<class T>
void BSTree<T>::destroy()
{
	destroy(root);
}