二叉查找树

一、简单介绍:本篇博客主要包含的内容
1、二叉查找树的概念,性质
2、二叉查找树的基本操作
(1)、节点结构介绍
(2)、构造/拷贝构造/运算符重载/树拷贝实现
(3)、插入操作
(4)、查找key的值
(6)、查找key的节点或向下/向上取整的节点
(7)、查找某个key的排名
(8)、获取排名为k的节点数据
(9)、二叉树删除操作
说明:以上操作使用C++的模板结构,主要以递归方式实现,也可以借助栈实现迭代版本的。
3、二叉查找树的完整代码链接

进入主体开始!!
二、二叉树的概念,性质。
1、二叉树是一个高效的查找树,查找效率很高。
2、二叉树键值key值关系:左节点 < 根节点 < 右节点。
3、二叉树中序遍历是一个有序的序列。
4、二叉查找树的插入/查找都以键值key进行比较,选择走那条路径。
5、二叉查找树的时间复杂度:O(N),平局时间复杂度:O(lgN)
6、当数据有序时,二叉树退化成单链表,效率变低。解决方法:使用平衡树或红黑树。

三、二叉树操作实现
1、节点数据结构:

template<class T,class V>
struct BSTNode{
	T _key;
	V _value;
	size_t _count;  //节点作为根节点时对应的节点数目
	BSTNode<T,V> *_left;
	BSTNode<T,V> *_right;
	
	BSTNode(T key,V value)
		:_key(key)
		,_value(value)
		,_count(1)
		,_left(NULL)
		,_right(NULL)
	{}
};
说明:_count用于统计当前节点作为根节点时,节点个数。用于查找某个key的排名或获取某排名的key节点。,若不进行此操作,可不用此项,防止统计出错。

2、节点的构造/拷贝构造/运算符重载/析构

template<class T,class V>
class BSTree{
public:
	typedef struct BSTNode<T,V> BSTNode;

	BSTree()  //构造函数
		:_root(NULL)
	{}

	BSTree(const BSTree &bst)   //拷贝构造
		:_root(NULL)
	{
		_root = _copy(_root,bst._root);  //深拷贝,进行节点复制
	}

	BSTree &operator=(const BSTree &bst) {   //运算符重载
		if (this != &bst) {
			BSTree<T,V> tmp(bst);
			swap(this->_root, tmp._root);  //注:此处交换的是root的地址
		}
		return *this;
	}

	//后序遍历删除
	~BSTree() {
		Delete(_root);
		_root = NULL;
	}
	//树拷贝---前序遍历进行拷贝(递归实现)
	1、根节点为空,则退出。
	2、为节点开辟空间,并拷贝节点的数据。
	3、左/右不为空,节点的左/右进行=递归拷贝左或右子树
	4、返回节点的地址。
	BSTNode* _copy(BSTNode *root, const BSTNode *bst) {
		if (bst == NULL) return NULL;
		root = new BSTNode(bst->_key,bst->_value);
		root->_count = bst->_count;
		
		if (bst->_left ) {
			(root)->_left = _copy(root, bst->_left);
		}
		if (bst->_right) {
			(root)->_right = _copy(root, bst->_right);
		}
		return root;   //返回拷贝后树的根
	}
	
	private:
	BSTNode *_root;
};

3、节点插入操作—递归实现
1、根为空,则创建一个新的节点,并返回。(注:根节点需接收返回值)
2、如果key < 根的key,根的左=递归根的左
3、如果key > 根的key,根的右=递归根的右
4、否则(相等)根的值(value) = value。//注:此处是替换原有key的value
5、计算根的节点个数。节点个数=左子树个数+右子树个数+1(根节点)
6、返回根节点。

void insert(T key, V value) {
	_root = _insert(_root, key, value);
}

BSTNode* _insert(BSTNode* root,T key, V value) {
	if (root == NULL) {
		return new BSTNode(key, value);
	}
	if (root->_key > key) {
		root->_left = _insert(root->_left, key, value);
	}
	else if (root->_key < key) {
		root->_right = _insert(root->_right, key, value);
	}
	else {
		root->_value = value;
	}
	root->_count = Size(root->_left) + Size(root->_right) + 1;
	return root;
}

4、查找键值为key的值value
1、获取根节点
2、循环节不为空
3、节点key==key,则返回节点的value
4、节点key > key, 节点向左遍历.
5、节点key < key, 节点向右遍历
6、退出循环则为找到,返回一个标记 如:-1

//查找
V Find(T key) {
	BSTNode *cur = _root;
	while (cur) {
		if (cur->_key == key) {
			return cur->_value;
		}else if (cur->_key > key) {
			cur = cur->_left;
		} else {
			cur = cur->_right;
		}
	}
	return -1;  //未找到返回-1
}

