当前位置:   article > 正文

C++数据结构之BST(二叉搜索树)的实现

bst

预告:本文是后续实现各种各样平衡二叉搜索树的铺垫。

在这里插入图片描述

01.BST的介绍

  1. BST 寻关键码访问 (call by key)
    相比于——
    Vector call by rank
    List call by position
    Hash table call by value
  2. 有序性
  3. 单调性

扩:从不可重复到可重复的扩充(下图)

在这里插入图片描述
在这里插入图片描述

02.BST 要实现的对外方法

方法 功能 参数 返回值
search 查找 T const & val BinNode * & 待插入位置/目标节点指针的引用
insert 插入 BinNode * 新节点指针
remove 移除 bool 树上是否存在值为val的节点

03.摘要

  1. 虚函数,方便派生类进行重写。
  2. 全局静态模板函数,适用于AVL,Splay,RedBlack等各种BST
  3. 这里的remove一看就是对外的,因为参数终于不是指针了,而是值。需要我们先找位置。
  4. return searchIn(BinTree::root, hot = NULL, val); 注意 =NULL

04.查找节点

4.1四个引用,都有妙用

看到searchIn的声明,居然全都是引用类型。

static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val)
  • 1

列举这四个引用各自的功能——

返回值引用:插入节点时,这个引用相当于插入位置,后续我们将新节点的指针赋给到这个返回值,父节点的左右孩子之一就会连上新节点。

BinNode<T> * & rt:如果这个不是引用,返回值返回的就是一个仅在函数内部的局部变量(即形参),后续改写这个引用值时,会发生错误。

BinNode<T> * & hot_node:在递归中随深度不断更新这个记忆热点,也是为了方便插入算法,等到最后退出时hot存的是插入位置的父节点。

T const & val:传递引用变量可以提速,为了不误改,前面加上const做约束。

