前言:
这三种是基础的树,要清晰的知道原理,并且知道代码实现
但是网络上的代码参差不齐,这是我罗列的,并且修改过的、完整的、实现度高、逻辑清晰的代码。
一、BST(二叉查找树)
功能实现:
插入某一元素,查找元素,删除指定元素(可以复用删除最小或者最大元素,这要直接敲代码才知道)
寻找和删除最小元素和最大元素(直接复用删除指定元素即可)
要点:
1、接口的设计
2、删除节点:依旧要维持BST树,默认用要删除节点的右子树的最小元素(即最左边元素)要替代删除节点的位置
也可以用左子树的最大元素
代码实现:
#include<iostream>
#include<queue>
using namespace std;
//struct TreeNode;
struct TreeNode {
int val;
TreeNode* left;//疑问,为什么不可以直接用TreeNode
TreeNode* right;
TreeNode(int x) : val(x),left(NULL),right(NULL) {}
};
//BST树(简单的,不需要平衡)
//功能实现如下:
//插入某一元素,查找元素,删除指定元素(可以复用删除最小或者最大元素,这要直接敲代码才知道)
//寻找和删除最小元素和最大元素(直接复用删除指定元素即可)
class BST {
public:
BST():size(0),root(NULL) { }
void insert(int x);
bool contain(int x);//查找元素
void erase(int x);//细节的,我们删除元素必须要调整树结构
int findMin();//返回查找到的最小值,本身上下层没有什么交互,所以可以直接用迭代法,更简单
int findMax();//返回查找到的最大值
void print();//层序遍历
int size;
private:
TreeNode* insert(TreeNode* node,int x);
bool contain(TreeNode* node, int x);
TreeNode* erase(TreeNode* node, int x);
TreeNode* findMin(TreeNode* node);
TreeNode* findMax(TreeNode* node);
TreeNode* root;
};
void BST::insert(int x) {//利用递归,比较简单,由于递归三部曲,由于接口固定,要再定义一个函数
root= insert(root, x);//有可能根节点初始值便为空
}
TreeNode* BST::insert(TreeNode* root, int x) {//该树不支持相同元素的插入,相同元素不会树扩大
if (root == NULL) {
size++;
return new TreeNode(x);
}
if (x < root->val)root->left=insert(root->left, x);//此递归的代码的框架,可以记住,用本层指针接住下层要回传的,并且本层也向上层回传
if (x > root->val)root->right=insert(root->right, x);//这个框架适合走某一条路径
return root;
}
bool BST::contain(int x) {
return contain(root, x);
}
bool BST::contain(TreeNode* node, int x) {//前序遍历
if (node == NULL)return false;//边界
if (node->val == x)return true;
else if (x < node->val)return contain(node->left, x);
else return contain(node->right, x);
}
void BST::erase(int x) {
if (contain(x))root=erase(root, x);//可能根节点会被更改
else cout << "没有此元素"<<endl;
return;
}
//删除节点的函数写错了
TreeNode* BST::erase(TreeNode* node, int x) {//处理三种情况下的删除节点,并且最后要返回根节点,因为有可能根节点也被操作了
if (node == NULL)return NULL;//由于提前判断必须包含,所以这条语句是不会运行到的
if (x ==node->val) {//一旦相等,就不需要再递归了,再向下走了
size--;
//处理三种情况下的删除节点
if (!node->left && !node->right) return NULL;//左右节点都为空
else if (node->left && !node->right)return node->left;//左节点不为空,右节点为空
else if (!node->left && node->right)return node->right;
else {//最复杂的情况,左右子节点都不空,需要多几步操作
//找到右子树的最小节点
TreeNode* min= findMin(node->right);
erase(root,min->val);//迅速重构了一棵树
min->left = node->left;
min->right = node->right;
// min->right = erase(node->right,min->val);;
return min;
}
}
else if (x < node->val) node->left = erase(node->left, x);
else node->right = erase(node->right, x);
return node;
}
int BST::findMin() {
return findMin(root)->val;
}
int BST::findMax() {
return findMax(root)->val;
}
TreeNode* BST::findMin(TreeNode* node) {
while (node->left) {
node = node->left;
}
return node;
}
TreeNode* BST::findMax(TreeNode* node) {
while (node->right) {
node = node->right;
}
return node;
}
void BST::print(){//层序遍历
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
TreeNode* node = que.front();
que.pop();
cout << node->val << " ";
if (node->left)que.push(node->left);
if (node->right)que.push(node->right);
}
cout << endl;
}
//测试代码
int main() {
BST a;
a.insert(4);
a.insert(2);
a.insert(1);
a.insert(3);
a.insert(6);
a.insert(5);
a.insert(15);
a.insert(7);
a.insert(16);
a.insert(14);
a.print();
a.erase(12);
a.print();
cout << a.findMin();
cout << a.size;
}
二、AVL树
2、1单旋与右旋
思路:
1、判断出问题的节点(不平衡的节点)
2、从该节点出发,走两条边,判断如下
两条边 | |
单旋 | 左—左;右—右 |
右旋 | 左—右;右—左 |
单旋:
插入14之前的AVL树
插入14,并且完成第一步,找到问题节点,即不平衡的节点,默认叶子节点的高度为0,即该节点左右子节点高度差大于1
完成第二步,从问题节点出发,向插入节点的方向走两步,然后就可以确定是单旋,并且旋转的对象是问题节点和走两步经过的节点(图中是4和6)
结果如上(话说,这样写笔记是真的累,哎)
右旋:
未插入8的AVL树
第一步,第二步,确定为双旋
结果如图
2、2实现
左旋与右旋的代码理解:
可参考:详细图文——AVL树_带翅膀的猫的博客-优快云博客_avl树
功能实现:
插入与删除操作,并且要求同步调整树结构使其达到平衡(要实现右旋与左旋(可复用性强)),并且要求同步调整高度
//AVL树
//功能实现:
//插入与删除操作,并且要求同步调整树结构使其达到平衡(要实现右旋与左旋(可复用性强)),并且要求同步调整高度
#include<iostream>
#include<queue>
using namespace std;
struct TreeNode
{
int val;
TreeNode* left;//疑问,为什么不可以直接用TreeNode
TreeNode* right;
int height;//叶子节点为0;空节点是不会调用.height的,height(-1)错误
TreeNode(int x) : val(x), left(NULL), right(NULL),height(0) {}
};
class AVLTree {
public:
AVLTree():root(NULL){}
void insert(int x);
void erase(int x);
void print();
TreeNode* root;
private:
TreeNode* leftRotate(TreeNode* node);
TreeNode* rightRotate(TreeNode* node);
TreeNode* insert(TreeNode* node,int x);
TreeNode* erase(TreeNode* node, int x);
int getHeight(TreeNode* node);
int getBalanceFactor(TreeNode* node);
};
TreeNode* AVLTree::leftRotate(TreeNode* node) {//注意调整完结构后要更新树的高度
TreeNode* temp = node->right;
node->right = temp->left;
temp->left = node;//简洁的美,观察结构:被承接之后,就可以毫无顾虑的被更改
node->height = max(getHeight(node->left), getHeight(node->right)) + 1;
temp->height = max(getHeight(temp->left), getHeight(temp->right)) + 1;
return temp;
}
TreeNode* AVLTree::rightRotate(TreeNode* node) {
TreeNode* temp = node->left;
node->left = temp->right;
temp->right = node;//简洁的美,观察结构:被承接之后,就可以毫无顾虑的被更改
node->height = max(getHeight(node->left), getHeight(node->right)) + 1;
temp->height = max(getHeight(temp->left), getHeight(temp->right)) + 1;
return temp;
}
void AVLTree::insert(int x) {
root= insert(root, x);//可能根节点被更新了
}
TreeNode* AVLTree::insert(TreeNode* node, int x) {//默认得不包含重复的节点
if (node == NULL) {//找到空位了,截至条件
return new TreeNode(x);
}
if (x < node->val)node->left = insert(node->left, x);
else if(x > node->val)node->right= insert(node->right, x);
//回溯到这一层之后要更新height
node->height = 1 + max(getHeight(node->left), getHeight(node->right));
//验证是否失去平衡,并且调整
int balanceFactor = getBalanceFactor(node);
//左边更深,并且是插入到左节点的左节点的位置
if (balanceFactor > 1 && getBalanceFactor(node->left) > 0) {
//右旋LL
return rightRotate(node);
}
if (balanceFactor < -1 && getBalanceFactor(node->right) < 0) {
//左旋RR
return leftRotate(node);
}
//LR:并且从下往上旋,底是右旋,所以要靠左旋;即LR对应于R-左旋;L-右旋
if (balanceFactor > 1 && getBalanceFactor(node->left) < 0) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
//RL
if (balanceFactor < -1 && getBalanceFactor(node->right) > 0) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
return node;
}
int AVLTree::getHeight(TreeNode* node) {
if (node == NULL)return -1;
else return node->height;
}
int AVLTree::getBalanceFactor(TreeNode* node) {
if (node == NULL)return 0;
else return getHeight(node->left) - getHeight(node->right);
}
//删除节点
void AVLTree::erase(int x) {
root=erase(root, x);
}
TreeNode* AVLTree::erase(TreeNode* node,int x) {
if (node == NULL) {
cout << "要删除的节点不存在" << endl;
return NULL;
}
TreeNode* balancenode=node;
if (x == node->val) {
//处理三种情况下的删除节点
if (!node->left && !node->right) return NULL;//左右节点都为空,实际上不需要参与调整
else if (node->left && !node->right)balancenode= node->left;//左节点不为空,右节点为空
else if (!node->left && node->right)balancenode= node->right;
else {//最复杂的情况,左右子节点都不空,返回右子树的最小节点,即最左节点
//找到右子树的最小节点
TreeNode* min = node->right;
while (min->left) {
min = min->left;
}
//不会陷入循环,因为erase不会走进这个if语句
//min->left = node->left;细节的不可以写在删除操作前面,会提前更改min的左右子节点,对接下来的erase操作有影响
min->right = erase(node->right,min->val);//可能会重构,节点将不是node->right
min->left = node->left;
balancenode= min;
node->left = node->right = NULL;
}
}
else if (x < node->val) node->left = erase(node->left, x);
else node->right = erase(node->right, x);
//维护平衡
//更新height
balancenode->height = 1 + max(getHeight(balancenode->left), getHeight(balancenode->right));
//计算平衡因子
int balanceFactor = getBalanceFactor(balancenode);
int leftbalanceFactor = getBalanceFactor(balancenode->left);
int rightbalanceFactor = getBalanceFactor(balancenode->right);
if (balanceFactor > 1 && leftbalanceFactor >= 0) {
//右旋LL
return rightRotate(balancenode);
}
if (balanceFactor < -1 && getBalanceFactor(balancenode->right) <= 0) {
//左旋RR
return leftRotate(balancenode);
}
//LR
if (balanceFactor > 1 && leftbalanceFactor < 0) {
balancenode->left = leftRotate(balancenode->left);
//cout << balancenode->left->val;
return rightRotate(balancenode);
}
//RL
if (balanceFactor < -1 && getBalanceFactor(balancenode->right) > 0) {
node->right = rightRotate(balancenode->right);
return leftRotate(balancenode);
}
return balancenode;
}
void AVLTree::print() {//层序遍历
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
TreeNode* node = que.front();
que.pop();
cout << node->val << " ";
if (node->left)que.push(node->left);
if (node->right)que.push(node->right);
}
cout << endl;
}
代码测试情况:
//测试代码
int main() {//
AVLTree a;
a.insert(6);
a.insert(15);
a.insert(4);
a.insert(2);
a.insert(5);
a.insert(7);
a.insert(16);
a.insert(1);
a.insert(3);
a.insert(14);
a.print();
a.erase(15);
a.print();
}
三、splay树
3、1旋转
“一“字旋转
”之“字旋转
(我是真画不下,xdm,自己看书吧)