AVLtree(C++实现)有统一的旋转操作
AVLTree源码实现
/*
* BinarySearchTree.h
* 1. 添加元素时需自己做判断元素是否合法
* 2. 除层序遍历外,本源代码均采用递归遍历,若要减少栈的消耗,应该实现迭代遍历
* 3. 本代码实现的AVL树有统一旋转操作,不用分情况讨论LL,LR,RR,RL来进行树的平衡
* 统一旋转操作有特殊优化版本和统一接口版本,本代码保留了统一接口的源码,但是采用的是特殊优化版本
* Created on: 2020年1月29日
* Author: LuYonglei
*/ #ifndef SRC_BINARYSEARCHTREE_H_
#define SRC_BINARYSEARCHTREE_H_
#include <queue> template<typename Element>
class BinarySearchTree {
public: BinarySearchTree(int (*cmp)(Element e1, Element e2)); //比较函数指针 virtual ~BinarySearchTree(); int size(); //元素的数量 bool isEmpty(); //是否为空 void clear() {
//清空所有元素
NODE *node = root_;
root_ = nullptr;
using namespace std;
queue<NODE*> q;
q.push(node);
while (!q.empty()) {
NODE *tmp = q.front();
if (tmp->left != nullptr)
q.push(tmp->left);
if (tmp->right != nullptr)
q.push(tmp->right);
delete tmp;
q.pop();
}
} void add(Element e) {
//添加元素
add(e, cmp_);
} void remove(Element e) {
//删除元素
remove(Node(e, cmp_));
} bool contains(Element e) {
//是否包含某元素
return Node(e, cmp_) != nullptr;
} void preorderTraversal(bool (*visitor)(Element e)) {
//前序遍历
if (visitor == nullptr)
return;
bool stop = false; //停止标志,若stop为true,则停止遍历
preorderTraversal(root_, stop, visitor);
} void inorderTraversal(bool (*visitor)(Element e)) {
//中序遍历
if (visitor == nullptr)
return;
bool stop = false; //停止标志,若stop为true,则停止遍历
inorderTraversal(root_, stop, visitor);
} void postorderTraversal(bool (*visitor)(Element e)) {
//后序遍历
if (visitor == nullptr)
return;
bool stop = false; //停止标志,若stop为true,则停止遍历
postorderTraversal(root_, stop, visitor);
} void levelOrderTraversal(bool (*visitor)(Element e)) {
//层序遍历,迭代实现
if (visitor == nullptr)
return;
levelOrderTraversal(root_, visitor);
} int height() {
//树的高度
return height(root_);
} bool isComplete() {
//判断是否是完全二叉树
return isComplete(root_);
} private: int size_; typedef struct _Node {
Element e;
_Node *parent;
_Node *left;
_Node *right;
int height; //节点的高度
_Node(Element e_, _Node *parent_) :
e(e_), parent(parent_), left(nullptr), right(nullptr), height() {
//节点构造函数
} inline bool isLeaf() {
return (left == nullptr && right == nullptr);
} inline bool hasTwoChildren() {
return (left != nullptr && right != nullptr);
} inline int balanceFactor() {
//获得节点的平衡因子
int leftHeight = left == nullptr ? : left->height; //获得左子树的高度
int rightHeight = right == nullptr ? : right->height; //获得右子树的高度
return leftHeight - rightHeight;
} inline bool isBalanced() {
//判断node是否平衡
int balanceFactor_ = balanceFactor();
return balanceFactor_ >= - && balanceFactor_ <= ; //平衡因子为-1,0,1则返回true
} inline void updateHeight() {
//更新节点的高度
int leftHeight = left == nullptr ? : left->height; //获得左子树的高度
int rightHeight = right == nullptr ? : right->height; //获得右子树的高度
height = + (leftHeight > rightHeight ? leftHeight : rightHeight); //把节点高度更新为左右子树最大的高度+1
} inline bool isLeftChild() {
//判断节点是否是父亲节点的左子结点
return parent != nullptr && parent->left == this;
} inline bool isRightChild() {
//判断节点是否是父亲节点的右子结点
return parent != nullptr && parent->right == this;
} inline _Node* tallerChild() {
//获得高度更高的子树
int leftHeight = left == nullptr ? : left->height; //获得左子树的高度
int rightHeight = right == nullptr ? : right->height; //获得右子树的高度
if (leftHeight > rightHeight)
return left;
if (leftHeight < rightHeight)
return right;
return isLeftChild() ? left : right;
} } NODE; NODE *root_; int (*cmp_)(Element e1, Element e2); //为实现树的排序的个性化配置,私有成员保存一个比较函数指针 NODE* Node(Element e, int (*cmp_)(Element e1, Element e2)) {
//返回e元素所在的节点
NODE *node = root_;
while (node != nullptr) {
int cmp = cmp_(e, node->e);
if (cmp == ) //找到了元素
return node;
if (cmp > ) { //待寻找元素大于节点存储的元素
node = node->right;
} else { //待寻找元素小于节点存储的元素
node = node->left;
}
}
return nullptr;
} NODE* predecessor(NODE *node) {
//返回node的前驱节点
if (node == nullptr)
return nullptr;
//前驱节点在左子树
NODE *tmp = node->left;
if (tmp != nullptr) {
while (tmp->right != nullptr)
tmp = tmp->right;
return tmp;
}
//从父节点,祖父节点中寻找前驱节点
while (node->parent != nullptr && node == node->parent->left) {
node = node->parent;
}
return node->parent;
} NODE* successor(NODE *node) {
//返回node的后继节点
if (node == nullptr)
return nullptr;
//后继节点在右子树
NODE *tmp = node->right;
if (tmp != nullptr) {
while (tmp->left != nullptr)
tmp = tmp->left;
return tmp;
}
//从父节点,祖父节点中寻找后继节点
while (node->parent != nullptr && node == node->parent->right) {
node = node->parent;
}
return node->parent;
} #if 0
//统一接口版本(未优化)
void rotate(NODE *r, NODE *a, NODE *b, NODE *c, NODE *d, NODE *e, NODE *f,
NODE *g) {
//统一的旋转操作
/* r是变换前高度最低的不平衡节点
* 传入的参数a,b,c,d,e,f,g是经过变换后节点应该在的位置
* d
* / \
* b f
* / \ / \
* a c e g
* a,c,e,g均有可能为空,所以要对它们做判空处理
* b,d,f绝不为空
* d的parent可能为null,则d就为根节点,所以就要改变树中root_的指向
*/
//对d节点做处理
d->parent = r->parent;
if (r->isLeftChild())
r->parent->left = d;
else if (r->isRightChild())
r->parent->right = d;
else
root_ = d;
//a-b-c
b->left = a;
b->right = c;
if (a != nullptr)
a->parent = b;
if (c != nullptr)
c->parent = b;
b->updateHeight();
//e-f-g
f->left = e;
f->right = g;
if (e != nullptr)
e->parent = f;
if (g != nullptr)
g->parent = f;
f->updateHeight();
//b-d-f
d->left = b;
d->right = f;
b->parent = d;
f->parent = d;
d->updateHeight();
} void rebalance(NODE *gNode) {
//恢复平衡,grand为高度最低的不平衡节点
NODE *pNode = gNode->tallerChild();
NODE *nNode = pNode->tallerChild();
if (pNode->isLeftChild()) {
if (nNode->isLeftChild()) {
//LL
/*
* gNode(f)
* / \ 变换后
* pNode(d) g ====> pNode(d)
* / \ / \
* nNode(b) e nNode(b) gNode(f)
* / \ / \ / \
* a c a c e g
*/
rotate(gNode, nNode->left, nNode, nNode->right, pNode,
pNode->right, gNode, gNode->right);
} else {
//LR
/*
* gNode(f)
* / \ 变换后
* pNode(b) g ====> nNode(d)
* / \ / \
* a nNode(d) pNode(b) gNode(f)
* / \ / \ / \
* c e a c e g
*/
rotate(gNode, pNode->left, pNode, nNode->left, nNode,
nNode->right, gNode, gNode->right);
}
} else {
if (nNode->isLeftChild()) {
//RL
/*
* gNode(b)
* / \ 变换后
* a pNode(f) ====> nNode(d)
* / \ / \
* nNode(d) g gNode(b) pNode(f)
* / \ / \ / \
* c e a c e g
*/
rotate(gNode, gNode->left, gNode, nNode->left, nNode,
nNode->right, pNode, pNode->right);
} else {
//RR
/*
* gNode(b)
* / \ 变换后
* a pNode(d) ====> pNode(d)
* / \ / \
* c nNode(f) gNode(b) nNode(f)
* / \ / \ / \
* e g a c e g
*/
rotate(gNode, gNode->left, gNode, pNode->left, pNode,
nNode->left, nNode, nNode->right);
}
}
} #endif void rotate(NODE *r, NODE *b, NODE *c, NODE *d, NODE *e, NODE *f) {
//统一的旋转操作
/* r是变换前高度最低的不平衡节点
* 传入的参数b,c,d,e,f是经过变换后节点应该在的位置
* d
* / \
* b f
* / \ / \
* a c e g
* c,e均有可能为空,所以要对它们做判空处理
* b,d,f绝不为空
* d的parent可能为null,则d就为根节点,所以就要改变树中root_的指向
*/
//对d节点做处理
d->parent = r->parent;
if (r->isLeftChild())
r->parent->left = d;
else if (r->isRightChild())
r->parent->right = d;
else
root_ = d;
//b-c
b->right = c;
if (c != nullptr)
c->parent = b;
b->updateHeight();
//e-f
f->left = e;
if (e != nullptr)
e->parent = f;
f->updateHeight();
//b-d-f
d->left = b;
d->right = f;
b->parent = d;
f->parent = d;
d->updateHeight();
} void rebalance(NODE *gNode) {
//恢复平衡,grand为高度最低的不平衡节点
NODE *pNode = gNode->tallerChild();
NODE *nNode = pNode->tallerChild();
if (pNode->isLeftChild()) {
if (nNode->isLeftChild()) {
//LL
/*
* gNode(f)
* / \ 变换后
* pNode(d) g ====> pNode(d)
* / \ / \
* nNode(b) e nNode(b) gNode(f)
* / \ / \ / \
* a c a c e g
*/
rotate(gNode, nNode, nNode->right, pNode, pNode->right, gNode);
} else {
//LR
/*
* gNode(f)
* / \ 变换后
* pNode(b) g ====> nNode(d)
* / \ / \
* a nNode(d) pNode(b) gNode(f)
* / \ / \ / \
* c e a c e g
*/
rotate(gNode, pNode, nNode->left, nNode, nNode->right, gNode);
}
} else {
if (nNode->isLeftChild()) {
//RL
/*
* gNode(b)
* / \ 变换后
* a pNode(f) ====> nNode(d)
* / \ / \
* nNode(d) g gNode(b) pNode(f)
* / \ / \ / \
* c e a c e g
*/
rotate(gNode, gNode, nNode->left, nNode, nNode->right, pNode);
} else {
//RR
/*
* gNode(b)
* / \ 变换后
* a pNode(d) ====> pNode(d)
* / \ / \
* c nNode(f) gNode(b) nNode(f)
* / \ / \ / \
* e g a c e g
*/
rotate(gNode, gNode, pNode->left, pNode, nNode->left, nNode);
}
}
} void afterAdd(NODE *node) {
//添加node之后的调整
if (node == nullptr)
return;
node = node->parent;
while (node != nullptr) {
if (node->isBalanced()) {
//如果节点平衡,则对其更新高度
node->updateHeight();
} else {
//此时对第一个不平衡节点操作,使其平衡
rebalance(node);
//整棵树恢复平衡后,跳出循环
break;
}
node = node->parent;
}
} void add(Element e, int (*cmp_)(Element e1, Element e2)) {
//当树为空时,添加的节点作为树的根节点
if (root_ == nullptr) {
root_ = new NODE(e, nullptr);
size_++;
//插入一个根节点之后进行调整
afterAdd(root_);
return;
}
//当添加的节点不是第一个节点
NODE *parent = root_;
NODE *node = root_;
int cmp = ; //比较结果
while (node != nullptr) {
parent = node; //保存父节点
cmp = cmp_(e, node->e); //由函数指针来比较
if (cmp > ) {
node = node->right; //添加的元素大于节点中的元素
} else if (cmp < ) {
node = node->left; //添加的元素小于节点中的元素
} else {
node->e = e; //相等时就覆盖
return; //添加的元素等于节点中的元素,直接返回
}
}
//判断要插入父节点的哪个位置
NODE *newNode = new NODE(e, parent); //为新元素创建节点
if (cmp > ) {
parent->right = newNode; //添加的元素大于节点中的元素
} else {
parent->left = newNode; //添加的元素小于节点中的元素
}
size_++;
//添加一个新节点之后进行调整
afterAdd(newNode);
} void afterRemove(NODE *node) {
//删除node之后的调整
if (node == nullptr)
return;
node = node->parent;
while (node != nullptr) {
if (node->isBalanced()) {
//如果节点平衡,则对其更新高度
node->updateHeight();
} else {
//此时对不平衡节点操作,使其平衡
rebalance(node);
}
node = node->parent;
}
} void remove(NODE *node_) {
//删除某一节点
if (node_ == nullptr)
return;
size_--;
//优先删除度为2的节点
if (node_->hasTwoChildren()) {
NODE *pre = successor(node_); //找到node_的后继节点
node_->e = pre->e; //用后继节点的值覆盖度为2的节点的值
//删除后继节点(后继节点的度只能为1或0)
node_ = pre;
}
//此时node_的度必然为0或1
NODE *replacement = node_->left != nullptr ? node_->left : node_->right;
if (replacement != nullptr) { //node_的度为1
replacement->parent = node_->parent;
if (node_->parent == nullptr) //度为1的根节点
root_ = replacement;
else if (node_->parent->left == node_)
node_->parent->left = replacement;
else
node_->parent->right = replacement;
//所有删除操作准备完成,准备释放节点内存前进行平衡操作
afterRemove(node_);
delete node_;
} else if (node_->parent == nullptr) { //node_是叶子节点,也是根节点
root_ = nullptr;
//所有删除操作准备完成,准备释放节点内存前进行平衡操作
afterRemove(node_);
delete node_;
} else { //node_是叶子节点,但不是根节点
if (node_->parent->left == node_)
node_->parent->left = nullptr;
else
node_->parent->right = nullptr;
//所有删除操作准备完成,准备释放节点内存前进行平衡操作
afterRemove(node_);
delete node_;
}
} void preorderTraversal(NODE *node, bool &stop, bool (*visitor)(Element e)) {
//递归实现前序遍历
if (node == nullptr || stop == true)
return;
stop = visitor(node->e);
preorderTraversal(node->left, stop, visitor);
preorderTraversal(node->right, stop, visitor);
} void inorderTraversal(NODE *node, bool &stop, bool (*visitor)(Element e)) {
//递归实现中序遍历
if (node == nullptr || stop == true)
return;
inorderTraversal(node->left, stop, visitor);
if (stop == true)
return;
stop = visitor(node->e);
inorderTraversal(node->right, stop, visitor);
} void postorderTraversal(NODE *node, bool &stop,
bool (*visitor)(Element e)) {
//递归实现后序遍历
if (node == nullptr || stop == true)
return;
postorderTraversal(node->left, stop, visitor);
postorderTraversal(node->right, stop, visitor);
if (stop == true)
return;
stop = visitor(node->e);
} void levelOrderTraversal(NODE *node, bool (*visitor)(Element e)) {
if (node == nullptr)
return;
using namespace std;
queue<NODE*> q;
q.push(node);
while (!q.empty()) {
NODE *node = q.front();
if (visitor(node->e) == true)
return;
if (node->left != nullptr)
q.push(node->left);
if (node->right != nullptr)
q.push(node->right);
q.pop();
}
} int height(NODE *node) {
//某一节点的高度
return node->height;
} bool isComplete(NODE *node) {
if (node == nullptr)
return false;
using namespace std;
queue<NODE*> q;
q.push(node);
bool leaf = false; //判断接下来的节点是否为叶子节点
while (!q.empty()) {
NODE *node = q.front();
if (leaf && !node->isLeaf()) //判断叶子节点
return false;
if (node->left != nullptr) {
q.push(node->left);
} else if (node->right != nullptr) { //node->left == nullptr && node->right != nullptr
return false;
}
if (node->right != nullptr) {
q.push(node->right);
} else { //node->right==nullptr
leaf = true;
}
q.pop();
}
return true;
} }; template<typename Element>
BinarySearchTree<Element>::BinarySearchTree(int (*cmp)(Element e1, Element e2)) :
size_(), root_(nullptr), cmp_(cmp) {
//树的构造函数
} template<typename Element>
BinarySearchTree<Element>::~BinarySearchTree() {
// 析构函数
clear();
} template<typename Element>
inline int BinarySearchTree<Element>::size() {
//返回元素个数
return size_;
} template<typename Element>
inline bool BinarySearchTree<Element>::isEmpty() {
//判断是否为空树
return size_ == ;
} #endif /* SRC_BINARYSEARCHTREE_H_ */
main测试例程
/*
* main.cpp
*
* Created on: 2020年1月29日
* Author: LuYonglei
*/ #include "BinarySearchTree.h"
#include <iostream>
#include <time.h> using namespace std; template<typename Element>
int compare(Element e1, Element e2) {
//比较函数,相同返回0,e1<e2返回-1,e1>e2返回1
return e1 == e2 ? : (e1 < e2 ? - : );
} template<typename Elemnet>
bool visitor(Elemnet e) {
cout << e << " ";
cout << endl;
return false; //若返回true,则在遍历时会退出
} int main(int argc, char **argv) {
BinarySearchTree<double> a(compare);
// a.add(85);
// a.add(19);
// a.add(69);
// a.add(3);
// a.add(7);
// a.add(99);
// a.add(95);
// a.add(2);
// a.add(1);
// a.add(70);
// a.add(44);
// a.add(58);
// a.add(11);
// a.add(21);
// a.add(14);
// a.add(93);
// a.add(57);
// a.add(4);
// a.add(56);
clock_t start = clock();
for (int i = ; i < ; i++) {
a.add(i);
}
for (int i = ; i < ; i++) {
a.remove(i);
}
// a.inorderTraversal(visitor);
clock_t end = clock();
cout << end - start << endl;
// cout <<a.height()<< endl;
// cout << a.isComplete() << endl;
// a.remove(7);
// a.clear();
// a.levelOrderTraversal(visitor);
// cout << endl;
// cout<<a.contains(0)<<endl;
}
AVLtree(C++实现)有统一的旋转操作的更多相关文章
- AVLTree(C++实现)没有统一旋转操作
最近疫情比较严重,只能在家里休息,利用休息之余,我用C++把AVL树实现了一遍 大学老师只讲一些比较简单的数据结构和算法,这些高级数据结构还是需要自己主动学习并且动手来实现的, 从前只听说过AVLTr ...
- 【微软100题】定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。 如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。
package test; /** * 定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部. 如把字符串abcdef左旋转2位得到字符串cdefab. 请实现字符串左旋转的函数. * ...
- Splay_Tree 模板(区间修改,旋转操作)
1.旋转操作 #define MAXN 100100 bool Add[MAXN];//延迟标记 struct Splay_Tree { int cnt, rt;//cnt为节点数,rt == roo ...
- splay tree旋转操作 hdu 1890
很神奇的旋转操作. 目前没看到其他数据结构能实现这个功能.平衡树不好处理区间操作,线段树很难旋转.splay tree搞这个就很简单了. 下面用的这个模板跑了700ms,好慢,估计是删除操作太费时了, ...
- hdu 1890 Robotic SortI(splay区间旋转操作)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1890 题解:splay又一高级的功能,区间旋转这个是用线段树这些实现不了的,这题可以学习splay的旋 ...
- AVL树的旋转操作详解
[0]README 0.0) 本文部分idea 转自:http://blog.csdn.net/collonn/article/details/20128205 0.1) 本文仅针对性地分析AVL树的 ...
- AVL树的单双旋转操作
把必须重新平衡的节点称为å.对于二叉树,å的两棵子树的高度最多相差2,这种不平衡可能有四种情况: 对å的左儿子的左子树进行插入节点(左-左) 对å的左儿子的右子树进行插入节点(左-右) 对å的右儿子的 ...
- CGAffineTransform函数旋转操作
本文转载至 http://blog.sina.com.cn/s/blog_923fdd9b0101ahyx.html 首先获取UITableView的CGAffineTransform函数:CG ...
- 第五章 NFS、rsync等统一用户相关操作
一.统一用户 1.httpd2.NFS挂载目录3.rsync 1.所有服务器统一创建用户 [root@web01 ~]# groupadd www -g 666[root@web01 ~]# user ...
随机推荐
- pandas小程序应用-实验
背景:来自于日常工作,针对医院行政人员统计日常门诊信息,手工统计繁琐.容易出错的问题,结合实际特点,采用python对数据进行自动统计. 具体步骤如下: 1.引入python工具包. import p ...
- 使用Sklearn-train_test_split 划分数据集
使用sklearn.model_selection.train_test_split可以在数据集上随机划分出一定比例的训练集和测试集 1.使用形式为: from sklearn.model_selec ...
- Vue通讯
vue组件通讯 #props传递数据 父->子 //父组件传递数据 <template> <Children :data="msg"></Chi ...
- 拒绝FileNotFoundException!总结了这几个读取jar包外配置文件的知识点
前言 相信很多人遇到过这个问题:本地运行的好好的程序,怎么部署到线上就报找不到配置呢? 初识getResource 案例一 FieldMapConfig.class.getResource(" ...
- numpy :: 计算特征之间的余弦距离
余弦距离在计算相似度的应用中经常使用,比如: 文本相似度检索 人脸识别检索 相似图片检索 原理简述 下面是余弦相似度的计算公式(图来自wikipedia): 但是,余弦相似度和常用的欧式距离的有所区别 ...
- 洛谷$P1600$ 天天爱跑步 树上差分
正解:树上差分 解题报告: 传送门$QwQ$! 这题还挺妙的,,,我想了半天才会$kk$ 首先对一条链$S-T$,考虑先将它拆成$S-LCA$和$LCA-T$,分别做.因为总体上来说差不多接下来我就只 ...
- DataX支持mysql8.X
:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdow ...
- CAP 3.0 版本发布通告
前言 大家好,我们很高兴宣布 CAP 发布了 3.0 版本正式版. 自从上次 CAP 2.6 版本发布 以来,已经过去了几个月的时间,关注的朋友可能知道,在这几个月的时间里,也发布了几个预览版的 3. ...
- Linux下搭建C/C++编程环境
Linux下搭建C/C++编程环境 1.KDevelop下载 wget -O KDevelop.AppImage https://download.kde.org/stable/kdevelop/5. ...
- Lua表(table)的个人总结
1.表的简介和构造 table是个很强大且神奇的东西,又可以作为数组和字典,又可以当作对象,设置module.它是由数组和哈希表结合的实现的.他的key可以是除nil以外任意类型的值,key为整数时, ...