虽然写这个博客主要目的是为了给我自己做一个思路记忆录,但是如果你恰好点了进来,那么先对你说一声欢迎。我并不是什么大触,只是一个菜菜的学生,如果您发现了什么错误或者您对于某些地方有更好的意见,非常欢迎您的斧正!
12.1什么是二叉搜索树
一棵二叉搜索树是以二叉树来组织的。除了key和卫星数据之外,每个结点还包含属性left、right和parent,它们分别指向结点的左孩子、右孩子和父亲。根结点是树中唯一父指针为NULL的结点。
这一节内容我看了一下大概就是介绍了二叉树的三种遍历:先序遍历、中序遍历和后序遍历。这三种遍历的具体图解我在另一篇文章中已经写了,有兴趣的可以看一下。这里对于这三种遍历就不详细讲了。如果直接点文字出现的是404,可以选择复制代码。 二叉树的前序、中序、后序遍历(https://blog.csdn.net/weixin_40851250/article/details/82994636)
12.2查询二叉树
我们可以看到二叉树有个很明显的特征:左边的数都比根结点小,右边的数都比根结点大。这一节主要介绍了五个函数:
①查找一个关键字key,找到就返回这个关键字所在的结点的指针,否则返回NULL
②查找最大关键字元素:这个显然,只要一直沿着右儿子遍历知道next指向NULL就可以了
③查找最小关键字元素:只要一直沿着左儿子遍历知道next指向NULL就可以了
④查找前驱元素:
1.若一个节点有左子树,那么该节点的前驱节点是其左子树中val值最大的节点
2.若一个节点没有左子树,那么判断该节点和其父节点的关系
2.1 若该节点是其父节点的右边孩子,那么该节点的前驱结点即为其父节点。
2.2 若该节点是其父节点的左边孩子,那么需要沿着其父亲节点一直向树的顶端寻找,
直到找到一个有右子树的结点Q,那么Q就是该节点的前驱节点
⑤查找后继元素:
1.若一个节点有右子树,那么该节点的后继节点是其右子树中val值最小的节点
2.若一个节点没有右子树,那么判断该节点和其父节点的关系
2.1 若该节点是其父节点的左边孩子,那么该节点的后继结点即为其父节点
2.2 若该节点是其父节点的右边孩子,那么需要沿着其父亲节点一直向树的顶端寻找,
直到找到一个有左孩子的节点Q,那么Q就是该节点的后继节点
下面这张图应该是情况2.2
12.3插入和删除
插入和删除操作会引起由二叉搜索树表示的动态集合的变化。
首先来看一下插入的伪代码:(插一部分,我们看一下BtreeNode* &root),解释一下为什么要有这个&,“这是C++的语法写法,&在形参中表示“引用”实参,LNode * &lst ; 中LNode * 是个整体,表示变量类型是LNode类指针, &lst中的&表明引用实参,即代表实参的一个别名。 ”(取自 kaixingui2012的回答如果链接打不开,可以复制下面的链接)(https://zhidao.baidu.com/question/2266744263935050308.html)
InsertBtreeNode(BtreeNode* &root, BtreeNode* key)
1、y=NULL
2、x=root
3、while x≠NULL /如果根结点不为空/
4、 y=x; /y是x的父结点/
5、 if key.data<x.data /如果key的值小于根结点/
6、 x=x.left /插入到根的左边/
7、 else x=x.right /否则插入到右边/
8、key.parent=y /key的父结点就是y/
9、if y==NULL /表示树是空的/
10、 root=key; /插入的数就是根结点/
11、elseif key.data<y.data /这几行是判断插入到左边还是插入到右边/
12、 y.left=key
13、else y.right=key
再来看一下删除。删除分为四种情况:
①没有孩子:使它为NULL
②只有左孩子:用左孩子代替它
③只有右孩子:用右孩子代表它
④有两个孩子:找到它的后继,用它的后继代替它
这四种情况的图解如下:
这里有一个辅助的子函数:Translant(root,u,v),它的作用是用一棵以v为根的子树来替换一棵以u为根的子树,这个子函数并没有处理v.left和v.right的更新,这些更新都删除函数里进行。我们先来看一下这个子函数的伪代码:
Translant(root,u,v)
1、if u.parent== NULL /如果要替换的是根结点/
2、 root=v
3、elseif u==u.parent.left /如果u是个左孩子/
4、 u.parent.left=v
5、else u.parent.right=v
6、if v≠NULL /如果v非空/
7、 v.parent=u.parent /使v的父结点变为u的父结点/
删除的伪代码:
void DeleteBtreeNode(BtreeNode* &root, BtreeNode* z)
1、if z.left=NULL /如果z没有左孩子,这个情况包括了z没有孩子/
2、 Translant(root,z,z.right) /交换z与z的右孩子/
3、elseif z.right==NULL /z没有右孩子/
4、 Translant(root,z,z.left)
5、else y=MinBtreeNode(z.right) /右子树的最小值,即要删除结点的后继/
6、 if y.parent≠z /后继与要删除结点不是父子关系/
7、 Translant(root,y,y.right)
8、 y.right=z.right
9、 y.right.parent=y
10、 Translant(root,z,y) /让后继代替要删除的结点/
11、 y.left=z.left
12、 y.left.parent=y
12.4随机构建二叉搜索树
这一章,我没怎么看,感觉没什么实质内容,就算有我也看不懂。
接下来是代码部分,建议粘贴到编辑器中自己运行一下:
二叉搜索树.h
#pragma once
/*构建二叉树结点*/
typedef struct BtreeNode
{
int data;/*该结点的数据*/
BtreeNode *left;/*指向左边结点的指针*/
BtreeNode *right;/*指向右边结点的指针*/
BtreeNode *parent;/*指向父结点的指针*/
}BtreeNode;
/*先序遍历*/
void PreBtreeNode(BtreeNode *root);
/*中序遍历*/
void MidBtreeNode(BtreeNode *root);
/*后序遍历*/
void PastBtreeNode(BtreeNode *root);
/*查找:返回找到的结点*/
BtreeNode* SearchBtreeNode(BtreeNode *root,int key);
/*查找最大值*/
BtreeNode* MaxBtreeNode(BtreeNode *root);
/*查找最小值*/
BtreeNode* MinBtreeNode(BtreeNode *root);
/*查找前驱*/
BtreeNode* BeforeBtreeNode(BtreeNode *root);
/*查找后继*/
BtreeNode* AfterBtreeNode(BtreeNode *root);
/*插入*/
void InsertBtreeNode(BtreeNode* &root, BtreeNode* key);
/*子过程:移动子树,用子树v代替子树u*/
void Translant(BtreeNode* &root, BtreeNode* u, BtreeNode* v);
/*删除*/
void DeleteBtreeNode(BtreeNode* &root, BtreeNode* z);
void BtreeTest();
二叉搜索树.cpp
#include "二叉搜索树.h"
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
/*先序遍历*/
void PreBtreeNode(BtreeNode *root)
{
if (root != NULL)
{
cout << root->data << "\t";/*打印这个数的数值*/
PreBtreeNode(root->left);
PreBtreeNode(root->right);
}
}
/*中序遍历*/
void MidBtreeNode(BtreeNode *root)
{
if (root != NULL)
{
MidBtreeNode(root->left);
cout << root->data << "\t";/*打印这个数的数值*/
MidBtreeNode(root->right);
}
}
/*后序遍历*/
void PastBtreeNode(BtreeNode *root)
{
if (root != NULL)
{
PastBtreeNode(root->left);
PastBtreeNode(root->right);
cout << root->data << "\t";/*打印这个数的数值*/
}
}
/*查找:返回找到的结点*/
BtreeNode* SearchBtreeNode(BtreeNode *root, int key)
{
if (root == NULL || root->data == key)/*如果结点为空或者找到就返回这个结点*/
return root;
if (key < root->data)/*如果key小于根的值,就搜索左子树*/
return SearchBtreeNode(root->left, key);
else
return SearchBtreeNode(root->right, key);/*否则搜索右子树*/
/*非递归版本*/
/*while (root != NULL && root->data != key)
{
if (key < root->data)
root = root->left;
else
root = root->right;
}
return root;*/
}
/*查找最大值*/
BtreeNode* MaxBtreeNode(BtreeNode *root)
{
if (root == NULL)
return root;
while (root->right != NULL)
root = root->right;/*不断搜索右子树*/
return root;
}
/*查找最小值*/
BtreeNode* MinBtreeNode(BtreeNode *root)
{
if (root == NULL)
return root;
while (root->left != NULL)
root = root->left;/*不断搜索右子树*/
return root;
}
/*查找前驱*/
BtreeNode* BeforeBtreeNode(BtreeNode *root)
{
/*1、若一个节点有左子树,那么该节点的前驱节点是其左子树中val值最大的节点*/
if (root->left != NULL)
return MaxBtreeNode(root->left);
/*2、若一个节点没有左子树,那么判断该节点和其父节点的关系
2.1 若该节点是其父节点的右边孩子,那么该节点的前驱结点即为其父节点。
2.2 若该节点是其父节点的左边孩子,那么需要沿着其父亲节点一直向树的顶端寻找,
直到找到一个有右子树的结点Q,那么Q就是该节点的前驱节点*/
BtreeNode* parent = root->parent;
while (parent != NULL && parent->left == root)/*root是左孩子*/
{
root = parent;
parent = parent->parent;
}
return parent;
}
/*查找后继*/
BtreeNode* AfterBtreeNode(BtreeNode *root)
{
/*1、1.若一个节点有右子树,那么该节点的后继节点是其右子树中val值最小的节点*/
if (root->right != NULL)
return MinBtreeNode(root->right);
/*2/若一个节点没有右子树,那么判断该节点和其父节点的关系
2.1 若该节点是其父节点的左边孩子,那么该节点的后继结点即为其父节点
2.2 若该节点是其父节点的右边孩子,那么需要沿着其父亲节点一直向树的
顶端寻找,直到找到一个有左孩子的节点Q,那么Q就是该节点的后继节点*/
BtreeNode* parent = root->parent;
while (parent != NULL && parent->right == root)/*root是右孩子*/
{
root = parent;
parent = parent->parent;
}
return parent;
}
/*插入*/
void InsertBtreeNode(BtreeNode* &root, BtreeNode* key)
{
BtreeNode* temp = root;/*建立一个临时指针指向根结点*/
BtreeNode* tempParent = NULL;/*临时节点的父结点*/
while (temp != NULL)/*根结点不为空*/
{
tempParent = temp;
if (key->data < temp->data)/*小于根结点*/
temp = temp->left;
else
temp = temp->right;
}
key->parent = tempParent;
if (tempParent == NULL)/*树是空的*/
root = key;
else if (key->data < tempParent->data)
{
tempParent->left = key;
key->parent = tempParent;
}
else
{
tempParent->right = key;
key->parent = tempParent;
}
}
/*子过程:移动子树,用子树v代替子树u*/
void Translant(BtreeNode* &root, BtreeNode* u, BtreeNode* v)
{
if (u->parent == NULL)/*如果u是根结点*/
root = v;/*v成为根结点*/
else if (u == u->parent->left)/*u是左子树*/
u->parent->left = v;
else
u->parent->right = v;
if (v != NULL)
v->parent = u->parent;
}
/*删除*/
void DeleteBtreeNode(BtreeNode* &root, BtreeNode* z)/*z是要删除的结点*/
{
if (z->left == NULL)/*z没有左子树(也包括了z既没有左子树也没有右子树的情况)*/
Translant(root, z, z->right);/*让z的右子树代替z,如果是第二种,那么交换后z=NULL*/
else if (z->right == NULL)/*z没有右子树*/
Translant(root, z, z->left);/*让z的左子树代替z*/
else/*既有左子树也有右子树*/
{
BtreeNode* temp = MinBtreeNode(z->right);/*寻找z的后继:右子树的最小值*/
if (temp->parent != z)/*如果它的后继的父结点不是z*/
{
Translant(root, temp, temp->right);/*让后继的右子树代替后继这个结点*/
temp->right = z->right;/*这个后继肯定没有左孩子!*/
temp->right->parent = temp;
}
Translant(root, z, temp);/*让后继取代要删除的结点*/
temp->left = z->left;
z->left->parent = temp;
}
}
void BtreeTest()
{
BtreeNode* root = (BtreeNode*)malloc(sizeof(BtreeNode));/*建立二叉树的根节点*/
srand((unsigned)time(NULL));
int i;
/*初始化根节点*/
root->data = rand() % 100 + 1;/*1-100之间的数*/
root->parent = NULL;
root->left = NULL;
root->right = NULL;
BtreeNode *six= (BtreeNode*)malloc(sizeof(BtreeNode));/*第六个结点*/
BtreeNode *temp= (BtreeNode*)malloc(sizeof(BtreeNode));/*临时节点*/
for (i = 0; i < 10; i++)
{
BtreeNode *key=(BtreeNode*)malloc(sizeof(BtreeNode));
key->data = rand() % 200 + 1;/*1-200之间的数*/
key->left = NULL;
key->right = NULL;
key->parent = NULL;
if (i == 6)six = key;
InsertBtreeNode(root, key);
}
cout << "插入10个数字后:" << endl;
cout << "先序遍历:";
PreBtreeNode(root); cout << endl;
cout << "中序遍历:";
MidBtreeNode(root); cout << endl;
cout << "后序遍历:";
PastBtreeNode(root);
cout << endl;
cout << endl;
/*测试最小与最大的数*/
temp = MinBtreeNode(root);
cout << "最小的数是:" << temp->data << endl;
temp = MaxBtreeNode(root);
cout << "最大的数是:" << temp->data << endl;
cout << endl;
/*寻找第六个数的前驱和后继*/
cout << "第六个数是:" << six->data << endl;
temp = BeforeBtreeNode(six);
if (temp != NULL)
cout << "它的前驱是:" << temp->data << endl;
else cout << "NULL!" << endl;
temp = AfterBtreeNode(six);
if (temp != NULL)
cout << "它的后继是:" << temp->data << endl;
else cout << "NULL!" << endl;
cout << endl;
cout << "删除第六个结点:" << endl;
DeleteBtreeNode(root, six);
cout << "先序遍历:";
PreBtreeNode(root);
cout << endl;
}
主函数
#include "二叉搜索树.h"
#include <stdio.h>
int main()
{
BtreeTest();
getchar();
getchar();
return 0;
}
运行结果:
参考网站以及博客:
二叉查找树的前驱后继(https://www.cnblogs.com/Renyi-Fan/p/8252227.html#_label1_0)二叉查找树的删除操作(https://www.cnblogs.com/Renyi-Fan/p/8253136.html)
二叉查找树(二)之 C++的实现(http://www.cnblogs.com/skywang12345/p/3576373.html#aa1)
kaixingui2012的回答(https://zhidao.baidu.com/question/2266744263935050308.html)