【C++自学笔记】深度理解二叉搜索树(模拟实现及源代码C++)

一、二叉搜索树

1、二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一颗空树,有以下性质:

  • 若它的左子树不为空,则左子树上所有结点的值都小于根结点的值;
  • 若它的右子树不为空,则右子树上所有结点的值都大于根结点的值;
  • 它的左右子树也分别为二叉搜索树;

                                              int a[ ] = { 5,3,4,1,7,8,2,6,0,9};

 

2、二叉搜索树操作

1、二叉搜索树的查找

1、查找步骤

若根结点不为空:

  • 如果根节点key == 查找key 返回true;
  • 如果根节点key > 查找key 在其左子树查找;
  • 如果根节点key < 查找key 在其右子树查找;

否则返回false;

2、代码

PNode Find(const T& data) {
		PNode pCur = _pRoot;
		while (pCur) {
			if (pCur->_data == data)
				return _pRoot;
			else if (pCur->_data < data)
				pCur = _pRoot->_pLeft;
			else if (pCur->_data > data)
				pCur = _pRoot->_pRight;
		}
		return nullptr;
}

2、二叉树的插入

1、插入过程:

  • 如果树为空,直接插入;
  • 如果树不为空:
  1. 按照二叉搜索树的性质,查找到插入结点的位置
  2. 插入新节点

2、代码

bool Insert(const T& data) {
		//空树
		if (_pRoot == nullptr) {
			_pRoot = new Node(data);
			return true;
		}
		//树不为空,找到位置
		PNode pCur = _pRoot;
		PNode pParent = nullptr;
		while (pCur) {
			pParent = pCur;
			if (pCur->_data > data) {
				pCur = pCur->_pLeft;
			}
			else if (pCur->_data < data) {
				pCur = pCur->_pRight;
			}
			else {
				return false;
			}
		}
		//插入
		pCur = new Node(data);
		if (data < pParent->_data) {
			pParent->_pLeft = pCur;
		}
		else {
			pParent->_pRight = pCur;
		}
		return true;
	}

3、二叉树的删除

1、首先查找元素是否在二叉树中,如果不存在,则返回,否则要删除的结点存在四种情况:

  • 要删除的结点无孩子结点;
  • 要删除的结点只有左孩子;
  • 要删除的结点只有右孩子;
  • 要删除的结点有左、右孩子;

2、实际上只有三种情况待处理(无孩子结点和只有左或只有右孩子的情况可以合并),真正的删除过程如下:

  • 删除该结点且使被删除结点的双亲结点指向被删除结点的左孩子结点;
  • 删除该结点且使被删除结点的双亲结点指向被删除结点的右孩子结点;
  • 在它的右孩子中寻找中序下的第一个结点(关键码最小),用它的值来填补到被删除的结点中,再来处理该结点的的删除问题;

3、代码

bool Erase(const T & data) {
		if (_pRoot == nullptr)
			return false;
		PNode pCur = _pRoot;
		PNode pParent = nullptr;
		//寻找该结点
		while (pCur) {
			if (pCur->_data > data) {
				pParent = pCur;
				pCur = pCur->_pLeft;
			}
			else if (pCur->_data < data) {
				pParent = pCur;
				pCur = pCur->_pRight;
			}
			else
				break;
		}
		if (pCur == nullptr)
			return false;
		//结点找到后,分情况处理
		//1、要删除的结点只有右孩子
		//2、要删除的结点只有左孩子
		//3、要删除的结点左右孩子都有
		Node* pDelNode = pCur;
		if (pCur->_pLeft == nullptr) {
			if (pCur == _pRoot) {
				_pRoot = pCur->_pRight;
			}
			else {
				if (pCur == pParent->_pLeft) {
					pParent->_pLeft = pCur->_pRight;
				}
				else {
					pParent->_pRight = pCur->_pRight;
				}
			}
		}
		else if (pCur->_pRight == nullptr) {
			if (pCur == _pRoot) {
				_pRoot = pCur->_pLeft;
			}
			else {
				if (pCur == pParent->_pRight) {
					pParent->_pLeft = pCur->_pLeft;
				}
				else {
					pParent->_pRight = pCur->_pLeft;
				}
			}
		}
		else {
			//找到右侧最小的节点来替换,pRet一定没有左孩子!!
			Node* pRet = pCur->_pRight;
			pParent = pCur;
			while (pRet->_pLeft) {
				pParent = pRet;
				pRet = pRet->_pLeft;
			}
			pCur->_data = pRet->_data;
			//如果 pRet 是根结点直接删除
			if (pRet == pParent->_pLeft) {
				pParent->_pLeft = pRet->_pRight;
			}
			else {
				pParent->_pRight = pRet->_pRight;
			}
			pDelNode = pRet;
		}
		delete pDelNode;
		return true;
	}

