• 平衡树:AVL树


    节点定义

    1. template<class Key, class Value>
    2. class AVLTreeNode {
    3. AVLTreeNode* L = nullptr, *R = nullptr;//左右子树
    4. int deep;//深度
    5. int lsize, rsize;//左右字数的深度
    6. linkNode*>* iterator;//迭代器,如果不想写无序遍历整棵树的,可以忽略
    7. template<class Key, class Value, class Cmp>
    8. friend class AVLTree;
    9. public:
    10. const Key first;//键
    11. Value second;//值
    12. AVLTreeNode(const Key& key, Value& val)
    13. :first(key), second(val) {
    14. lsize = rsize = 0;
    15. }
    16. ~AVLTreeNode() {}//注意,这边要手动清理Value的内容,这里不清理是为了安全
    17. };

    内部方法:

    1:pushUp:用两个子节点的信息更新父节点的信息;

    2:zig:右旋[依赖与pushUp]

    3:zag:左旋[依赖与pushUp]

    3:maintainNode[依赖zig/zag]:保持一个节点的平衡

    4:takeNode/takeNode_recursion[依赖zig/zag]:删除给定节点

    这些方法就是所有内部且不公开与客户端程序员的方法了

    下面是代码:

    pushUp

    1. template<class Key, class Value, class Cmp>
    2. void AVLTree::pushUp(AVLTreeNode*& node) {
    3. if (node == nullptr) return;
    4. if (node->L != nullptr)
    5. node->lsize = node->L->deep;
    6. else node->lsize = 0;
    7. if (node->R != nullptr)
    8. node->rsize = node->R->deep;
    9. else node->rsize = 0;
    10. node->deep = max(node->lsize, node->rsize) + 1;
    11. }

    zig

    1. template<class Key, class Value, class Cmp>
    2. void AVLTree::zig(AVLTreeNode*& node) {
    3. AVLTreeNode* l = node->L;
    4. if (l == nullptr) return;//如果为空,那么就不需要旋转
    5. node->L = l->R, l->R = node;
    6. pushUp(node);
    7. node = l;
    8. pushUp(node);
    9. }

    zag

    1. template<class Key, class Value, class Cmp>
    2. void AVLTree::zag(AVLTreeNode*& node) {
    3. AVLTreeNode* r = node->R;
    4. if (r == nullptr) return;
    5. node->R = r->L, r->L = node;
    6. pushUp(node);
    7. node = r;
    8. pushUp(node);
    9. }

    maintainNode

    先判断是左偏还是右偏,然后在判断是否要用双旋

    时间复杂度1

    1. template<class Key, class Value, class Cmp>
    2. void AVLTree::maintainNode(AVLTreeNode*& node) {
    3. if (node == nullptr) return;
    4. if (node->lsize - node->rsize > 1) {
    5. if (node->L->rsize > node->L->lsize) {
    6. zag(node->L);
    7. }
    8. zig(node);
    9. }
    10. else if (node->rsize - node->lsize > 1) {
    11. if (node->R->lsize > node->R->rsize) {
    12. zig(node->R);
    13. }
    14. zag(node);
    15. }
    16. }

    tackNode/tackNode_recursion

    先将一个点一直左右旋转,直到为叶节点,然后删除掉,注意,我们回溯路径的时候要进行一次反向旋转,时间复杂度logn

    1. template<class Key, class Value, class Cmp>
    2. void AVLTree::tackNode(AVLTreeNode*& node) {
    3. tackNode_recursion(node);
    4. pushUp(node);
    5. }
    6. template<class Key, class Value, class Cmp>
    7. void AVLTree::tackNode_recursion(AVLTreeNode*& node) {
    8. if (node->L == nullptr&&node->R == nullptr) {
    9. Delete(node);
    10. node = nullptr;
    11. }
    12. else {
    13. if (node->L != nullptr) {
    14. zig(node);
    15. tackNode_recursion(node->R);
    16. zag(node);
    17. }
    18. else {
    19. zag(node);
    20. tackNode_recursion(node->L);
    21. zig(node);
    22. }
    23. }
    24. }

    外部方法:

    insert、find、remove、clear,getSize、[begin]、[end]

    insert

    找到位置,注意,该位置一点是叶子节点,然后沿着路径maintainNode

    1. template<class Key, class Value, class Cmp>
    2. void AVLTree::insert(AVLTreeNode*& node, const Key& k, Value& val) {
    3. static int ptr;
    4. if (node == nullptr) {//到空,自己创建一个
    5. size++;
    6. node = getNode(k, val);
    7. node->deep = 1;
    8. maintainNode(node);
    9. return;
    10. }
    11. else if (cmp(k, node->first) == 0) {//已经存在,k == root->k
    12. return;
    13. }
    14. else if (cmp(k, node->first) == -1) {
    15. insert(node->L, k, val);
    16. }
    17. else {
    18. insert(node->R, k, val);
    19. }
    20. pushUp(node);
    21. maintainNode(node);
    22. }

    remove

    找到目标节点,然后tackNode,接着回溯路径maintainNode

    1. template<class Key, class Value, class Cmp>
    2. void AVLTree::remove(AVLTreeNode*& node, const Key& k) {
    3. if (node == nullptr) {
    4. return;//没找到
    5. }
    6. else if (cmp(k, node->first) == 0) {//找到目标节点
    7. tackNode(node);
    8. }
    9. else if (cmp(k, node->first) == -1) {
    10. remove(node->L, k);
    11. }
    12. else {
    13. remove(node->R, k);
    14. }
    15. pushUp(node);
    16. maintainNode(node);
    17. }

    clear【这里介绍Delete】

    撤销一个子树[当要删除一个节点的时候且不希望删除左右子树,要记得将左右子树指针置空],如果有迭代器,那么也要将对应节点删除

    1. template<class Key, class Value, class Cmp>
    2. void AVLTree::Delete(AVLTreeNode* node) {
    3. if (node == nullptr) return;
    4. Delete(node->L);
    5. Delete(node->R);
    6. size--;
    7. iteratorSystem.pop_node(node->iterator);
    8. delete node;
    9. }

    其他操作也都大同小异了,本菜就不介绍了,会上面那些操作也就ok了

  • 相关阅读:
    STM32实战总结:HAL之SDIO
    【0114】libpq连接句柄PGconn介绍
    【南京大学jyy操作系统】(三)理解并发程序执行 | 并发控制 : 互斥
    弹性力学的简单学习
    大型企业网如何部署NAT实现需求
    【吴恩达·机器学习】第四章:详解神经网络:推理和训练
    耗时大半个月收整全套「Java架构进阶pdf」
    html网站-关于发展历程的案例
    什么是集成测试?集成测试方法有哪些?
    ansible学习笔记【12】parted模块
  • 原文地址:https://blog.csdn.net/weixin_62953519/article/details/127408413