目录
1. 二叉排序树(BST) Binary Search Tree
1. 二叉排序树(BST) Binary Search Tree
(1)二叉排序树的插入
int BST_Insert(BSTree& T, ElemType k) {
if (!T) { //原树为空 新插入的结点为根结点
T = (BSNode*)malloc(sizeof(BSNode));
if (!T) { //申请失败
return 0;
}
T->key = k;
T->lchild = T->rchild = NULL;
return 1;//插入成功
}
else if (k == T->key) { //树中存在 值为k 的结点 插入失败
return 0;
}
else if (k < T->key) { //插入到左子树
return BST_Insert(T->lchild, k);
}
else { //插入到右子树
return BST_Insert(T->rchild, k);
}
}
(2)二叉排序树的查找
1. 非递归查找算法 时间复杂度为 O(1)
2. 递归查找算法 时间复杂度为 O(h) (其中h为树的高度)
//在二叉排序树中查找值为 key的结点
//1. 非递归查找算法 时间复杂度为 O(1)
BSNode* BST_Search(BSTree T, ElemType key) {
while (T != NULL && key != T->key) {//若树空或等于根结点值 结束循环
if (key < T->key) {
T = T->lchild; //小于 在左子树上查找
}
else {
T = T->rchild; //大于 在右子树上查找
}
}
return T;
}
//2. 递归查找算法 时间复杂度为 O(h) (其中h为树的高度)
BSNode* BST_Search_Recurrence(BSTree T, ElemType key) {
if (!T) { //树空 查找失败
return NULL;
}
if (key == T->key) { //查找成功
return T;
}
else if (key < T->key) { //在左子树中查找
return BST_Search_Recurrence(T->lchild, key);
}
else { //在右子树中查找
return BST_Search_Recurrence(T->rchild, key);
}
}
(3)二叉排序树的创建
//按照 str[] 中的关键字序列建立二叉排序树
void CreateBST(BSTree& T, ElemType str[], int n) {
T = NULL; //初始时 T 为空树
int i = 0;
while (i < n) { //依次将每个关键字插入到二叉排序树中
BST_Insert(T, str[i]);
i++;
}
}
(4)二叉排序树的删除
在删除节点的时候我们只需考虑一下三种情况:
(1)若被删除结点z是叶结点,则直接删除,不会破坏二叉排序树的性质。
(2)若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,替代z的位置。
![]()
删除前
![]()
删除后 (3)若结点z有左、右两棵子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转换成了第一或第二种情况。
方案一、右子树的最小值替代
![]()
删除前 ![]()
删除后 方案二、左子树的最大值替代
![]()
删除前 ![]()
删除后
(5)完整代码
#include <iostream>
using namespace std;
typedef int ElemType;
typedef struct BSNode {//二叉排序树结点
ElemType key;
struct BSNode* lchild, * rchild;
} BSNode, * BSTree;
typedef struct BiTNode {
ElemType data;//数据域
struct BiTNode* lchild, * rchild; //左 右孩子指针
// struct BiTNode *parent;//父指针
} BiTNode, * BiTree;
//在二叉排序树中查找值为 key的结点
//1. 非递归查找算法 时间复杂度为 O(1)
BSNode* BST_Search(BSTree T, ElemType key) {
while (T != NULL && key != T->key) {//若树空或等于根结点值 结束循环
if (key < T->key) {
T = T->lchild; //小于 在左子树上查找
}
else {
T = T->rchild; //大于 在右子树上查找
}
}
return T;
}
//2. 递归查找算法 时间复杂度为 O(h) (其中h为树的高度)
BSNode* BST_Search_Recurrence(BSTree T, ElemType key) {
if (!T) { //树空 查找失败
return NULL;
}
if (key == T->key) { //查找成功
return T;
}
else if (key < T->key) { //在左子树中查找
return BST_Search_Recurrence(T->lchild, key);
}
else { //在右子树中查找
return BST_Search_Recurrence(T->rchild, key);
}
}
//二叉排序树插入关键字为 k 的新结点(递归实现)
int BST_Insert(BSTree& T, ElemType k) {
if (!T) { //原树为空 新插入的结点为根结点
T = (BSNode*)malloc(sizeof(BSNode));
if (!T) { //申请失败
return 0;
}
T->key = k;
T->lchild = T->rchild = NULL;
return 1;//插入成功
}
else if (k == T->key) { //树中存在 值为k 的结点 插入失败
return 0;
}
else if (k < T->key) { //插入到左子树
return BST_Insert(T->lchild, k);
}
else { //插入到右子树
return BST_Insert(T->rchild, k);
}
}
//按照 str[] 中的关键字序列建立二叉排序树
void CreateBST(BSTree& T, ElemType str[], int n) {
T = NULL; //初始时 T 为空树
int i = 0;
while (i < n) { //依次将每个关键字插入到二叉排序树中
BST_Insert(T, str[i]);
i++;
}
}
//二叉排序树的删除
bool deleteBST(BSTree& T, ElemType value) {
BSNode* pre = NULL;
BSNode* p, * q, * s;
// 找到待删结点 p 以及其前驱结点 pre
p = T;
while (p) {
if (p->key == value) {
break; //找到待删除节点
}
pre = p;
if (p->key > value) {
p = p->lchild;
}
else {
p = p->rchild;
}
}
if (p == NULL) { //没有找到待删结点
return false;
}
if (!p->lchild && !p->rchild) {//如果查找的是叶子结点 直接删除
pre->key > p->key ? pre->lchild = NULL : pre->rchild = NULL;
free(p);
}
else if (!p->lchild) { //左子树空,则只需重接它的右子树
pre->key > p->key ? pre->lchild = p->rchild : pre->rchild = p->rchild;
free(p);
}
else if (!p->rchild) { //右子树空,则只需重接它的左子树
pre->key > p->key ? pre->lchild = p->lchild : pre->rchild = p->lchild;
free(p);
}
else { //左右子树均不空
q = p;
s = q->lchild;
while (s->rchild) {
q = s;
s = s->rchild;
}
if (p == q) {
s->rchild = q->rchild;
pre->key > p->key ? pre->lchild = s : pre->rchild = s;
free(p);
}
else {
q->rchild = s->lchild;
s->rchild = p->rchild;
s->lchild = p->lchild;
pre->key > p->key ? pre->lchild = s : pre->rchild = s;
free(p);
}
}
return true;
}
//二叉树的遍历
//访问一个结点 打印字符
void visit(BSNode* p) {
cout << p->key << " ";
}
//先序遍历
void PreOrder(BSTree T) {
if (T) {
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
//中序遍历
void InOrder(BSTree T) {
if (T) {
InOrder(T->lchild);
visit(T);
InOrder(T->rchild);
}
}
//先序 + 中序
void Order(BSTree T) {
cout << endl;
cout << "PreOrder" << endl;
PreOrder(T);
cout << endl;
cout << "InOrder" << endl;
InOrder(T);
cout << endl;
cout << endl;
}
void test() {
BSTree T;
ElemType str[] = { 50, 66, 60, 65, 62, 26, 28, 21, 70 };
CreateBST(T, str, sizeof(str) / sizeof(ElemType));
cout << "Create a Binary Search Tree str:" << endl;
Order(T);
if (BST_Insert(T, 49)) {//插入不存在的元素
cout << "Insert the success!" << endl;
}
else {
cout << "Insert the failure!" << endl;
}
Order(T);
if (BST_Insert(T, 26)) {//插入存在的元素
cout << "Insert the success!" << endl;
}
else {
cout << "Insert the failure!" << endl;
}
Order(T);
BST_Search(T, 21);
cout << "BST_Search(T, 21)->key :" << BST_Search(T, 21)->key << endl;
//1.删除叶结点
cout << "deleteBST(T, 21):" << endl;
deleteBST(T, 21);
Order(T);
//2.删除 无左子树结点
cout << "deleteBST(T, 28):" << endl;
deleteBST(T, 28);
Order(T);
//3.删除 无右子树结点
cout << "deleteBST(T, 65):" << endl;
deleteBST(T, 65);
Order(T);
//4.删除 含有 左右子树结点
cout << "deleteBST(T, 66):" << endl;
deleteBST(T, 66);
Order(T);
}
int main() {
test();
return 0;
}
代码运行结果:
2. 平衡二叉树(AVL)
(1)平衡二叉树的定义
AVL树的名字来源于它的发明作者G.M. Adelson-Velsky 和 E.M. Landis。AVL树是最先发明的自平衡二叉查找树(Self-Balancing Binary Search Tree,简称平衡二叉树)。
平衡二叉树定义(AVL):它或者是一棵空树,或者具有以下性质的二叉排序树:它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一棵平衡二叉树。
(2)平衡二叉树相关概念
1. 平衡因子:将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor)。对于平衡二叉树,BF的取值范围为[-1,1]。如果发现某个节点的BF值不在此范围,则需要对树进行调整。
![]()
非平衡二叉树&平衡二叉树 2. 最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树。
![]()
最小不平衡子树示例
(3)平衡二叉树的平衡调整
平衡二叉树的插入
左单旋
![]()
左单旋 右单旋
![]()
右单旋
四种平衡旋转
1 . LL平衡旋转(右单旋转):由于在A的左孩子的左子树上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1增至2。下面是LL型的最简单形式。显然,按照大小关系,结点B应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡,A结点就好像是绕结点B顺时针旋转一样。
2 . RR平衡调整(左单旋转):由于在A的右孩子的右子树上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。图是RR型的最简单形式。显然,按照大小关系,结点B应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡,A结点就好像是绕结点B逆时针旋转一样。
3 .LR平衡调整(先左后右双旋转):由于在A的左孩子的右子树上插入新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先左旋转后右旋转。先将A结点的左孩子B的右子树的根结点C向左上旋转提升到B结点的位置,然后再把该C结点向右上旋转提升到A结点的位置。
4 .RL平衡调整(先右后左双旋转):由于在A的右孩子的左子树上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。图是RL型的最简单形式。显然,按照大小关系,结点C应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡。
(4)代码实现思路
1 . 右旋:
2 . 左旋: