热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

关于AVLTree(C++实现)没有统一旋转操作的问题

这篇文章主要介绍了关于AVLTree(C++实现)没有统一旋转操作的问题,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

最近疫情比较严重,只能在家里休息,利用休息之余,我用C++把AVL树实现了一遍

大学老师只讲一些比较简单的数据结构和算法,这些高级数据结构还是需要自己主动学习并且动手来实现的,

从前只听说过AVLTree,我从看书了解原理到把它一点一点写出来最后在调试一共花了大概3天的时间。应该已经算很长时间了。

一般情况下AVL树是不用我么自己写的,但是为了有一份已经实现的代码作为我以后再来回顾算法实现的依照,我还是决定对自己狠一些把它实现了一遍

以下代码均采用C++11 标准

在ubuntu 18.04上经过编译和调试

/*
 * 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 
template
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 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(1) {
      //节点构造函数
    }
    inline bool isLeaf() {
      return (left == nullptr && right == nullptr);
    }
    inline bool hasTwoChildren() {
      return (left != nullptr && right != nullptr);
    }
    inline int balanceFactor() {
      //获得节点的平衡因子
      int leftHeight = left == nullptr ? 0 : left->height; //获得左子树的高度
      int rightHeight = right == nullptr ? 0 : right->height; //获得右子树的高度
      return leftHeight - rightHeight;
    }
    inline bool isBalanced() {
      //判断node是否平衡
      int balanceFactor_ = balanceFactor();
      return balanceFactor_ >= -1 && balanceFactor_ <= 1; //平衡因子为-1,0,1则返回true
    }
    inline void updateHeight() {
      //更新节点的高度
      int leftHeight = left == nullptr &#63; 0 : left->height; //获得左子树的高度
      int rightHeight = right == nullptr &#63; 0 : right->height; //获得右子树的高度
      height = 1 + (leftHeight > rightHeight &#63; 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 &#63; 0 : left->height; //获得左子树的高度
      int rightHeight = right == nullptr &#63; 0 : right->height; //获得右子树的高度
      if (leftHeight > rightHeight)
        return left;
      if (leftHeight e);
      if (cmp == 0) //找到了元素
        return node;
      if (cmp > 0) { //待寻找元素大于节点存储的元素
        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;
  }
  void afterRotate(NODE *gNode, NODE *pNode, NODE *child) {
    //在左旋转与右旋转中统一调用
    pNode->parent = gNode->parent;
    if (gNode->isLeftChild())
      gNode->parent->left = pNode;
    else if (gNode->isRightChild())
      gNode->parent->right = pNode;
    else
      //此时gNode->parent 为nullptr,gNode为root节点
      root_ = pNode;
    if (child != nullptr)
      child->parent = gNode;
    gNode->parent = pNode;
    //左右子树发生变化,所以要更新高度
    gNode->updateHeight();
    pNode->updateHeight();
  }
  void rotateLeft(NODE *gNode) {
    //对gNode进行左旋转
    NODE *pNode = gNode->right;
    NODE *child = pNode->left;
    gNode->right = child;
    pNode->left = gNode;
    afterRotate(gNode, pNode, child);
  }
  void rotateRight(NODE *gNode) {
    //对gNode进行右旋转
    NODE *pNode = gNode->left;
    NODE *child = pNode->right;
    gNode->left = child;
    pNode->right = gNode;
    afterRotate(gNode, pNode, child);
  }
  void rebalance(NODE *gNode) {
    //恢复平衡,grand为高度最低的不平衡节点
    NODE *pNode = gNode->tallerChild();
    NODE *nNode = pNode->tallerChild();
    if (pNode->isLeftChild()) {
      if (nNode->isLeftChild()) {
        //LL
        /*
         *    gNode
         *   /     对gNode右旋
         *   pNode    ====>    pNode
         *  /            /   \
         *  nNode          nNode  gNode
         */
        rotateRight(gNode);
      } else {
        //LR
        /*
         *    gNode         gNode
         *   /    对pNode左旋   /    对gNode右旋
         *   pNode   ====>    nNode   ====>    nNode
         *   \          /           /   \
         *    nNode       pNode         pNode gNode
         */
        rotateLeft(pNode);
        rotateRight(gNode);
      }
    } else {
      if (nNode->isLeftChild()) {
        //RL
        /*
         *  gNode         gNode
         *   \    对pNode右旋  \    对gNode左旋
         *   pNode   ====>    nNode   ====>    nNode
         *   /            \          /   \
         *  nNode           pNode       gNode pNode
         */
        rotateRight(pNode);
        rotateLeft(gNode);
      } else {
        //RR
        /*
         *  gNode
         *  \    对gNode左旋
         *   pNode   ====>    pNode
         *   \          /   \
         *    nNode       gNode nNode
         */
        rotateLeft(gNode);
      }
    }
  }
  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 = 0; //比较结果
    while (node != nullptr) {
      parent = node; //保存父节点
      cmp = cmp_(e, node->e); //由函数指针来比较
      if (cmp > 0) {
        node = node->right; //添加的元素大于节点中的元素
      } else if (cmp <0) {
        node = node->left; //添加的元素小于节点中的元素
      } else {
        node->e = e; //相等时就覆盖
        return; //添加的元素等于节点中的元素,直接返回
      }
    }
    //判断要插入父节点的哪个位置
    NODE *newNode = new NODE(e, parent); //为新元素创建节点
    if (cmp > 0) {
      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 &#63; 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 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 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
BinarySearchTree::BinarySearchTree(int (*cmp)(Element e1, Element e2)) :
    size_(0), root_(nullptr), cmp_(cmp) {
  //树的构造函数
}
template
BinarySearchTree::~BinarySearchTree() {
  // 析构函数
  clear();
}
template
inline int BinarySearchTree::size() {
  //返回元素个数
  return size_;
}
template
inline bool BinarySearchTree::isEmpty() {
  //判断是否为空树
  return size_ == 0;
}
#endif /* SRC_BINARYSEARCHTREE_H_ */
main方法
/*
 * main.cpp
 *
 * Created on: 2020年1月29日
 *   Author: LuYonglei
 */
#include "BinarySearchTree.h"
#include 
#include 
using namespace std;
template
int compare(Element e1, Element e2) {
  //比较函数,相同返回0,e1e2返回1
  return e1 == e2 &#63; 0 : (e1 
bool visitor(Elemnet &e) {
  cout < 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);
//  a.remove(99);
//  a.remove(85);
//  a.remove(95);
  clock_t start = clock();
  for (int i = 0; i <1000000; i++) {
    a.add(i);
  }
  for (int i = 0; i <1000000; i++) {
    a.remove(i);
  }
//  a.inorderTraversal(visitor);
  clock_t end = clock();
  cout <

总结

以上所述是小编给大家介绍的关于AVLTree(C++实现)没有统一旋转操作的问题,希望对大家有所帮助!


推荐阅读
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 在ubuntu服务器上安装vscode,但是目前使用的方法都无法成功。第一次安装经历:安装完anaconda后有自动安装vscode的选项,输入yes后,没有出现错误,但是在终端输 ... [详细]
  • MySQL语句大全:创建、授权、查询、修改等【MySQL】的使用方法详解
    本文详细介绍了MySQL语句的使用方法,包括创建用户、授权、查询、修改等操作。通过连接MySQL数据库,可以使用命令创建用户,并指定该用户在哪个主机上可以登录。同时,还可以设置用户的登录密码。通过本文,您可以全面了解MySQL语句的使用方法。 ... [详细]
  • Vagrant虚拟化工具的安装和使用教程
    本文介绍了Vagrant虚拟化工具的安装和使用教程。首先介绍了安装virtualBox和Vagrant的步骤。然后详细说明了Vagrant的安装和使用方法,包括如何检查安装是否成功。最后介绍了下载虚拟机镜像的步骤,以及Vagrant镜像网站的相关信息。 ... [详细]
  • STM32与FPGA的对比及学习建议
    本文对比了野火STM32F103指南针板和Xilinx的PYNQ-Z2板(ZYNQ-7020),介绍了野火STM32F103指南针板的学习资料和讲解视频的详细程度,建议初学者学习野火的资料。同时,介绍了STM32开发所用的Keil程序和C指针的重要性。对于ZYNQ-7020的开发,提到了其自带的Linux、Ubuntu18.4系统以及使用SD卡烧入镜像的方法。 ... [详细]
author-avatar
凌波_薇步
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有