码迷,mamicode.com
首页 > 其他好文 > 详细

二叉搜索树

时间:2014-07-06 11:06:37      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:二叉搜索树 二叉查找树   二叉遍历树   指针   链表   

定义

二叉搜索树(Binary Search Tree)或称二叉查找树,也称二叉排序树(Binary Sort Tree)。它或者是一棵空树,或者是具有下列性质的二叉树:
  1. 若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 左、右子树也分别为二叉搜索树;

性质

二叉搜索树与普通二叉树相比,有一些优秀的特征或性质:
  1. 由于节点是有序排放的:左子树<根节点<右子树。故在查找一个节点的时候,只需先和根节点比较,再决定是进入左子树还是右子树查找。而普通二叉树需要一个一个地遍历。
  2. 查找、插入的时间复杂度是O(h),h是树的高度。即当树的高度尽量低(比较平衡)时,效率高。

算法解释

不得不说,非线性结构的操作确实难于线性结构的,有些算法的逻辑比较复杂。下面对代码中给出的部分算法进行解释,便于阅读。
  1. 构造方法:BinarySearchTree();建树的过程就是一个插入的过程,所以插入操作是重要的。
  2. 求叶子节点数:int leaf();按某种方式遍历树,若左右孩子皆为空,即为叶子节点。代码中是按中序遍历的。
  3. 查找指定节点:bool search(ElemType);根据二叉搜索树节点的分布特点,查找只需在左或右子树中进行,并且插入树中已有的节点也算插入失败。插入操作逻辑比较清楚,代码易看懂。
  4. 获取指定节点的前驱:BTNode* predecessor(ElemType);这个操作在普通二叉树中是没有的。在二叉搜索树中,某节点的前驱指的是中序遍历时的前驱。故该操作本质上是一个中序遍历的过程。稍微不同的是,在遍历的过程中需要记录最近一次遍历的节点plastVisit,并判断当前访问的节点是否是指定节点。若是,则返回plastVisit。
  5. 获取后继和获取前驱的道理是一样的。
  6. 获取最小节点:BTNode* minimum();二叉搜索树中的最小节点一定是位于左子树(如果存在)。于是,不断遍历左子树即可,比较简单。
  7. 获取最大节点:BTNode* maximum();二叉搜索树中的最大节点一定是位于右子树(如果存在)。于是,不断遍历右子树即可,比较简单。
  8. 插入节点:bool insertNode(ElemType);插入的过程本质上也是查找,需要记住的是:新节点会插入到叶子节点处。
  9. 遍历:void traverse();二叉搜索树的遍历可以是多样的,各种遍历方式也在上一篇二叉树中实现了,这里只给出中序遍历。因为,对一棵二叉搜索树进行中序遍历会得到节点从小到大的排序序列。
  10. 删除节点:bool deleteNode(ElemType);删除的规则是这样的:
  • 若待删节点无左子树,则用其右子树的根节点替换它。
  • 若待删节点有左子树,则在左子树中寻找中序遍历的最后一个节点,用该节点替换它。
删除规则比较好看懂,但具体实施时,细节繁多,很不容易。这也是所有操作中最复杂的。画图理解:
bubuko.com,布布扣
其它操作在上一篇二叉树中已有所解释,不再赘述。具体细节还得看代码,代码较长,建议以方法为单位来理解,

代码

