zoukankan      html  css  js  c++  java
  • 二叉搜索树

    定义

    二叉搜索树(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);删除的规则是这种:
    • 若待删节点无左子树,则用其右子树的根节点替换它。
    • 若待删节点有左子树,则在左子树中寻找中序遍历的最后一个节点,用该节点替换它。


    删除规则比較好看懂。但详细实施时,细节繁多,非常不easy。

    这也是全部操作中最复杂的。绘图理解:


    其他操作在上一篇二叉树中已有所解释。不再赘述。

    详细细节还得看代码,代码较长,建议以方法为单位来理解。

    代码

    类定义

    #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; }

    执行



    算法优化

    插入算法的一个优化版本号
    //插入新节点
    bool BinarySearchTree::insertNode(ElemType data)
    {
    	BTNode *parent, *child;
    	parent = NULL;
    	child = Root;
    	while (child)
    	{
    		parent = child;
    		if (data < child->data)
    			child = child->lchild;
    		else if (data > child->data)
    			child = child->rchild;
    		else//插入同样keyword的节点。返回false
    			return false;
    	}
    	//此时parent要么为空,要么就是叶子节点
    	if (parent == NULL)//空树
    		Root = new BTNode(data);
    	else if (data < parent->data)
    		parent->lchild = new BTNode(data);
    	else
    		parent->rchild = new BTNode(data);
    	size++;
    	return true;
    }

    完整代码下载:二叉搜索树
        
    若是有所帮助,顶一个哦!


    专栏文件夹:


    版权声明:本文博主原创文章。转载,转载请注明出处。

  • 相关阅读:
    [转]double free or corruption (!prev): 0x080644c8 ***
    linux sleep用法
    ubuntu的终端下修改IP、MAC、DNS及GATE
    jmeter mina2总结
    double free or corruption (!prev): 0x080644c8 ***
    jmeter最简单使用
    超级详细Tcpdump 的用法
    eclipse下打开jmeter源码
    Jmeter 命令行选项目录
    JavaScript的一些实用技巧收藏
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/4805154.html
Copyright © 2011-2022 走看看