4.2递归版

		virtual BinNode<T> * & search(T const & val)
		{
			return searchIn(BinTree<T>::root, hot = NULL, val); //注意,这个=NULL,如果不写,插入根节点并向上更新高度时就会报错
		}
		static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val)
		{
			if (!rt || rt->data == val) return rt; // 返回的是引用

			hot_node = rt; //在递归中随深度不断更新

			if (val < rt->data) return searchIn(rt->left, hot_node, val);
			else return searchIn(rt->right, hot_node, val);
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

4.3非递归版

尾递归转迭代,略。

05.插入节点

5.1利用search的返回值

有了查找节点算法中“记忆热点”hot的设计,经过search()的运行,就可以得到插入位置的父节点。或许应该记得BinTree里写过的几个函数:insertAsLeft()insertAsRight(),我们只需要将valhot->data做比较即可。在这里,我们换一种写法——不浪费search的返回值。你知道,查找一旦失败,返回值就是NULL的引用,利用它,就无需在insert()中判断究竟应该插入到hot的左边还是右边。

先找到插入位置,X的类型必须是引用,后续我们将新节点的指针赋给到X,hot的左右孩子之一就会连上新节点。

BinNode<T> * & X = search(val); 
  • 1

下面这一句话将 “父->子” “子->父” 相互关系都连接好了。

X = new BinNode(val, hot); 
  • 1

在这里插入图片描述

5.2更新高度的注意事项

更新高度由于之前做的优化,检测到某处更新后与更新前高度一致则不会再上行更新,所以高度更新要给父节点更新,即updateHighAbove(hot),如果给了X更新,那就不会继续下去。

5.3插入算法的完整代码

		virtual BinNode<T> * insert(T const & val)
		{
			
			BinNode<T> * & X = search(val); //为了找到插入位置
			if (!X)
			{
				X = new BinNode(val, hot); //这一句话将两个关系连接

				// 不要忘记
				BinTree<T>::size++;
				updateHighAbove(hot);
			}
			return X;
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

insert()的返回值是X,但返回类型是BinNode<T> *,并不是引用,这在语法中是允许的。所返回的东西仅仅在数值上与X相同,但与X完全脱离了关系。

06.删除节点

6.1框架

		virtual bool remove(T const & val)
		{
			BinNode<T> * & X = search(val);
			if (!X) //树里没有val
			{
				return false;
			}
			else
			{
				removeAt(X, hot);
				BinTree<T>::size--;
				updateHighAbove(hot);
				return true;
			}
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

6.2单分支,直接替代

在这里插入图片描述

6.3双分支,化繁为简

还是想,哪一个节点替代被删节点的位置。那一定是直接后继。求中序遍历下的直接后继。

// struct BinNode中,功能:求中序遍历下的直接后继
	BinNode<T> * succ()
	{
		BinNode<T> * succ_node;
		if (right)
		{
			succ_node = right;
			while (succ_node->left)
				succ_node = succ_node->left;
		}
		else
		{
			succ_node = this;
			while (succ_node != succ_node->parent->left)
				succ_node = succ_node->parent;
			succ_node = succ_node->parent;
		}
		return succ_node;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述

6.4代码

		BinNode<T> * removeAt(BinNode<T> * & X, BinNode<T> * & hot_node) //注意这个引用
		{
			// hot_node指向要被删除的父亲
			BinNode<T> * del_node; // 实际要被删除的节点
			BinNode<T> * succ_node; // 实际要被删除的节点的接替者

			if (!X->left)
			{
				del_node = X;
				succ_node = X->right;
				X = X->right;
			}
			else if (!X->right)
			{
				del_node = X;
				succ_node = X->left;
				X = X->left;
			}
			else // 双分支情况
			{ 
				// 找到中序的直接后继
				del_node = X->succ();
				succ_node = del_node->right;

				swap(del_node->data, X->data);
				BinTree<T>::fromParentTo(del_node) = succ_node;
			}

			hot_node = del_node->parent;
			if (succ_node) succ_node->parent = hot_node;
			delete del_node;
			return succ_node;
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

07.code BST

# pragma once
# include "BinTree.h"

template <typename T>
void swap(T & a, T & b)
{
	T t;
	t = a;
	a = b;
	b = t;
}

template <typename T>
class BST : public BinTree<T> {
	public:

		//***********************************************************查找*********************************************************

		virtual BinNode<T> * & search(T const & val)
		{
			return searchIn(BinTree<T>::root, hot = NULL, val);
		}

		static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val)
		{
			if (!rt || rt->data == val) return rt; // 返回的是引用

			hot_node = rt; //在递归中随深度不断更新

			if (val < rt->data) return searchIn(rt->left, hot_node, val);
			else return searchIn(rt->right, hot_node, val);
		}

		//***********************************************************插入*********************************************************

		virtual BinNode<T> * insert(T const & val)
		{
			
			BinNode<T> * & X = search(val); //为了找到插入位置
			if (!X)
			{
				X = new BinNode<T>(val, hot); //这一句话将两个关系连接

				// 不要忘记
				BinTree<T>::size++;
				BinTree<T>::updateHighAbove(hot);
			}
			return X;
		}

		//***********************************************************删除*********************************************************

		virtual bool remove(T const & val)
		{
			BinNode<T> * & X = search(val);
			if (!X) //树里没有val
			{
				return false;
			}
			else
			{
				removeAt(X, hot);
				BinTree<T>::size--;
				BinTree<T>::updateHighAbove(hot);
				return true;
			}
		}

		BinNode<T> * removeAt(BinNode<T> * & X, BinNode<T> * & hot_node) //注意!!!这个引用
		{
			// hot_node指向要被删除的父亲
			BinNode<T> * del_node; // 实际要被删除的节点
			BinNode<T> * succ_node; // 实际要被删除的节点的接替者

			if (!X->left)
			{
				del_node = X;
				succ_node = X->right;
				X = X->right;
			}
			else if (!X->right)
			{
				del_node = X;
				succ_node = X->left;
				X = X->left;
			}
			else // 双分支情况
			{ 
				// 找到中序的直接后继
				del_node = X->succ();
				succ_node = del_node->right;

				swap(del_node->data, X->data);
				BinTree<T>::fromParentTo(del_node) = succ_node;
			}

			hot_node = del_node->parent;
			if (succ_node) succ_node->parent = hot_node;
			delete del_node;
			return succ_node;
		}

		//******************************************************统一重平衡算法****************************************************

		// 在AVL中有极大用处

		BinNode<T> * connect34(BinNode<T> *, BinNode<T> *, BinNode<T> *, BinNode<T> *, BinNode<T> *, BinNode<T> *, BinNode<T> *);
		BinNode<T> * rotateAt(BinNode<T> * x);

	protected:
		BinNode<T> * hot; // 命中节点的父亲
};


template <typename T>
BinNode<T> * BST<T>::connect34(BinNode<T> * a, BinNode<T> * b, BinNode<T> * c, BinNode<T> * T1, BinNode<T> * T2, BinNode<T> *T3, BinNode<T> * T4)
{
	b->left = a;  b->right = c;
	a->left = T1; a->right = T2;
	c->left = T3; c->right = T4;

	a->parent = b; c->parent = b;

	if (T1) T1->parent = a;
	if (T2) T2->parent = a;
	if (T3) T3->parent = c;
	if (T4) T4->parent = c;
	a->updateHigh(); b->updateHigh(); c->updateHigh();
	return b;
}

template <typename T>
BinNode<T> * BST<T>::rotateAt(BinNode<T> * v)
{
	BinNode<T> * p = v->parent;
	BinNode<T> * g = p->parent;
	BinNode<T> * T1, *T2, *T3, *T4, *a, *b, *c;

	if (p == g->left && v == p->left)
	{
		a = v; b = p; c = g;
		T1 = v->left; T2 = v->right; T3 = p->right; T4 = g->right;
	}
	else if (p == g->left && v == p->right)
	{
		a = p; b = v; c = g;
		T1 = p->left; T2 = v->left; T3 = v->right; T4 = g->right;
		
	}	
	else if (p == g->right && v == p->left)
	{
		a = g; b = v; c = p;
		T1 = g->left; T2 = v->left; T3 = v->right; T4 = p->right;
	}
	else
	{
		a = g; b = p; c = v;
		T1 = g->left; T2 = p->left; T3 = v->left; T4 = v->right;
	}
	b->parent = g->parent; //向上链接
	return connect34(a, b, c, T1, T2, T3, T4);

}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165

谢谢观看~

树的后续

C++数据结构之平衡二叉搜索树(一)——AVL的实现(zig-zag/左右双旋/3+4重构)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/572343
推荐阅读
相关标签
  

闽ICP备14008679号