高阶数据结构与算法 | 二叉搜索树的实现

二叉搜索树

二叉搜索树又叫做二叉排序树,具有以下特性:

  1. 二叉搜索树的左孩子比父节点小,右孩子比父节点大
  2. 二叉搜索树的左子树的全部节点都小于根节点,右子树的全部节点都大于根节点
  3. 所有节点的左右子树都为二叉搜索树
  4. 键值是唯一的,所以二叉搜索树不能有相同的键值

根据使用场景的不同,二叉搜索树还分为K模型和KV模型:

K模型:即只有key作为关键码,只需要存储Key即可(底层实际存放的是<value,value>键值对),关键码为需要搜索到的值,如STL中的set,multiset

V模型: 每一个关键码key,都有与之对应的值value,即<key,value>键值对.如STL中的map,multimap

二叉搜索树的作用:

1.  排序:

中序遍历:先遍历左子树,再遍历根节点,再遍历右子树.

二叉搜索树的特性:左子树小于根节点,右子树大于根节点

所以通过一次中序遍历,即可以获得排序的结果.

2. 查找:

对于二叉搜索树,查找是其主要的功能,STL中的map和set底层也是通过平衡二叉搜索树(红黑树)实现的.

二叉搜索树的查找十分简单,键值比根节点大则进入右子树,键值比根节点小则进入左子树,它的思路有点类似二分查找,平均时间复杂度为0(log2N)

但是上述情况仅限于二叉搜索树是一个完全二叉树,如果构建时树为有序数列,则二叉搜索树会退化为单支树,时间复杂度则会变成O(N)

二叉搜索树的实现思路: 

节点数据结构:节点值,左右节点

template<class T>
struct BSTNode {
	T _val;
	BSTNode<T>* _left;
	BSTNode<T>* _right;
	BSTNode(const T& val = T()) 
		:_val(val)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

查找:

查找是二叉搜索树的核心,插入以及删除操作都可以复用这里的代码

思路:直接从根节点出发,比根节点大则查找右子树,比根节点小则查找左子树,相同则返回.如果遍历完还没有找到,则说明不存在此树中,返回nullptr

Node* Find(const K& key)
{
	//根据二叉搜索树的性质,从根节点出发,比根节点大则查找右子树,比根节点小则查找左子树
	Node* cur = _root;

	while (cur){
		//比根节点大则查找右子树
		if (key > cur->_key)
		{
			cur = cur->_right;
		}
		//比根节点小则查找左子树
		else if (key < cur->_key)
		{
			cur = cur->_left;
		}
		//相同则返回
		else
		{
			return cur;
		}
	}

	//遍历完则说明查找不到,返回false
	return nullptr;
}

 插入:

先找到需要插入的位置(为了能够保证找到父节点,还需要用一个指针更新父节点)

找到合适的位置后,需要判断当前的键值与父节点的大小,进而判断出应该查到父节点的左子树还是右子树.

	//插入:查找+插入
	bool insert(const T& val) {
		if (_root == nullptr) {
			_root = new Node(val);
            return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur) {
			parent = cur;
			if (cur->_val == val) {
				return false;
			}
			else if (cur->_val > val) {
				cur = cur->_left;
			}
			else {
				cur = cur->_right;
			}
		}
		cur = new Node(val);
		//找到合适的位置
		if(parent->_val < val){
			parent->_right = cur;
		}
		else {
			parent->_left = cur;
		}
		return true;
	}

 删除:

删除操作相对起来会比较复杂,因为对于非叶子节点,删除节点就会导致二叉搜索树的结构破坏,所以不能直接删除,需要找到一个节点替换原有节点,再将原有节点删除.

删除分为三种情况:

  1. 删除叶子节点:直接删除
  2. 删除的节点只有一个子树:如果删除的节点只有一个子树,那么删除这个节点后,就让父节点指向他的这一子树(前两种情况可以合并处理)
  3. 删除的节点有左右子树:选择一个节点来替换它后,再删除,就可以不破坏原有结构.如果要保持原有结构不变化,那么选择的节点必须要和删除节点在中序遍历中是连续的,而满足的只有两个节点,一个是其左子树的最大值,一个是其右子树的最小值。

//删除
	bool erase(const T& val) {
		//查找
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur) {
            parent = cur;
			if (cur->_val == val)
				break;
			else if (cur->_val > val){
				cur = cur->_left.;
			}
			else {
				cur = cur->_right;
			}
		}
		if (cur == nullptr) {
			return false;
		}
		//删除:
        //如果删除节点为叶子节点:
		if (cur->_left == nullptr && cur->_right == nullptr) {
			if (cur == _root)
				_root = nullptr;
			else {
				if (parent->_left == cur)
					parent->_left = nullptr;
				else
					parent->_right = nullptr;
			}
			delete cur;
		}
        //删除的节点只有一个子树:
		//左节点为空,度为1
		else if (cur->_left == nullptr) {
			if (cur == _root) {
				_root = cur->_right;
			}
			else {
				if (parent->_left == cur) {
					parent->_left = cur->_right;
				}
				else {
					parent->_right = cur->_right;
				}
			}
			delete cur;
		}
        //
		//右节点为空,度为1
		else if (cur->_right == nullptr) {
			if (cur == _root) {
				_root = cur->_left;
			}
			else {
				if (parent->_left == cur) {
					parent->_left = cur->_left;
				}
				else {
					parent->_right = cur->_left;
				}
			}
			delete cur;
		}
        //左右子树都不为空:
		else {
			Node* leftMostChild = cur->_right; //删除节点右子树的最左节点
			Node* parent = cur; //删除节点
			//找右子树的最左结点
			while (leftMostChild->_left) {
				parent = leftMostChild;
				leftMostChild = leftMostChild->_left;
			}
			cur->_val = leftMostChild->_val;
			//删除右子树最左结点
			if (parent->_right == leftMostChild)
				parent->_right = leftMostChild->_right;
			else
				parent->_left = leftMostChild->_right;
			delete leftMostChild;
		}
		return true;
	 }

 整体代码实现:


#include <iostream>
using namespace std;
 
template<class T>
struct BSTNode {
	T _val;
	BSTNode<T>* _right;
	BSTNode<T>* _left;
 
	BSTNode(T val)
		:_val(val)
		, _right(nullptr)
		, _left(right)
	{}
};
 
template<class T>
class BSTree {
public:
	typedef BSTNode<T> Node;
 
	//查找某个值在二叉树中是否存在
	Node* find(const T& val) {
		Node* cur = _root;
		while (cur) {
			if (cur->_val == val) {
				return cur;
			}
			else if (cur->_val > val) {
				cur = cur->_left;
			}
			else {
				cur = cur->_right;
			}
		}
		return nullptr;
	}
	//二叉搜索树插入节点
	bool insert(const T& val) {
		if (_root == nullptr) {
			_root = new Node(val);
			return true;
		}
 
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur) {
			parent = cur;
			if (cur->_val == val) {
				return false;
			}
			else if (cur->_val > val) {
				cur = cur->_left;
			}
			else {
				cur = cur->_right;
			}
		}
		//找到插入的位置,执行插入操作
		cur = new Node(val);
		if (val < parent->_val) {
			parent->_left = cur;
		}
		else {
			parent->_right = cur;
		}
		return true;
	}
	//删除
	bool erase(const T& val) {
		//查找
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur) {
			if (cur->_val == val)
				break;
			else if (cur->_val > val){
				parent = cur;
				cur = cur->_left.;
			}
			else {
				parent = cur;
				cur = cur->_right;
			}
		}
		if (cur == nullptr) {
			return false;
		}
		//删除:
		if (cur->_left == nullptr && cur->_right == nullptr) {
			if (cur == _root)
				_root = nullptr;
			else {
				if (parent->_left == cur)
					parent->_left = nullptr;
				else
					parent->_right = nullptr;
			}
			delete cur;
		}
		//左节点为空,度为1
		else if (cur->_left == nullptr) {
			if (cur == _root) {
				_root = cur->_right;
			}
			else {
				if (parent->_left == cur) {
					parent->_left = cur->_right;
				}
				else {
					parent->_right = cur->_right;
				}
			}
			delete cur;
		}
		//右节点为空,度为1
		else if (cur->_right == nullptr) {
			if (cur == _root) {
				_root = cur->_left;
			}
			else {
				if (parent->_left == cur) {
					parent->_left = cur->_left;
				}
				else {
					parent->_right = cur->_left;
				}
			}
			delete cur;
		}
		else {
			Node* leftMostChild = cur->_right;
			Node* parent = cur;
			//找右子树的最左结点
			while (leftMostChild->_left) {
				parent = leftMostChild;
				leftMostChild = leftMostChild->_left;
			}
			cur->_val = leftMostChild->_val;
			//删除右子树最左结点
			if (parent->_right == leftMostChild)
				parent->_right = leftMostChild->_right;
			else
				parent->_left = leftMostChild->_right;
			delete leftMostChild;
		}
		return true;
	 }
	//按顺序输出:提供给外界的接口,因为外界无法访问类的私有成员_root
	void inorder() {
		_inorder(_root);
		cout << endl;
	}
    //中序遍历
	void _inorder(Node* root) {
		if (root) {
			//中序遍历
			inorder(root->left);
			cout << root->_val << " ";
			_inorder(root->_right);
		}
	}
	void destroy(Node* root) {
		if (root) {
			destroy(root->_left);
			destroy(root->_right);
			delete root;
		}
	}
	~BSTree() {
		destroy(_root);
	}
 
	void copyTree(Node* root) {
		if (root) {
			insert(root->_val);
			copyTree(root->left);
			copyTree(root->_right);
		}
	}
	Node* copyTree2(Node* root) {
		if (root) {
			Node* cur = new Node(root->val);
			cur->_left = copyTree2(root->_left);
			cur->_right = copyTree2(root->_right);
			return cur;
		}
	}
	//构造函数
	BSTree()
		:_root(nullptr)
	{}
	//拷贝构造
	BSTree(const BSTree<T>& bst)
		:_root(nullptr)
	{
		_root = copyTree2(bst._root);
	}

	//赋值运算符重载:现代写法
	//BSTree<T>& operator=(BSTree<T>& bst) {
		//swap(_root, bst._root);
		//return *this;
	//}
 
	BSTree<T>& operator=(const BSTree<T>& bst) {
		if (this != &bst) {
			destroy(_root);
			_root = copyTree2(bst._root);
		}
		return *this;
	}
private:
	Node* _root;
};

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值