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

    参考:《算法导论》

    定义

      二叉搜索树首先是一棵二叉树(每个节点至多有两个孩子),每个节点的左子树中节点的键值都不大于它,右子树中的节点键值都不小于它。每个节点的属性包括left(左儿子节点)、right(右儿子节点)、parent(父亲节点),以及该节点的键值data。

      如下图所示,图(a)、(b)是一棵二叉搜索树,图c不是一棵二叉搜索树

     

    定义二叉搜索树的数据结构如下所示

    using Elem_Type = int;
    struct BitSearchTree {
    	BitSearchTree *left = nullptr, *right = nullptr, *parent = nullptr;  //左右儿子和父亲
    	Elem_Type data;  //存储的数据
    }*Root;  //定义一个根

      二叉搜索树的操作主要包括顺序遍历Inorder_Walk、查询节点Find_node、查找最大值节点Maxmum、最小值节点Minimum、查找后继节点Succcessor、查找后缀节点Precursor、插入节点InsertNode、删除节点DeleteNode

    操作

    顺序遍历

      由于二叉搜索树的性质,因此中序遍历(左、根、右)即可按顺序输出二叉搜索树的节点,时间复杂度O(n),n为二叉搜索树的节点数

    void Inorder_Walk(BitSearchTree *t) {   //从小到大输出二叉树的值(中序遍历)
    	if (t != nullptr) {   //非空
    		Inorder_Walk(t->left);  //遍历左儿子
    		printf("%d", t->data);     //输出当前节点的键值
    		Inorder_Walk(t -> right);  //遍历右儿子
    	}
    }

    查询

      查询二叉搜索树中是否存在某个键值为x的节点,根据二叉搜索树的性质,先从根开始查询,当查询的键值比当前节点键值大时,向右走,比当前的键值小时向左儿子走,直到找到该值或者到到空节点(未找到)。函数返回键值所在的节点或者,未查到则返回空节点。如下图所示,查询节点1,从根节点开始,5>1,向左走到3,3>1,向左到1,则返回该节点,查询8时,5<8,向右到6,6<8,向右到9,9>8,向左,到空节点。时间复杂度为O(h),h为二叉搜索树的高度

    BitSearchTree *Find_node(BitSearchTree *t, Elem_Type x) {    //搜索值为x的节点(也可用while循环)
    	if (t == nullptr) {         //空节点,未查到
    		//cout<<"not find"<<endl;
    		return t;
    	}
    	if (t->data == x)     //当前节点的键值与查询的键值相同
    		return t;
    	if (x > t->data)        //查询的键值比当前节点大,向右走
    		return Find_node(t->right,x);
    	return Find_node(t->left, x);    //左走
    }
    //调用:Find_node(root, x),从根开始查

    最大值和最小值

      根据二叉搜索树的性质,一直向左走即可找到最小键值的节点,一直向右走即可找到最大键值的节点。复杂度为O(h),h为高度

    BitSearchTree *Maxmum(BitSearchTree *t,Elem_Type &a) {    //查找最大值节点
    	while (t->right != nullptr) {
    		a = t->data;
    		t = t->right;	  //向右走
    	}
    	return t;
    }
    BitSearchTree *Minimum(BitSearchTree *t, Elem_Type &a) {  //查找最小值节点
    	while (t->left != nullptr) {
    		a = t->data;
    		t = t->left;                    //向左走
    	}
    	return t;
    }

    前驱和后继

      节点x的后继是大于x键值的节点中,键值最小的节点,如果x的键值为最大值,则返回空节点。根据二叉树的性质,如果该节点存在右孩子,则其后继为右子树的最小值。如图所示,节点15的后继为右子树的最小值17。如果该节点不存在右子树,则其后继为其祖先中作为左儿子存在的节点的父节点。如图中找15的后继节点,由于15无右孩子,需要向上找,直到6,是作为左儿子存在,故15的后继为6的父亲16。复杂度为O(h),h为高度。

    BitSearchTree *Succcessor(BitSearchTree *t, Elem_Type &a) {  //查找节点t的后继
    	if (t->right != nullptr)                  //存在右儿子
    		return Minimum(t->right, a);     //右子树最小值
    	BitSearchTree *p = t->parent;        
    	while (p != nullptr&&p->right == t) {     //找到作为左儿子存在的节点t
    		a = p->data;
    		t = p;
    		p = p->parent;
    	}
    	return p;     //返回t的父亲节点p
    }
    BitSearchTree *Precursor(BitSearchTree *t, Elem_Type &a) {  //查找节点t的前驱
    	if (t->left != nullptr)               //存在左儿子
    		return Maxmum(t->left, a);     //返回左子树最大值
    	BitSearchTree *p = t->parent;
    	while (p != nullptr&&p->left == t) {     //找到作为右儿子存在的节点t
    		t = p;
    		a = p->data;
    		p = p->parent;
    	}
    	return p;    //返回t的父亲p
    }

     插入

      二叉搜索树的插入都是将新节点插到叶子节点的子节点。如果树非空,从根节点开始,如果插入节点的键值大于当前节点,则向右走;否则向左走。直到叶子节点,将插入的节点作为叶子节点的子节点,左儿子还是右儿子由值决定。复杂度O(h),h为树的高度。

    BitSearchTree *InsertNode(BitSearchTree *t, Elem_Type a) {   //插入一个值a,t一般为root
    	BitSearchTree *x =new BitSearchTree(), *z=nullptr;
    	x->data = a;
    	while (t != nullptr) {     
    		z = t;
    		if (a > t->data)      //向右走
    			t = t->right;
    		else t = t->left;     //向左走
    	}
    	x->parent = z;
    	if (z == nullptr) {      //树为空
    		Root = x;
    		return Root;
    	}
    	else if (z->data > a)    //和叶子节点相连
    		z->left = x;
    	else z->right = x;
    	return x;
    }

     删除

       删除的情况比较复杂,因为如果删除的节点有孩子,还需要找一个节点代替这个被删除的节点,使得新的二叉树满足二叉搜索树的性质,删除的情况主要分为四种如下图所示,设要删除的节点为z

    • 删除的节点无左儿子

      此时直接使其右儿子取代z即可,则易知二叉搜搜索树的性质不变,时间复杂度O(1)

    •  删除的节点无右儿子

       同理,此时直接使其右儿子取代z即可,二叉搜搜索树的性质不变,时间复杂度O(1)

    •  z节点左右儿子都存在,其后继恰好是其右儿子,此时用什么节点替代z如何才能保持二叉搜索树的性质不变呢?考虑到后继节点的特点,如果用z的后继替代,则左子树键值依然不大于z,右子树键值不小于z,此时直接用y代替z即可

    •  z的左右儿子都在,且其后继y不是他的孩子,此时依然要用z的后继节点代替z才能保持二叉搜索树的性质不变。用后继y(y一定没有左孩子)代替z,y的右子树代替y的位置

     

      删除操作时间复杂度为O(h),h为树的高度

       首先定义一个Transplant函数,表示使用节点v为根的子树代替节点为u的子树(函数没有更新v原先的左右孩子)

    void Transplant(BitSearchTree *u, BitSearchTree *v) {  //v为根的子树代替u为根的子树,未更新v的孩子
    	if (u->parent == nullptr)    //代替根节点
    		Root = v;
    	else if (u->parent->left == u)   //u为左孩子
    		u->parent->left = v;
    	else u->parent->right = v;      //u为右孩子
    	if (v != nullptr)
    		v->parent = u->parent;
    }
    

      下面定义删除节点函数

    void DeleteNode(BitSearchTree *t,int &a) {  //删除节点t,t存储的数据位a
    	if (t != nullptr)                   
    		a = t->data;
    	if (t->left == nullptr)                //t无左儿子
    		Transplant(t, t->right);
    	else if (t->right == nullptr)          //t无右儿子
    		Transplant(t, t->left);
    	else if (t->right->left == nullptr) {    //z的后继为其右儿子
    		Transplant(t, t->right);
    		t->left->parent = t->right;
    		t->right->left = t->left;	
    	}
    	else {                      //其他情况
    		int b=0;
    		BitSearchTree *p = Succcessor(t, b);   
    		Transplant(p, p->right);
    		Transplant(t, p);
    		p->left = t->left;
    		p->left->parent = p;
    		p->right = t->right;
    		p->right->parent = p;
    	}
    	delete t;
    }
    

      

  • 相关阅读:
    实验四 决策树算法及应用
    实验三 朴素贝叶斯算法及应用
    实验二 K-近邻算法及应用
    实验一 感知器及其应用
    实验三 面向对象分析与设计
    实验二 结构化分析与设计
    实验一 软件开发文档与工具的安装与使用
    ATM管理系统
    活动图与流程图的区别与联系
    四则运算题目生成程序
  • 原文地址:https://www.cnblogs.com/dlutjwh/p/10717022.html
Copyright © 2011-2022 走看看