7、查找key向上/向下取整key的节点
向上取整:取大于key,但最接近key的值。
向下取整:取小于key,但最接近key的值。
1、查找向上取整的数—递归实现
节点为空,则返回NULL,表示未找到数据。
如果节点的key==key,表示找到向等值,返回该节点。
如果节点的key < key,表示,向上取整的数在右子树,return 递归右子树
节点key > key,获取并递归左子树。
如果不为空,则该节点为最接近key的节点,返回退出。为找到,则当前根节点为最接近key的节点,返回该节点退出。

V findNearUp(T key) {
	BSTNode *tmp = find_near_up(_root, key);
	if (tmp)  return tmp->_key;
	else  return -1;  //未找到返回-1
}

BSTNode* find_near_up(BSTNode* root, T key) {
	if (root == NULL) {
		return NULL;
	}
	if (root->_key == key) {  
		return root;
	}
	if (root->_key < key) {  //root->key小于key值,则比key大值在右子树。
		return find_near_up(root->_right, key);
	}  //右子树未找到
	BSTNode *cur = find_near_up(root->_left, key);  //root->key 大与key,或右子树未找到,则在左子树找最接近key的值
	if (cur != NULL) return cur;  //找到相等的值,则返回该节点
	else return root;  //未找到,则根节点为最接近key的值
}

2、向下取整
与向上取整类似,修改比较的符号,和递归的方向即可。

V findNearDown(T key) {
	BSTNode* tmp = find_near_down(_root, key);
	if (tmp) return tmp->_key;
	else return -1;
}

BSTNode* find_near_down(BSTNode *root, T key) {
	if (root == NULL) return NULL;
	if (root->_key == key) {
		return root;
	}
	if (root->_key > key) {  //root->key > key,则最接近小于key的值在左子树
		return find_near_down(root->_left, key);
	}
	BSTNode *cur = find_near_down(root->_right, key);  //root->key < key,最接近key的值在右子树
	if (cur) return cur;  //找到最接近的值
	else return root;  //根为最接近的值
}

8、查找第k大小的节点
节点作为根节点时,该节点的排名即该节点左子树的节点个数(注:从0算起,因为当前节点未统计),
节点排名=节点左子树几点的个数。
右子树节点的排名=左子树节点个数+1+递归该节点的右
1、如果左子数的节点个数 > k,则该节点在左子树
2、如果左子的个数 < k ,则该节点在右子树。即查找排名为 k - 左子树节点个数-1(根节点)

V find_sizeth(int k){
	return _find_sizeth(_root, k - 1);  //统计节点的排名从0算起。所以-1
}

V _find_sizeth(BSTNode* root, int k) {
	if (root == NULL)  return 0;
	
	int size_left = Size(root->_left);
	if (size_left == k) return root->_value;  //与左子树节点个数相同,即排名为k的节点
	if (size_left > k) {
		return _find_sizeth(root->_left, k);
	}
	if (size_left < k) {
		return _find_sizeth(root->_right, k - size_left - 1);
	}
}

9、查找某个key的排名
1、节点为空则返回标志-1,表示未找到该key
2、节点的key == key ,则返回节点左子树的节点个数。
3、节点的key > key,则返回递归在左子树查找。
4、节点的key < key,则返回递归在右子树查找,如果 返回统计结果为-1,则说明未找到key值,返回-1退出。否则找到key值返回 左子树节点个数+1(根)+右子树统计的个数。

//查找key的排名
int rank(T key) {
	return _rank(_root, key)+1;  //此处统计从0算起
}

int _rank(BSTNode *root,T key) {
	if (root == NULL)  return -1;

	if (root->_key < key) {  //右子树节点的排名=左子树节点个数+1(根节点)+右子树
		int size_right = _rank(root->_right, key);
		if (size_right < 0) {  //注:小于0,则说明未找到该数据,直接退出,不再加上之前计算的值。
			return size_right;
		}
		else {
			return Size(root->_left) + 1 + size_right;
		}
	}
	else if(root->_key > key){  //在左子树查找
		return _rank(root->_left, key);
	}
	else {  //key值相同
		return Size(root->_left);  //返回左子树节点个数即为排名
	}
}

10、删除节点
1、删除最小key节点
向左遍历到最左的节点,将其上一个节点指向该节点不为空的节点(右节点)上。删除该节点
注:链接方向与原来的方向保持一致,同时更新size值

