目录
一、BTS部分
1.BST的定义:
为空树或者具有下列性质的二叉树:①左子树不空:则左子树所有节点小于根结点;②右子树不空:则右子树所有的结点大于根结点;③左右子树也分别为二叉搜索树。显然它的定义是递归的,而且根据这个定义我们可以得到一个重要的性质:当中序遍历一棵二叉搜索树的时候,会得到一个以结点值递增的有序序列。
首先给出二叉树的结构体定义
struct TreeNode {
int value;
TreeNode* left;
TreeNode* right;
//构造函数:接受一个int类型的参数val并且初始化数据域value
TreeNode(int val) : value(val), left(nullptr), right(nullptr) {}
};
2.创建BST(由一个关键字序列生成BST):
一般情况下让第一个元素作为根节点即可,随后对关键字序列进行访问,让他和根结点比较,决定进入那一侧(递归)。注意的是新的元素被插入后都变成了叶子结点。整体逻辑是非常简单的。
3.向二叉搜索树中插入元素:
由于会出现是否允许有重复元素的先决条件我们分情况讨论:
当不支持有重复元素的时候:我们选择忽略掉该元素即可。可以采用先查后找的方法,或者更简单直接利用if...else...语句将待插入关键字vlaue等于当前结点的key的情况处理方法为返回根节点即可。
当支持有重复的元素时候:可以人为的选择插入左侧还是右侧。
下面我们给出实现的代码:(这里选择了不支持重复元素的方法)
TreeNode* insert(TreeNode* root, int value) {
if (root == nullptr) {
return new TreeNode(value);
}
if (value < root->value) {
root->left = insert(root->left, value);
} else if (value > root->value) {
root->right = insert(root->right, value);
}
return root;
}
4.向二叉搜索树中删除某个元素:
删除一个元素的逻辑一句话概括为:要用其左子树最大的元素或者右子树最小的元素代替即可。
// 查找最小值节点,我们要用这个函数去找待删除结点的右子树的最小值
TreeNode* findMin(TreeNode* node) {
while (node->left != nullptr) {
node = node->left;
}
return node;
}
// 删除节点
TreeNode* deleteNode(TreeNode* root, int value) {
if (root == nullptr) {
return root;
}
if (value < root->value) {//第一步先找到待删除结点
root->left = deleteNode(root->left, value);
} else if (value > root->value) {
root->right = deleteNode(root->right, value);
} else {
// 找到要删除的节点
if (root->left == nullptr) {//该节点没有左子树那么直接用右子树的根节点代替之即可
TreeNode* temp = root->right;
delete root;
return temp;
} else if (root->right == nullptr) {//该节点没有右子树那么直接用左子树的根节点代替之即可
TreeNode* temp = root->left;
delete root;
return temp;
}
// 有两个子节点的情况找右子树的最小值即可
TreeNode* temp = findMin(root->right);
root->value = temp->value;
root->right = deleteNode(root->right, temp->value);
}
return root;
}
二、BBT部分
1.定义:
我们在前面提到过由一个序列生成二叉搜索树的时候,我们一般选择第一个元素作为二叉树的根结点,当这个序列为有序的时候,它会变成一条链表,此时查找的时间复杂度由O(logn)退化为O(n),我们引入平衡二叉树的概念。①基于BST而实现的。②左右子树的深度之差的绝对值小于等于1,而且左右子树也是平衡二叉树(递归)。此外考虑到后面会频繁的访问父节点,我们需要采用三叉链的结构。下面给出结构体的定义:
struct Node {
int key; // 存放int类型关键字
Node* left;//指向左孩子
Node* right;//指向右孩子
Node* parent; // 指向父节点的指针
int balanceFactor; // 平衡因子
};
2.准备函数:
在实现插入操作之前要有以下准备:
求二叉树的高度的方法:
// 获取两个整数的最大值
int max(int a, int b) {
return (a > b) ? a : b;
}
int height(Node* N) {
if (N == nullptr)
return 0;
return 1 + max(height(N->left), height(N->right));
}
创建新的结点:
// 创建一个新的节点
Node* newNode(int key) {
Node* node = new Node();
node->key = key;
node->left = nullptr;
node->right = nullptr;
node->parent = nullptr; // 新节点默认没有父节点
node->balanceFactor = 0; // 新节点默认平衡因子为0
return(node);
}
3.插入的过程:
第一步:按照BST的插入逻辑将待插入元素插到指定的位置。第二步:更新平衡因子。第三步:检查失衡并且去调整。
对于第三步的若检查处失去平衡的四种情况以及处理方法:
①LL型:(插入较高的左子树的左侧)
首先我们给出最开始的形式
随后我们进行插入操作首先插入元素,后修改bf,出现了不平衡的结点
接下来第一步将curr的父结点与currL链接:
1
注意:curr的父结点对currL是唯一确定的,因为curr只有一个parent。所以在操作①直接currL.parent=curr.parent即可。而curr的父节点要连接currL的时候要判断curr是左还是右,以确定用哪一个指针来指向currL。
接下来是旋转的过程:
就做了两件事情:①让curr成为currL的右孩子;②让currLR成为curr的左孩子。
最后,补充curr和currLR的父结点:
当然,最后再修改一下bf即可。
代码实现
// 右旋操作
Node* rightRotate(Node*curr ) {//LL情况下对结点curr进行右旋
Node* currL = curr->left;//curr结点的左孩子
Node* currLR = currL->right;//currL的右孩子
// 更新currL的父节点
currL->parent = curr->parent;
if (curr->parent != nullptr) {
if (curr->parent->left == curr)
curr->parent->left = currL;
else
curr->parent->right = currL;
}
// 执行旋转
currL->right = curr;
curr->left = currLR;
// 更新curr和currLR的父节点
curr->parent = currL;
if (currLR != nullptr)
currLR->parent = curr;
// 更新平衡因子
curr->balanceFactor = height(curr->left) - height(curr->right);
currL->balanceFactor = height(currL->left) - height(currL->right);
// 返回新的根节点
return currL;
}
②对于RR型(对于左旋而言逻是辑一致的直接给出代码):
// 左旋操作
Node* leftRotate(Node* curr) {//RR情况对结点curr进行左旋
Node* currR = curr->right;
Node* currRL = currR->left;
// 更新currR的父节点
currR->parent = curr->parent;
if (curr->parent != nullptr) {
if (curr->parent->left == curr)
curr->parent->left = currR;
else
curr->parent->right = currR;
}
// 执行旋转
currR->left = curr;
curr->right = currRL;
// 更新curr和currRL的父节点
curr->parent = currR;
if (currRL != nullptr)
currRL->parent = curr;
// 更新平衡因子
curr->balanceFactor = height(curr->left) - height(curr->right);
currR->balanceFactor = height(currR->left) - height(currR->right);
// 返回新的根节点
return currR;
}
③对于LR型:(在较高左子树的右侧插入)
首先来看插入元素前的状态:
接下来我们通过BST的插入元素以及修改bf值后会得到两种不同的情况,但是都属于LR类型。在旋转的过程中是一样的,区别仅仅在如何修改bf:
接下来我们以左侧的情况为例分析,
第一步:对currL进行左旋:
第二步:对curr进行右旋:
如图此时bf回归正常
④对于RL型:逻辑上是类似的唯一的不同是先对currR右旋,再对curr左旋即可。
4.代码层对四种情况的识别:
首先判断平衡因子属于大于1还是小于-1的情况,再判断关键字与node的左/右孩子的大小:
// 插入节点并保持AVL树平衡
Node* insert(Node* node, int key) {//node为被插入的树,而key表示被插入元素
// 1. 执行正常的BST插入:插入过程为递归地寻找
if (node == nullptr) {//当前树为空时候
Node* new_node = newNode(key);//利用newNode创建新的结点
new_node->parent = nullptr; // 根节点没有父节点
return new_node;
}
if (key < node->key) {//向左插入
Node* leftChild = insert(node->left, key);
node->left = leftChild;
leftChild->parent = node; // 更新左子节点的父节点指针
}
else if (key > node->key) {//向右插入
Node* rightChild = insert(node->right, key);
node->right = rightChild;
rightChild->parent = node; // 更新右子节点的父节点指针
}
else // 不允许重复键
return node;
// 2. 更新节点的平衡因子
node->balanceFactor = height(node->left) - height(node->right);
// 3. 获取节点的平衡因子,检查是否失衡
int balance = getBalance(node);
// 如果节点失衡,有四种情况需要处理
// 左左情况
if (balance > 1 && key < node->left->key)
return rightRotate(node);
// 右右情况
if (balance < -1 && key > node->right->key)
return leftRotate(node);
// 左右情况
if (balance > 1 && key > node->left->key) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
// 右左情况
if (balance < -1 && key < node->right->key) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
// 返回未修改的节点指针
return node;
}
5.全部代码 :
#include <iostream>
using namespace std;
// 定义AVL树节点结构
struct Node {
int key; // 存放int类型关键字
Node* left;//指向左孩子
Node* right;//指向右孩子
Node* parent; // 指向父节点的指针
int balanceFactor; // 平衡因子
};
// 获取节点的高度
int height(Node* N) {
if (N == nullptr)
return 0;
return 1 + max(height(N->left), height(N->right));
}
// 获取两个整数的最大值
int max(int a, int b) {
return (a > b) ? a : b;
}
// 创建一个新的节点
Node* newNode(int key) {
Node* node = new Node();
node->key = key;
node->left = nullptr;
node->right = nullptr;
node->parent = nullptr; // 新节点默认没有父节点
node->balanceFactor = 0; // 新节点默认平衡因子为0
return(node);
}
// 右旋操作
Node* rightRotate(Node*curr ) {//LL情况下对结点curr进行右旋
Node* currL = curr->left;//curr结点的左孩子
Node* currLR = currL->right;//currL的右孩子
// 更新currL的父节点
currL->parent = curr->parent;
if (curr->parent != nullptr) {
if (curr->parent->left == curr)
curr->parent->left = currL;
else
curr->parent->right = currL;
}
// 执行旋转
currL->right = curr;
curr->left = currLR;
// 更新curr和currLR的父节点
curr->parent = currL;
if (currLR != nullptr)
currLR->parent = curr;
// 更新平衡因子
curr->balanceFactor = height(curr->left) - height(curr->right);
currL->balanceFactor = height(currL->left) - height(currL->right);
// 返回新的根节点
return currL;
}
// 左旋操作
Node* leftRotate(Node* curr) {//RR情况对结点curr进行左旋
Node* currR = curr->right;
Node* currRL = currR->left;
// 更新currR的父节点
currR->parent = curr->parent;
if (curr->parent != nullptr) {
if (curr->parent->left == curr)
curr->parent->left = currR;
else
curr->parent->right = currR;
}
// 执行旋转
currR->left = curr;
curr->right = currRL;
// 更新curr和currRL的父节点
curr->parent = currR;
if (currRL != nullptr)
currRL->parent = curr;
// 更新平衡因子
curr->balanceFactor = height(curr->left) - height(curr->right);
currR->balanceFactor = height(currR->left) - height(currR->right);
// 返回新的根节点
return currR;
}
// 获取平衡因子
int getBalance(Node* N) {
if (N == nullptr)
return 0;
return N->balanceFactor;
}
// 插入节点并保持AVL树平衡
Node* insert(Node* node, int key) {//node为被插入的树,而key表示被插入元素
// 1. 执行正常的BST插入:插入过程为递归地寻找
if (node == nullptr) {//当前树为空时候
Node* new_node = newNode(key);//利用newNode创建新的结点
new_node->parent = nullptr; // 根节点没有父节点
return new_node;
}
if (key < node->key) {//向左插入
Node* leftChild = insert(node->left, key);
node->left = leftChild;
leftChild->parent = node; // 更新左子节点的父节点指针
}
else if (key > node->key) {//向右插入
Node* rightChild = insert(node->right, key);
node->right = rightChild;
rightChild->parent = node; // 更新右子节点的父节点指针
}
else // 不允许重复键
return node;
// 2. 更新节点的平衡因子
node->balanceFactor = height(node->left) - height(node->right);
// 3. 获取节点的平衡因子,检查是否失衡
int balance = getBalance(node);
// 如果节点失衡,有四种情况需要处理
// 左左情况
if (balance > 1 && key < node->left->key)
return rightRotate(node);
// 右右情况
if (balance < -1 && key > node->right->key)
return leftRotate(node);
// 左右情况
if (balance > 1 && key > node->left->key) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
// 右左情况
if (balance < -1 && key < node->right->key) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
// 返回未修改的节点指针
return node;
}
// 中序遍历打印AVL树
void inOrder(Node* root) {
if (root != nullptr) {
inOrder(root->left);
cout << root->key << "(" << root->balanceFactor << ") "; // 打印平衡因子
inOrder(root->right);
}
}
int main() {
Node* root = nullptr;
// 插入节点
root = insert(root, 14);
root = insert(root, 2);
root = insert(root, 13);
root = insert(root, 4);
root = insert(root, 5);
root = insert(root, 12);
// 打印中序遍历结果
cout << "Inorder traversal of the constructed AVL tree is \n";
inOrder(root);
return 0;
}