3、二叉搜索树的性能分析

插入 和 删除 操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

                                  

最优情况下:二叉搜索树为完全二叉树,其平均比较次数为:log2N;

最差情况下:二叉搜索树退化为单支树,其平均比较次数为:N / 2;

4、二叉搜索树的完整构建(C++)

#pragma once

#include<iostream>
#include<queue>
#include<stack>
using namespace std;


template<class T>
struct BSTNode {
	BSTNode(const T& data = T)
		:_pLeft(nullptr)
		, _pRight(nullptr)
		, _data(data)
	{}
	BSTNode<T>* _pLeft;
	BSTNode<T>* _pRight;
	T _data;
};
template<class T>
class BSTree {
	typedef BSTNode<T> Node;
	typedef Node* PNode;
public:
	BSTree()
		:_pRoot(nullptr)
	{}
	~BSTree() {
		_Distory(_pRoot);
	}
	PNode Find(const T& data) {
		PNode pCur = _pRoot;
		while (pCur) {
			if (pCur->_data == data)
				return _pRoot;
			else if (pCur->_data < data)
				pCur = _pRoot->_pLeft;
			else if (pCur->_data > data)
				pCur = _pRoot->_pRight;
		}
		return nullptr;
	}
	bool Insert(const T & data) {
		//空树
		if (_pRoot == nullptr) {
			_pRoot = new Node(data);
			return true;
		}
		//树不为空,找到位置
		PNode pCur = _pRoot;
		PNode pParent = nullptr;
		while (pCur) {
			pParent = pCur;
			if (pCur->_data > data) {
				pCur = pCur->_pLeft;
			}
			else if (pCur->_data < data) {
				pCur = pCur->_pRight;
			}
			else {
				return false;
			}
		}
		//插入
		pCur = new Node(data);
		if (data < pParent->_data) {
			pParent->_pLeft = pCur;
		}
		else {
			pParent->_pRight = pCur;
		}
		return true;
	}
	bool Erase(const T & data) {
		if (_pRoot == nullptr)
			return false;
		PNode pCur = _pRoot;
		PNode pParent = nullptr;
		//寻找该结点
		while (pCur) {
			if (pCur->_data > data) {
				pParent = pCur;
				pCur = pCur->_pLeft;
			}
			else if (pCur->_data < data) {
				pParent = pCur;
				pCur = pCur->_pRight;
			}
			else
				break;
		}
		if (pCur == nullptr)
			return false;
		//结点找到后,分情况处理
		//1、要删除的结点只有右孩子
		//2、要删除的结点只有左孩子
		//3、要删除的结点左右孩子都有
		Node* pDelNode = pCur;
		if (pCur->_pLeft == nullptr) {
			if (pCur == _pRoot) {
				_pRoot = pCur->_pRight;
			}
			else {
				if (pCur == pParent->_pLeft) {
					pParent->_pLeft = pCur->_pRight;
				}
				else {
					pParent->_pRight = pCur->_pRight;
				}
			}
		}
		else if (pCur->_pRight == nullptr) {
			if (pCur == _pRoot) {
				_pRoot = pCur->_pLeft;
			}
			else {
				if (pCur == pParent->_pRight) {
					pParent->_pLeft = pCur->_pLeft;
				}
				else {
					pParent->_pRight = pCur->_pLeft;
				}
			}
		}
		else {
			//找到右侧最小的节点来替换,pRet一定没有左孩子!!
			Node* pRet = pCur->_pRight;
			pParent = pCur;
			while (pRet->_pLeft) {
				pParent = pRet;
				pRet = pRet->_pLeft;
			}
			pCur->_data = pRet->_data;
			//如果 pRet 是根结点直接删除
			if (pRet == pParent->_pLeft) {
				pParent->_pLeft = pRet->_pRight;
			}
			else {
				pParent->_pRight = pRet->_pRight;
			}
			pDelNode = pRet;
		}
		delete pDelNode;
		return true;
	}
	Node* FindMin(Node* pRoot) {
		if (pRoot->_pLeft == nullptr)
			return pRoot;
		Node* pCur = pRoot;
		while (pCur->_pLeft)
			pCur = pCur->_pLeft;
		return pCur;
	}
	//Node* FindMax(PNode pCur) {
	//	if (_pRoot == nullptr)
	//		return nullptr;
	//	pCur = _pRoot;
	//	while (pCur->_pRight)
	//		pCur = pCur->_pRight;
	//	return pCur;
	//}
	//前、中、后序遍历
	void Preorder() {
		_Postorder(_pRoot);
	}
	void Inorder() {
		_Inorder(_pRoot);
	}
	void Postorder() {
		_Postorder(_pRoot);
	}
	//前、中、后序遍历非递归
	void PreorderNor() {
		_PreorderNor(_pRoot);
	}
	void InorderNor() {
		_InorderNor(_pRoot);
	}
	void PostorderNor() {
		_PostorderNor(_pRoot);
	}
	//层序遍历
	void LevelOrder() {
		_LevelOrder(_pRoot);
	}
private:
	//前、中、后递归遍历
	void _Preorder(PNode pRoot) {
		if (pRoot == nullptr)
			return;
		printf("%s", pRoot->_data);
		_Preorder(pRoot->_pLeft);
		_Preorder(pRoot->_pRight);
	}
	void _Inorder(PNode pRoot) {
		if (pRoot == nullptr)
			return;
		_Inorder(pRoot->_pLeft);
		printf("%s", pRoot->_data);
		_Inorder(pRoot->_pRight);
	}
	void _Postorder(PNode pRoot) {
		if (pRoot == nullptr)
			return;
		_Postorder(pRoot->_pLeft);
		_Postorder(pRoot->_pRight);
		printf("%s", pRoot->_data);
	}
	//前、中、后非递归遍历
	void _PreorderNor(PNode pRoot) {
		stack<PNode> s;
		PNode pCur;
		PNode pTop;
		pCur = pRoot;
		if (pCur == nullptr)
			return;
		while (!s.empty()) {
			while (pCur != nullptr) {
				printf("%s", pCur->_data);
				s.push(pCur);
				pCur = pCur->_pLeft;
			}
			pTop = s.top;
			s.pop();

			pCur = pTop->_pRight;
		}
	}
	void _InorderNor(PNode pRoot) {
		stack<PNode> s;
		PNode pCur;
		pCur = pRoot;
		if (pRoot == nullptr)
			return;
		while (!s.empty()) {
			while (pCur != nullptr) {
				s.push(pCur);
				pCur = pCur->_pLeft;
			}
			pCur = s.top();
			s.pop();
			printf("%s", pCur->_data);
			pCur = pCur->_pRight;
		}
	}
	void _PostorderNor(PNode pRoot) {
		stack<PNode> s;
		PNode pCur;
		PNode pTop;
		PNode pLast;
		pCur == pRoot;
		if (pCur == nullptr)
			return;
		while (!s.empty()) {
			while (pCur != nullptr) {
				s.push(pCur);
				pCur = pCur->_pLeft;
			}
			pTop = s.top();
			if (pTop->_pRight == nullptr || pTop->_pRight == pLast) {
				s.pop();
				printf("%s", pTop->_data);
				pLast = pTop;
			}
			else {
				pCur = pTop->_pRight;
			}
		}
	}
	//层序遍历
	void _LevelOrder(PNode pRoot) {
		queue<PNode> q;
		PNode* front;
		if (pRoot == nullptr)
			return;
		q.push(pRoot);
		while (!q.empty()) {
			front = q.front();
			q.pop();
			if (front->_pLeft)
				q.push(front->_pLeft);
			if (front->_pRight)
				q.push(front->_pRight);
			printf("%s", front->_data);
		}
	}
private:
	void _Distory(Node * pRoot) {
		if (pRoot == nullptr)
			return;
		_Distory(pRoot->_pLeft);
		_Distory(pRoot->_pRight);
		delete pRoot;
		pRoot = nullptr;
	}
private:
	PNode _pRoot;
};


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_ClivenZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值