void deleteMin() {
	BSTNode *del = NULL;
	_root = _deleteMin(_root,del);  //用于接收,处理删除根节点的情况
	delete del;  //析构删除掉的节点
}
//递归查找删除节点,并从树中移除该节点
BSTNode* _deleteMin(BSTNode *root,BSTNode *&del) {  //此处要修改del指针的指向,所以使用二级指针,或引用
	if (root->_left == NULL) {
		del = root;
		return root->_right;
	}
	root->_left = _deleteMin(root->_left,del);
	root->_count = Size(root->_left) + Size(root->_right) + 1; //重新计算节点个数
	return root;  //返回根节点
}

2、删除任意一个节点
1、删除节点只有左子树/右子树之一,删除节点,并让上一个节点指向不为空的左/右子树,方法同上
2、删除左右子树都不为空的节点:
方法1:在删除节点的左子树中找最最大节点作为替换节点,与删除节点进行数据交换,删除交换节点,方法同删除一个子树节点
方法2:删除节点的右子树最小值替换删除节点,修改替换节点的左右指针域。删除替换节点。

void deleteKey(T key) {
	//_deleteKey(_root, key);
	_deleteKey2(_root, key);
}

//方法2:
//寻找删除节点
BSTNode* _deleteKey2(BSTNode* root, T key) {
	if (root == NULL)  return NULL;
	//递归寻找删除节点
	if (root->_key > key) {
		root->_left = _deleteKey2(root->_left, key);
	}
	else if (root->_key < key) {
		root->_right = _deleteKey2(root->_right, key);
	}
	else {  //找到删除节点,进行删除
		//只有一个子树
		if (root->_left == NULL) {
			return root->_right;
		}
		else if (root->_right == NULL) {
			return root->_left;
		}
		 //左右节点都不为空,具有两子树
		else { 
			BSTNode *del = root;
			del->_right = _deleteMin(del->_right,root);  //找出最小节点,并更新删除节点的右,并摘除,注:并未析构,
			root->_right = del->_right; 
			root->_left = del->_left;
			delete del;
		}
	}
	root->_count = Size(root->_left) + Size(root->_right) + 1;  //更新节点个数
	return root;  //返回替换后的节点,用于接收处理根节点情况。
}

bool _deleteKey(BSTNode *&root, T key) {  //注:存在删除根节点的问题,此处使用二级指针。
	if (root == NULL)  return false;
	//递归查找删除节点
	if (root->_key > key) {
		_deleteKey(root->_left, key);
	}
	else if (root->_key < key) {
		_deleteKey(root->_right, key);
	}
	//找到删除节点
	else {
		BSTNode *tmp = root;
		//只有左/右节点
		if (root->_left == NULL) {
			root = root->_right;
			delete tmp;
			tmp = NULL;
		}
		else if(root->_right == NULL){
			root = root->_left;
			delete tmp;
			tmp = NULL;
		}
		//左/右节点都存在
		else {
			BSTNode* right_min = _keyMin(root->_right);  //查找右子树最小节点,作为替换节点
			_swap(root, right_min);   //交换节点的数据
			_deleteKey(root->_right, right_min->_key);  //删除替换节点
		}
		//更新删除节点处的节点个数
		if (root) {
			root->_count = Size(root->_left) + Size(root->_right) + 1;
		}
		return true;
	}
	//更新父节点的节点个数
	if (root) {
		root->_count = Size(root->_left) + Size(root->_right) + 1;
	}
}

void _swap(BSTNode* left, BSTNode* right) {
	swap(left->_key, right->_key);  //库交换函数
	swap(left->_value, right->_value);
}

11、其它操作

//获取最小key
V keyMin() {
	BSTNode* tmp = _keyMin(_root);
	if (tmp)  return tmp->_key;
	else return -1;
}

BSTNode* _keyMin(BSTNode *root) {
	if (root == NULL) {
		return NULL;
	}
	if (root->_left == NULL) {
		return root;
	}
	else {
		return _keyMin(root->_left);
	}
}

//获取某节点作为根节的时节点个数
int Size(BSTNode *node) {
	if (node) {
		return node->_count;
	}
	return 0;
}

//输出某范围的数据min~max
void minToMax(T min, T max) {
	_minToMax(_root, min, max);
}

void _minToMax(BSTNode *root, T min, T max) {
	if (root == NULL) {
		return;
	}
	if (root->_key > min) {
		_minToMax(root->_left, min, max);
	}
	if (min <= root->_key && max > root->_key) {
		cout << root->_key << endl;
	}
	if (root->_key < max) {
		_minToMax(root->_right, min, max);
	}
}

三、完整代码链接:
https://github.com/weienjun/GetHub/tree/master/BST

有问题欢迎大家指出!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值