二叉搜索树上的基本操作花费的时间与这棵树的高度成正比,对于有n个节点的搜索二叉树平均高度O(lg n),但是这样的情况并不能保证。
1)、二叉搜索树(BST)性质:
1. 若任意根节点存在左子树,则左子树的所有节点均小于等于根节点。
2. 若任意根节点存在右子树,则右子树的所有节点均大于等于根节点。
3. 子树同样是二叉搜索树。
搜索树的实现包括:插入操作、查找操作、删除操作、遍历操作。
2)、二叉搜索树的分块实现:
①、结构体搭建
//创建二叉树结构体 ,左孩子, 右孩子, 父节点,实例化操作;
struct STreeNode {
//成员
TreeKeyType key;
pSTreeNode pLeftChild;
pSTreeNode pRightChild;
//实例化操在这里插入代码片作
STreeNode (TreeKeyType Value){
key = Value;
pLeftChild = NULL;
pRightChild = NULL;
}
};
②、创建一个二叉搜索数的类,用来管理独有的方法
//创建类来管理操作方法
class CBinTree {
public :
CBinTree();;
~CBinTree();
void Insert(TreeKeyType Value);
void Insert(pSTreeNode pNode, TreeKeyType Value);
pSTreeNode Search(TreeKeyType Value);
pSTreeNode Search(pSTreeNode pNode, TreeKeyType Value);
void Delete(TreeKeyType Value);
pSTreeNode getSuccessor(pSTreeNode Value); //删除操作
void Preorder(); // 前序遍历,非递归方法(借用堆栈)
void Inorder(); // 中序遍历,非递归方法(借用堆栈)
void Postorder(); // 后序遍历,非递归方法(借用堆栈)
void PreorderRecursively(pSTreeNode pNode); // 前序遍历,递归调用
void InorderRecursively(pSTreeNode pNode); // 中序遍历,递归调用
void PostorderRecursively(pSTreeNode pNode); // 后序遍历,递归调用
pSTreeNode GetMaxKey(); // 获得二叉查找树中元素值最大的节点
pSTreeNode GetMinKey(); // 获得二叉查找树中元素值最小的节点
public:
pSTreeNode pRoot; //定义一个根节点
};
③、插入
{15, 3, 20, 8, 10} 是如何顺序插入的,在纸上实现一遍以自己加深记忆。
当我想插入10这个元素时,需要根节点和val = 10 进行比较;
if(node - > 左孩子存在)
if (val < node.val) {
将下一个左孩子作为根节点继续比较 ;
}
if(node - > 右孩子存在)
if (val > node.val) {
将下一个右孩子作为根节点继续比较 ;
}
程序如下
void CBinTree::Insert(pSTreeNode pNode, TreeKeyType Value) {
//目前的节点数据大于输入值
if (pNode->key > Value)
{
//如果左孩子空了,插入左孩子处,否则进入递归调用
if (pNode->pLeftChild == NULL)
pNode->pLeftChild = new STreeNode(Value);
else
Insert(pNode->pLeftChild, Value);
}else {
if (pNode->pRightChild == NULL)
//找到叶子节点就进行插入
pNode->pRightChild = new STreeNode(Value);
else {
Insert(pNode->pRightChild, Value);
}
}
}
④、查找操作
查找操作最为简单,从根节点开始遍历,val大,向右子树开始继续寻找,否则就向左子树开始继续寻找。这里也用了递归调用的方法,看起来一目了然。
程序如下:
//入口函数
pSTreeNode CBinTree::Search(TreeKeyType Value) {
return Search(pRoot, Value);
}
pSTreeNode CBinTree::Search(pSTreeNode pNode,TreeKeyType Value) {
if (pNode == NULL) {
return NULL;
}
if (pNode->key == Value) {
return pNode;
}else {
if (pNode->key < Value) {
return Search(pRoot->pRightChild, Value);
}else {
return Search(pRoot->pLeftChild, Value);
}
}
}
⑤、删除操作
二叉树的删除操作最为麻烦,再删除之前,对着搜索二叉树的图进行试探,并且从其他大佬们那学到的,删除操作最好考虑全部的可能性,之后再看是否能优化:
1.删除叶子节点;
2.删除节点只有单独的右子树;
3.删除节点只有单独的左子树;
4.删除节点左子树右子树均存在;
首先找到需要删除的节点pFindNode,并定义一个pParentNode变量来标记删除节点的父节点。
情况1,如果删除的时叶子节点,需要将标记的pParentNode直接指向NULL即可删除,结束后释放pFindNode内存;
情况2,3,只存在单独的子树,当删除节点pFindNode不存在左子树的情况下,其右子树均大于pFindNode,那么,如果pFindNode是父节点pParentNode的左孩子,说明下面的一切都比父节点小,请都归顺于pParentNode,成为他的左子树吧,因为你们都比他小,同理删除节点是父节点的有孩子,那么全部归顺为右子树。
当删除节点pFindNode不存在右子树的情况下,其左子树均小于pFindNode,那么,如果pFindNode是父节点pParentNode的左孩子,说明下面的一切都比父节点小,请都归顺于pParentNode,成为他的左子树吧,因为你们都比他小。
情况4,删除的节点同时拥有左右子树,则重点是找到删除元素的后继节点,将后继节点(刚好比pFindNode大的数)为首,重新构造搜索二叉树子树,再将其衔接到pParentNode节点下。
如果你理解了删除的内涵,你会发现,情况4的复杂在于后继节点不能直接继承,需要找到间接继承人。
如图,后继点 9 一定是没有左子树的(因为他就是最左的),他的右子树,此时需要继承在sucParent的左子树下,因为他是恒小于sucParent的。
//删除操作,二叉树删除操作最为麻烦,起码要考虑四种方式
//1.叶子节点2.单独的左子树3.单独的右子树3.左右子树都存在
void CBinTree::Delete(TreeKeyType Value) {
pSTreeNode pParentNode = pRoot;
pSTreeNode pFindNode = pRoot;
// 找到Value元素对应的节点
while (pFindNode != NULL)
{
if (pFindNode->key == Value)
break;
pParentNode = pFindNode;
if (pFindNode->key > Value)
pFindNode = pFindNode->pLeftChild;
else
pFindNode = pFindNode->pRightChild;
}
if (pFindNode == NULL)
return;
// ‘独生子家庭’ 和 ‘丁克’同时讨论,这两种轻情况优化再一起,通过一个pTemp来存储后继节点的头
if (pFindNode->pLeftChild == NULL || pFindNode->pRightChild == NULL)
{
// 一个子结点为空或者两个子结点都为空
pSTreeNode pTemp = NULL;
if (pFindNode->pLeftChild != NULL)
pTemp = pFindNode->pLeftChild;
else if (pFindNode->pRightChild != NULL)
pTemp = pFindNode->pRightChild;
if (pParentNode->pLeftChild == pFindNode)
pParentNode->pLeftChild = pTemp;
else
pParentNode->pRightChild = pTemp;
delete pFindNode;
pFindNode = NULL;
}
else
{
// 删除这边主要是找到后继节点作为交换节点,其他的重新构成一个树
// 再整体进行继承
pSTreeNode successor = getSuccessor(pFindNode);
if (pFindNode == pRoot) {
pRoot = successor;
}else if (pFindNode == pParentNode->pLeftChild) {
pParentNode->pLeftChild = successor;
}else {
pParentNode->pRightChild = successor;
}
successor->pLeftChild = pFindNode->pLeftChild;
}
}
pSTreeNode CBinTree::getSuccessor(pSTreeNode delNode) {
pSTreeNode pTemp = delNode->pRightChild; //此处定义右孩子是为了找到后继节点
//所谓的后继节点其实就是右孩子的最左孩子---**比删除节点大的最小节点**;
pSTreeNode sucParent = NULL;
pSTreeNode successor = pTemp;
while (pTemp != NULL) {
//找到后继节点并建立新的索引
sucParent = successor;
successor = pTemp;
pTemp = pTemp->pLeftChild;
}
if (successor != delNode->pRightChild) {
sucParent->pLeftChild = successor->pRightChild;//都是比父节点小的,左边分支的右子树永远比这个小根节点小
successor->pRightChild = delNode->pRightChild;
}
//如果右孩子本来就和删除元素相等就不用变化了
return successor;
}
⑥、获取最值操作
搜做二叉树的获取最值只与其深度有关,这也是为甚么平衡搜索二叉树的出现原因。
1.最右即为最大;
2.最左即为最小;
程序如下:
pSTreeNode CBinTree::GetMaxKey() {
pSTreeNode pMax = pRoot;
while (pMax->pLeftChild != NULL) {
if (pMax->pLeftChild != NULL)
pMax = pMax->pLeftChild;
}
return pMax;
}
pSTreeNode CBinTree::GetMinKey() {
pSTreeNode pMin = pRoot;
while (pMin->pRightChild != NULL) {
if (pMin->pRightChild != NULL)
pMin = pMin->pRightChild;
}
return pMin;
}
⑦、二叉树的遍历实现
递归实现先序遍历不谈,只说一点
在这里插入图片描述
对于递归上图所示的二叉树实际内部访问顺序为:
F C A A A C D B B B D D C F E H H H E G M M M G G E F
先序遍历 Preorder 实际上是递归顺序中每个节点的第一次出现的顺序;
中序遍历 Inorder 实际上是递归顺序中每个节点的第二次出现的顺序;
后续遍历Postorder实际上是递归顺序中每个节点的第三次出现的顺序;
递归实现程序如下:
//递归试前序遍历实现输出
void CBinTree::PreorderRecursively(pSTreeNode pNode) {
if (pNode == NULL)
return;
cout << "" << pNode->key << " ";
PreorderRecursively(pNode->pLeftChild);
PreorderRecursively(pNode->pRightChild);
}
//递归调用实现中序遍历
void CBinTree::InorderRecursively(pSTreeNode pNode) {
if (pNode == NULL)
return;
InorderRecursively(pNode->pLeftChild);
cout << "" << pNode->key << " ";
InorderRecursively(pNode->pRightChild);
}
//递归调用实现后续遍历
void CBinTree::PostorderRecursively(pSTreeNode pNode) {
if (pNode == NULL)
return;
PostorderRecursively(pNode->pLeftChild);
PostorderRecursively(pNode->pRightChild);
cout << "" << pNode->key << " ";
}
3)主程序清单
#include <iostream>
#include <stack>
using namespace std;
typedef int TreeKeyType;
typedef struct STreeNode* pSTreeNode;
//创建二叉树结构体 ,左右父
struct STreeNode {
//成员
TreeKeyType key;
pSTreeNode pLeftChild;
pSTreeNode pRightChild;
//实例化操作
STreeNode (TreeKeyType Value){
key = Value;
pLeftChild = NULL;
pRightChild = NULL;
}
};
//创建类来管理操作方法
class CBinTree {
public :
CBinTree();;
~CBinTree();
void Insert(TreeKeyType Value);
void Insert(pSTreeNode pNode, TreeKeyType Value);
pSTreeNode Search(TreeKeyType Value);
pSTreeNode Search(pSTreeNode pNode, TreeKeyType Value);
void Delete(TreeKeyType Value);
void Preorder(); // 前序遍历,非递归方法(借用堆栈)
void Inorder(); // 中序遍历,非递归方法(借用堆栈)
void Postorder(); // 后序遍历,非递归方法(借用堆栈)
void PreorderRecursively(pSTreeNode pNode); // 前序遍历,递归调用
void InorderRecursively(pSTreeNode pNode); // 中序遍历,递归调用
void PostorderRecursively(pSTreeNode pNode); // 后序遍历,递归调用
pSTreeNode GetMaxKey(); // 获得二叉查找树中元素值最大的节点
pSTreeNode GetMinKey(); // 获得二叉查找树中元素值最小的节点
pSTreeNode getSuccessor(pSTreeNode Value);
void FreeMemory(pSTreeNode pNode); // 释放内存
public:
pSTreeNode pRoot;
};
CBinTree::CBinTree()
{
pRoot = NULL;
}
CBinTree::~CBinTree()
{
if (pRoot == NULL)
return;
//FreeMemory(pRoot);
}
void CBinTree::Insert(TreeKeyType Value) {
if (pRoot == NULL) {
pRoot = new STreeNode(Value);
}
else {
Insert(pRoot, Value);
}
}
void CBinTree::Insert(pSTreeNode pNode, TreeKeyType Value) {
//目前的节点数据大于输入值
if (pNode->key > Value)
{
//如果左孩子空了,插入左孩子处,否则进入递归调用
if (pNode->pLeftChild == NULL)
pNode->pLeftChild = new STreeNode(Value);
else
Insert(pNode->pLeftChild, Value);
}else {
if (pNode->pRightChild == NULL)
//找到叶子节点就进行插入
pNode->pRightChild = new STreeNode(Value);
else {
Insert(pNode->pRightChild, Value);
}
}
}
//search
pSTreeNode CBinTree::Search(TreeKeyType Value) {
return Search(pRoot, Value);
}
pSTreeNode CBinTree::Search(pSTreeNode pNode,TreeKeyType Value) {
if (pNode == NULL) {
return NULL;
}
if (pNode->key == Value) {
return pNode;
}else {
if (pNode->key < Value) {
return Search(pRoot->pRightChild, Value);
}else {
return Search(pRoot->pLeftChild, Value);
}
}
}
//删除操作,二叉树删除操作最为麻烦,起码要考虑四种方式
//1.叶子节点2.单独的左子树3.单独的右子树3.左右子树都存在
void CBinTree::Delete(TreeKeyType Value) {
pSTreeNode pParentNode = pRoot;
pSTreeNode pFindNode = pRoot;
// 找到Value元素对应的节点
while (pFindNode != NULL)
{
if (pFindNode->key == Value)
break;
pParentNode = pFindNode;
if (pFindNode->key > Value)
pFindNode = pFindNode->pLeftChild;
else
pFindNode = pFindNode->pRightChild;
}
if (pFindNode == NULL)
return;
// 处理Value元素的父节点和Value元素的节点
if (pFindNode->pLeftChild == NULL || pFindNode->pRightChild == NULL)
{
// 一个子结点为空或者两个子结点都为空
pSTreeNode pTemp = NULL;
if (pFindNode->pLeftChild != NULL)
pTemp = pFindNode->pLeftChild;
else if (pFindNode->pRightChild != NULL)
pTemp = pFindNode->pRightChild;
if (pParentNode->pLeftChild == pFindNode)
pParentNode->pLeftChild = pTemp;
else
pParentNode->pRightChild = pTemp;
delete pFindNode;
pFindNode = NULL;
}
else
{
// 删除这边主要是找到后继节点作为交换节点,其他的重新构成一个树
//整体进行替换
pSTreeNode successor = getSuccessor(pFindNode);
if (pFindNode == pRoot) {
pRoot = successor;
}else if (pFindNode == pParentNode->pLeftChild) {
pParentNode->pLeftChild = successor;
}else {
pParentNode->pRightChild = successor;
}
successor->pLeftChild = pFindNode->pLeftChild;
}
}
pSTreeNode CBinTree::getSuccessor(pSTreeNode delNode) {
pSTreeNode pTemp = delNode->pRightChild;
pSTreeNode sucParent = NULL;
pSTreeNode successor = pTemp;
while (pTemp != NULL) {
//找到后继节点并建立新的索引
sucParent = successor;
successor = pTemp;
pTemp = pTemp->pLeftChild;
}
if (successor != delNode->pRightChild) {
sucParent->pLeftChild = successor->pRightChild;//都是比父节点小的,左边分支的右子树永远比这个小根节点小
successor->pRightChild = delNode->pRightChild;
}
return successor;
}
pSTreeNode CBinTree::GetMaxKey() {
pSTreeNode pMax = pRoot;
while (pMax->pLeftChild != NULL) {
if (pMax->pLeftChild != NULL)
pMax = pMax->pLeftChild;
}
return pMax;
}
pSTreeNode CBinTree::GetMinKey() {
pSTreeNode pMin = pRoot;
while (pMin->pRightChild != NULL) {
if (pMin->pRightChild != NULL)
pMin = pMin->pRightChild;
}
return pMin;
}
//非递归算法实现先序遍历
void CBinTree::Preorder() {
if (pRoot == NULL) {
cout << "二叉树为空" << endl;
return;
}
stack<pSTreeNode> StackTree;
pSTreeNode pNode = pRoot;
while (!StackTree.empty() || pNode != NULL) {
while (pNode != NULL) {
cout << " " << pNode->key << " ";
StackTree.push(pNode);
pNode = pNode->pLeftChild;
}
pNode = StackTree.top(); //提出栈顶元素,但是不删除
StackTree.pop();
pNode = pNode->pRightChild;
}
}
//非递归算法实现中序遍历
void CBinTree::Inorder() {
if (pRoot == NULL) {
cout << "二叉树为空" << endl;
return;
}
stack<pSTreeNode> StackTree;
pSTreeNode pNode = pRoot;
while (pNode != NULL || !StackTree.empty()) {
while (pNode != NULL) {
StackTree.push(pNode);
pNode = pNode->pLeftChild;
}
pNode = StackTree.top();
StackTree.pop();
cout << " " << pNode->key << " ";
}
//cout << endl;
}
//非递归算法的后序遍历实现
//将先序遍历的结果存入
void CBinTree::Postorder() {
if (pRoot == NULL) {
cout << "二叉树为空" << endl;
return;
}
stack<pSTreeNode> StackTree;
stack<pSTreeNode> StackHelp;
pSTreeNode pNode = pRoot;
StackTree.push(pNode);
while (!StackTree.empty()) {
pNode = StackTree.top();
StackTree.pop();
StackHelp.push(pNode);
if (pNode->pLeftChild != NULL) {
StackTree.push(pNode->pLeftChild);
}
if (pNode->pRightChild != NULL) {
StackTree.push(pNode->pRightChild);
}
}
while (!StackHelp.empty()) {
pNode = StackHelp.top();
StackHelp.pop();
cout << " " << pNode->key << " ";
}
//cout << endl;
}
//递归试前序遍历实现输出
void CBinTree::PreorderRecursively(pSTreeNode pNode) {
if (pNode == NULL)
return;
cout << "" << pNode->key << " ";
PreorderRecursively(pNode->pLeftChild);
PreorderRecursively(pNode->pRightChild);
}
//递归调用实现中序遍历
void CBinTree::InorderRecursively(pSTreeNode pNode) {
if (pNode == NULL)
return;
InorderRecursively(pNode->pLeftChild);
cout << "" << pNode->key << " ";
InorderRecursively(pNode->pRightChild);
}
//递归调用实现后续遍历
void CBinTree::PostorderRecursively(pSTreeNode pNode) {
if (pNode == NULL)
return;
PostorderRecursively(pNode->pLeftChild);
PostorderRecursively(pNode->pRightChild);
cout << "" << pNode->key << " ";
}
int main() {
CBinTree* pBinTree = new CBinTree();
if (pBinTree == NULL)
return 0;
// 1.实现插入数据,存储成搜索二叉树的形式
pBinTree->Insert(15);
pBinTree->Insert(3);
pBinTree->Insert(20);
pBinTree->Insert(8);
pBinTree->Insert(10);
pBinTree->Insert(18);
pBinTree->Insert(6);
pBinTree->Insert(1);
pBinTree->Insert(26);
//数据已经插入进去了,现在前序遍历一遍
pSTreeNode pRoot = pBinTree->pRoot;
cout << "递归先序遍历为:" ;
pBinTree->PreorderRecursively(pRoot);
cout << endl;
cout << "非递归先序遍历为:";
pBinTree->Preorder();
cout << endl;
cout << "递归中序遍历为:";
pBinTree->InorderRecursively(pRoot);
cout << endl;
cout << "非递归中序遍历为:";
pBinTree->Inorder();
cout << endl;
cout << "递归后序遍历为:";
pBinTree->PostorderRecursively(pRoot);
cout << endl;
cout << "非递归后序遍历为:";
pBinTree->Postorder();
cout << endl;
pSTreeNode pMaxNode = pBinTree->GetMaxKey();
pSTreeNode pMinNode = pBinTree->GetMinKey();
cout <<"输出最大值为:" <<pMaxNode->key;
cout << "输出最小值为:" << pMinNode->key;
cout << endl;
int DeleteKey = 8;
pBinTree->Delete(DeleteKey);
cout << "删除元素" << DeleteKey << "之后的递归前序遍历:";
pBinTree->PreorderRecursively(pRoot);
cout << endl;
}s
这篇程序是从大佬那学习来的,为了加深自己理解,在此总结记录,新手,有错请见谅。
参考文献
/https://www.cnblogs.com/Renyi-Fan/p/8253136.html/
/https://blog.csdn.net/lining0420/article/details/76167901/