类定义
#include<iostream>
#include<iomanip>
#include<stack>
#include<queue>
using namespace std;
typedef int ElemType;
//二叉树节点
class BTNode   //Binary Tree Node
{
public:
	ElemType data;
	BTNode* lchild;   //左孩子
	BTNode* rchild;   //右孩子
	BTNode(ElemType d, BTNode* left = NULL, BTNode* right = NULL)
		:data(d), lchild(left), rchild(right){}
};
//二叉搜索树
class BinarySearchTree
{
private:
	//树根
	BTNode* Root;
	//节点总数
	int size;
public:
	//构造方法
	BinarySearchTree();
	//析构方法
	~BinarySearchTree();
	//判断树空
	bool empty()
	{return Root == NULL;}
	//求节点总数
	int getSize()
	{return size;}
	//求叶子节点数
	int leaf();
	//查找
	bool search(ElemType);
	//获取父节点
	BTNode* parent(ElemType);
	//获取前驱
	BTNode* predecessor(ElemType);
	//获取后继
	BTNode* successor(ElemType);
	//获取最小节点
	BTNode* minimum();
	//获取最大节点
	BTNode* maximum();
	//插入新节点
	bool insertNode(ElemType);
	//删除节点
	bool deleteNode(ElemType);
	//中序遍历
	void traverse()
	{inOrderWithoutRecursion();}
	void inOrderWithoutRecursion();
};
类实现
//构造方法
BinarySearchTree::BinarySearchTree()
{
	size = 0;
	Root = NULL;
	ElemType data;
	cout << "建树,输入节点,输入0结束:";
	while (cin >> data && data)
		insertNode(data);
}
//析构方法
BinarySearchTree::~BinarySearchTree()
{
	if (!empty())
	{
		queue<BTNode*> q;
		q.push(Root);
		BTNode* p = NULL;
		while (!q.empty())
		{
			p = q.front();
			q.pop();
			//左孩子不为空,则左孩子入队
			if (p->lchild)
				q.push(p->lchild);
			//右孩子不为空,则右孩子入队
			if (p->rchild)
				q.push(p->rchild);
			//释放内存
			delete p;
		}
	}
}
//求叶子节点数
int BinarySearchTree::leaf()
{
	int num = 0;
	//按中序遍历
	if (!empty())
	{
		stack<BTNode*> s;
		BTNode* p = Root;
		while (!s.empty() || p)
		{
			if (p)
			{
				s.push(p);
				p = p->lchild;
			}
			else
			{
				p = s.top();
				s.pop();
				//左右子树均为空,则为叶子节点
				if (p->lchild == NULL && p->rchild == NULL)
					num++;
				p = p->rchild;
			}
		}
	}
	return num;
}
//查找
bool BinarySearchTree::search(ElemType data)
{
	if (!empty())
	{
		BTNode* p = Root;
		while (p)
		{
			if (data == p->data)
				return true;
			else if (data < p->data)
				p = p->lchild;
			else
				p = p->rchild;
		}
	}
	//树空或查找失败
	return false;
}
BTNode* BinarySearchTree::parent(ElemType data)
{
	if (!empty())
	{
		//根节点的父节点为空
		if (Root->data == data)
			return NULL;
		stack<BTNode*> s;
		BTNode* p = Root;
		while (!s.empty() || p)
		{
			if (p)
			{
				s.push(p);
				p = p->lchild;
			}
			else
			{//左子树访问完后,访问右子树
				p = s.top();
				s.pop();
				if ((p->lchild && p->lchild->data == data) || (p->rchild && p->rchild->data == data))
					return p;
				p = p->rchild;
			}
		}
	}
	return NULL;
}
//获取前驱
BTNode* BinarySearchTree::predecessor(ElemType data)
{
	BTNode* pcur, *plastVisit;
	pcur = plastVisit = NULL;
	if (!empty())
	{
		stack<BTNode*> s;
		pcur = Root;
		while (!s.empty() || pcur)
		{
			if (pcur)
			{
				//plastVisit = pcur;
				s.push(pcur);
				pcur = pcur->lchild;
			}
			else
			{
				pcur = s.top();
				s.pop();
				if (pcur->data == data)
					return plastVisit;
				else
					plastVisit = pcur;
				pcur = pcur->rchild;
			}
		}
	}
	return plastVisit;
}
//获取后继
BTNode* BinarySearchTree::successor(ElemType data)
{
	BTNode* pcur = NULL;
	pcur = Root;
	if (!empty())
	{
		stack<BTNode*> s;
		while (!s.empty() || pcur)
		{
			if (pcur)
			{
				s.push(pcur);
				pcur = pcur->lchild;
			}
			else
			{
				pcur = s.top();
				s.pop();
				if (pcur->data == data)
					return pcur->rchild;
				pcur = pcur->rchild;
			}
		}
	}
	//空树
	return NULL;
}
//获取最小节点
BTNode* BinarySearchTree::minimum()
{
	//最小节点在左子树最下边
	if (!empty())
	{
		BTNode* p = Root;
		while (p->lchild)
			p = p->lchild;
		return p;
	}
	//树空
	return NULL;
}
//获取最大节点
BTNode* BinarySearchTree::maximum()
{
	//最大节点在右子树最下边
	if (!empty())
	{
		BTNode* p = Root;
		while (p->rchild)
			p = p->rchild;
		return p;
	}
	//树空
	return NULL;
}
//插入新节点
bool BinarySearchTree::insertNode(ElemType data)
{
	/*
	 新节点都会被插入到叶子处
	 插入一般不会失败,除非是插入了重复节点。
	*/
	if (Root == NULL)
	{
		Root = new BTNode(data);
		size++;
		return true;
	}
	else
	{
		BTNode* p = Root;
		while (true)
		{
			if (data < p->data)
			{
				//如果有左子树,则继续遍历左子树
				if (p->lchild)
					p = p->lchild;
				else
				{//否则,插入节点,下同
					p->lchild = new BTNode(data);
					break;
				}
			}
			else if (data > p->data)
			{
				if (p->rchild)
					p = p->rchild;
				else
				{
					p->rchild = new BTNode(data);
					break;
				}
			}
			else//遇到重复节点
				return false;
		}
		//插入新节点成功,节点总数加一
		size++;
		return true;
	}
}
//删除节点
bool BinarySearchTree::deleteNode(ElemType data)
{
	/*
	删除规则
	1.若待删节点无左子树,则用其右子树的根节点替换它。
	2.若待删节点有左子树,则在左子树中寻找中序遍历的最后一个节点,用该节点替换它。
	*/
	if (!empty())
	{
		//树中无此节点,删除失败
		if (!search(data))
			return false;
		/*
		p:待删结点
		Parent:待删除节点的父节点
		temp:替换节点
		tempp:替换节点的父节点
		*/
		BTNode* p, *Parent, *temp, *tempp;
		p = Parent = temp = tempp = NULL;
		//获取待删除节点的父节点
		Parent = parent(data);
		//根据父节点,确定待删结点
		if (Parent->lchild && Parent->lchild->data == data)
			p = Parent->lchild;
		else
			p = Parent->rchild;
		//如果左子树不为空,查找其中序遍历的最后一个节点
		if (p->lchild)
		{
			temp = p->lchild;
			while (temp->rchild)
			{
				tempp = temp;
				//不断遍历右子树
				temp = temp->rchild;
			}
			//如果p的左孩子即是替换节点
			if (tempp == NULL)
				p->lchild = temp->lchild;
			else//替换节点的左子树作为其父节点的右子树(这句难以理解,需要多想想)
				tempp->rchild = temp->lchild;
			//替换节点继承待删结点的左右孩子
			temp->lchild = p->lchild;
			temp->rchild = p->rchild;
		}
		else
			temp = p->rchild;
		//替换节点替换掉待删结点(这也是为什么需要找到待删结点的父节点)
		if (Parent == NULL)  //待删结点恰为根节点
			Root = temp;
		else if (Parent->lchild == p)  //待删结点本身处于左子树
			Parent->lchild = temp;
		else//待删结点本身处于右子树
			Parent->rchild = temp;
		//删除待删结点
		delete p;
		//节点总数减一
		size--;
		return true;
	}
	//树空
	return false;
}
//中序遍历
void BinarySearchTree::inOrderWithoutRecursion()
{
	if (!empty())
	{
		stack<BTNode*> s;
		BTNode* p = Root;
		while (!s.empty() || p)
		{
			if (p)
			{
				s.push(p);
				p = p->lchild;
			}
			else
			{
				p = s.top();
				s.pop();
				cout << setw(4) << p->data;
				p = p->rchild;
			}
		}
		cout << endl;
	}
}
主函数
int main()
{
	cout << "******二叉搜索树***by David***" << endl;
	BinarySearchTree tree;
	cout << "中序遍历" << endl;
	tree.traverse();
	cout << "树中节点总数 " << tree.getSize() << endl;
	cout << "叶子节点数 " << tree.leaf() << endl;
	BTNode* p = NULL;
	p = tree.minimum();
	p ? cout << "最小节点是 " << p->data << endl : cout << "树空!" << endl;
	p = tree.maximum();
	p ? cout << "最大节点是 " << p->data << endl : cout << "树空!" << endl;
	ElemType data = 2;
	cout << endl << "查找节点 " << data << endl;
	if (tree.search(data))
	{
		cout << "节点 " << data << " 查找成功!" << endl;
		p = tree.predecessor(data);
		p ? cout << "节点 " << data << " 的前驱是 " << p->data << endl : cout << "无前驱!" << endl;
		p = tree.successor(data);
		p ? cout << "节点 " << data << " 的后继是 " << p->data << endl : cout << "无后继!" << endl;
	}
	else
		cout << "节点 " << data << "不在树中!" << endl;
	data = 6;
	cout << endl <<"删除节点 " << data << endl;
	if (tree.deleteNode(data))
	{
		cout << "删除成功!" << endl;
		cout << "中序遍历" << endl;
		tree.traverse();
		cout << "树中节点总数 " << tree.getSize() << endl;
		cout << "叶子节点数 " << tree.leaf() << endl;
		data = 5;
		cout << endl << "查找节点 " << data << endl;
		if (tree.search(data))
		{
			cout << "节点 " << data << " 查找成功!" << endl;
			p = tree.predecessor(data);
			p ? cout << "节点 " << data << " 的前驱是 " << p->data << endl : cout << "无前驱!" << endl;
			p = tree.successor(data);
			p ? cout << "节点 " << data << " 的后继是 " << p->data << endl : cout << "无后继!" << endl;
		}
		else
			cout << "节点 " << data << "不在树中!" << endl;
	}
	else
		cout << "删除失败!" << endl;
	cout << endl;
	system("pause");
	return 0;
}

运行

bubuko.com,布布扣bubuko.com,布布扣

完整代码下载:二叉搜索树
专栏目录:数据结构与算法目录








二叉搜索树,布布扣,bubuko.com

二叉搜索树

标签:二叉搜索树 二叉查找树   二叉遍历树   指针   链表   

原文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/36437541

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!