二叉搜索树

参考:《算法导论》

定义

  二叉搜索树首先是一棵二叉树(每个节点至多有两个孩子),每个节点的左子树中节点的键值都不大于它,右子树中的节点键值都不小于它。每个节点的属性包括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;
}

  

 

posted @ 2019-04-16 14:46  dlutjwh  阅读(141)  评论(0编辑  收藏  举报