来自上帝的骰子---Treap(树堆)详解


为什么说是上帝的骰子?

解释这个问题,首先由这个数据结构的名字开始,Treap = Tree + Heap,即为树堆之意,然而实际上用到堆的地方就是利用了一个随机的值标记每个结点,然后根据这个值对树进行左旋、右旋操作来调整父子树直接的关系,你可以严格让它遵循小根堆也可以遵循大根堆,这都无所谓。

也就是说每个结点的结构需要额外存储一个随机值,来决定它是否旋转调整,这对比普通的BST就要优越很多了,但这个也看运气,如果骰子没摇好,出现极端的情况,则可能即便是旋转了多次还是效率低下,我后面对BST、Treap、AVL进行了各方面的对比。

相对AVL,它不需要记录深度,不需要根据深度来判断是否旋转,旋转这件事就完全交给老天了,而且也不存在复杂的旋转情况,只有左旋和右旋。

注意:我写的这个Treap是以大根堆的方式进行维护的!也就是父节点大于子节点的随机值。

Treap实现

1. 结点的结构

struct node {
    
    
	//值、优先级(随机数、该数的总结点数、该结点的val次数
    int val, priority, length, cnt;
    node *lchild;
    node *rchild;

    node() : val(0), length(1), cnt(1), lchild(nullptr), rchild(nullptr) {
    
    
        srand((unsigned) time(NULL));//记得重新设定种子
        priority = rand();
    }

    node(int val) : val(val), length(1), cnt(1), lchild(nullptr), rchild(nullptr) {
    
    
        srand((unsigned) time(NULL));
        priority = rand();
    }

    void update() {
    
    
        length = cnt;
        if (lchild != nullptr)length += lchild->length;
        if (rchild != nullptr)length += rchild->length;
    }
};

2. Treap的抽象数据结构

class Treap {
    
    
    node *head;
    int length;
public:
    /*construct&destruct*/
    Treap() : head(nullptr), length(0) {
    
    }

    Treap(int val) : head(new node(val)), length(1) {
    
    }

public://内部类设计迭代器
    class iterator {
    
    
        node *head;
        node *root;
    public:
        /*迭代器部分*/
        iterator(node *head, node *root) : head(head), root(root) {
    
    }

        iterator &operator++() {
    
    
            root = queryNext(head, root->val);
            return *this;
        }

        iterator operator++(int) {
    
    
            iterator t = *this;
            root = queryNext(head, root->val);
            return t;
        }

        iterator &operator--() {
    
    
            root = queryPre(head, root->val);
            return *this;
        }

        iterator operator--(int) {
    
    
            iterator t = *this;
            root = queryPre(head, root->val);
            return t;
        }

        int operator*() {
    
    
            return root->val;
        }

        bool operator!=(const iterator &t) {
    
    
            return t.root != root;
        }
    };

private:
    /*static function*/
    /*rotate*/
    static node *rotateLeft(node *root);

    static node *rotateRight(node *root);

    /*insert&remove*/
    static node *insert(node *root, int val, int &size);

    static node *remove(node *root, int val, int &size);

    /*query rank&value*/
    static int getLength(node *root);

    static int queryRank(node *root, int val);//快速查询val的排名
    static int queryValue(node *root, int rank);//快速查询排名为rank的数
    /*query pre&next*/
    static node *queryPre(node *root, int val);

    static node *queryNext(node *root, int val);

    static void inorder_print(node *root);

    static void destroy(node *root);

public:
    /*public function*/
    /*insert&remove*/
    void insert(int val) {
    
    
        head = insert(head, val, length);
    }

    void remove(int val) {
    
    
        head = remove(head, val, length);
    }

    int size() {
    
    
        return length;
    }

    bool isEmpty() {
    
    
        return length == 0;
    }

    /*query rank&value*/
    int queryRank(int val) {
    
    
        return queryRank(head, val);
    }

    int queryValue(int rank) {
    
    
        return queryValue(head, rank);
    }

    void inorder_print() {
    
    
        inorder_print(head);
    }

    /*begin&end*/
    iterator begin() {
    
    
        node *t = head;
        while (t->lchild != nullptr) {
    
    
            t = t->lchild;
        }
        return iterator(head, t);
    }

    iterator end() {
    
    
        return iterator(head, nullptr);
    }
};

3. 左旋和右旋

具体可以看我之前AVL树对左旋右旋的描述,这里只开发源码查看,文章链接

左旋:

node *Treap::rotateLeft(node *root) {
    
    
    node *son = root->rchild;
    root->rchild = son->lchild;
    son->lchild = root;
    root->update();//记得先更新底下的情况
    son->update();
    return son;
}

右旋:

node *Treap::rotateRight(node *root) {
    
    
    node *son = root->lchild;
    root->lchild = son->rchild;
    son->rchild = root;
    root->update();
    son->update();
    return son;
}

4. 插入和删除

插入:

node *Treap::insert(node *root, int val, int &size) {
    
    
    if (root == nullptr) {
    
    
        ++size;
        return new node(val);
    }
    if (root->val == val) {
    
    
        root->cnt++;
        size++;
    } else if (root->val > val) {
    
    
        root->lchild = insert(root->lchild, val, size);
        //根据优先级判断是否右旋,因为只可能在左边增加长度,通过维持优先级的大根堆
        if (root->priority < root->lchild->priority) root = rotateRight(root);
    } else if (root->val < val) {
    
    
        root->rchild = insert(root->rchild, val, size);
        if (root->priority < root->rchild->priority) root = rotateLeft(root);
    }
    root->update();//注意更新长度信息
    return root;
}

删除:

删除这里还是详解一下:

找到要删除的目标节点后,我们根据让树旋转使得优先级较大的孩子替换掉父亲(目标节点)。然后继续追杀父亲结点,直到该结点被逼到叶子结点,删除即可。

node *Treap::remove(node *root, int val, int &size) {
    
    
    if (root == nullptr)return nullptr;//没找到
    if (root->val == val) {
    
    
        //含有多个相同值,直接操作cnt即可
        if (root->cnt > 1) {
    
    
            root->cnt--;
            --size;
        }
            //分为两类情况:叶子结点情况和非叶子结点情况
        else if (root->lchild != nullptr || root->rchild != nullptr) {
    
    
            //只有左子树或者左子树优先级大于右子树情况
            if (root->rchild == nullptr || root->lchild != nullptr && root->lchild->priority > root->rchild->priority) {
    
    
                root = rotateRight(root);//右旋后继续追杀
                root->rchild = remove(root->rchild, val, size);
            } else {
    
    //只有右子树或者右子树优先级大于左子树的情况
                root = rotateLeft(root);//左旋后继续追杀
                root->lchild = remove(root->lchild, val, size);
            }
        } else {
    
    //叶子结点情况,直接删除,然后把
            delete root;
            root = nullptr;
            --size;
        }
    } else if (root->val > val) {
    
    
        root->lchild = remove(root->lchild, val, size);
    } else if (root->val < val) {
    
    
        root->rchild = remove(root->rchild, val, size);
    }
    if (root)
        root->update();
    return root;
}

5. 查询排名

原理:由于是排序二叉树,而且记录了树的结点个数,所以我们根据左边个数(小于当前结点的个数),如果我们的值小于当前结点的值,则小于它的个数范围肯定是在当前结点的左边,直接迁移到左孩子即可,如果大于当前结点,则当前结点的左边大小+它自身也是无法满足要查询的值的排名,rank加上该值后root继续右移。如果出现找到该元素的情况,则直接返回rank+左边长度。如果其中不存在,则最后得出的rank肯定是需要+1的。

inline int Treap::getLength(node *root) {
    
    
    if (root == nullptr)
        return 0;
    return root->length;
}

int Treap::queryRank(node *root, int val) {
    
    //相当于查询有多少个数小于等于val
    int rank = 0;
    while (root != nullptr) {
    
    
        if (root->val == val)return rank + getLength(root->lchild) + root->cnt;
        else if (root->val > val)root = root->lchild;
        else rank += getLength(root->lchild) + root->cnt, root = root->rchild;
    }
    return rank + 1;//如果未找到,则在原来的基础上+1
}

6. 按排名查询值

inline int Treap::getLength(node *root) {
    
    
    if (root == nullptr)
        return 0;
    return root->length;
}
int Treap::queryValue(node *root, int rank) {
    
    //相当于得到第k大的数:支持重复元素是最骚的!!!
    while (root != nullptr) {
    
    
        if (getLength(root->lchild) + root->cnt > rank) {
    
    
            if (getLength(root->lchild) + 1 > rank)
                root = root->lchild;
            else
                return root->val;
        } else if (getLength(root->lchild) + root->cnt < rank) {
    
    
            rank -= getLength(root->lchild) + root->cnt;
            root = root->rchild;
        } else {
    
    //rank与左子树的大小相等的情况
            return root->val;
        }
    }
    return 0;
}

7. 查询前驱后继

前驱:

node *Treap::queryPre(node *root, int val) {
    
    //一样的道理:如果有左子树,就是左子树中最大的结点,如果没有则是最接近该结点的父节点(应在它的右侧
    node *res = nullptr;
    while (root != nullptr) {
    
    
        if (root->val < val)res = root, root = root->rchild;
        else root = root->rchild;
    }
    return res;
}

后继:

node *Treap::queryNext(node *root, int val) {
    
    //寻找后继
    node *res = nullptr;
    while (root != nullptr) {
    
    
        if (root->val > val)res = root, root = root->lchild;
        else root = root->rchild;
    }
    return res;
}

8. 销毁Treap

void Treap::destroy(node *root) {
    
    
    if (root == nullptr)
        return;
    destroy(root->lchild);
    destroy(root->rchild);
    delete root;
    root = nullptr;
}

9. 迭代器的设计

public://内部类设计迭代器
    class iterator {
    
    
        node *head;
        node *root;
    public:
        /*迭代器部分*/
        iterator(node *head, node *root) : head(head), root(root) {
    
    }

        iterator &operator++() {
    
    
            root = queryNext(head, root->val);
            return *this;
        }

        iterator operator++(int) {
    
    
            iterator t = *this;
            root = queryNext(head, root->val);
            return t;
        }

        iterator &operator--() {
    
    
            root = queryPre(head, root->val);
            return *this;
        }

        iterator operator--(int) {
    
    
            iterator t = *this;
            root = queryPre(head, root->val);
            return t;
        }

        int operator*() {
    
    
            return root->val;
        }

        bool operator!=(const iterator &t) {
    
    
            return t.root != root;
        }
    };

完整源代码

Treap.h

//
// Created by Alone on 2021/10/14.
//

#ifndef MY_TINY_STL_TREAP_H
#define MY_TINY_STL_TREAP_H

#include <cstdio>
#include <cstdlib>
#include <ctime>

struct node {
    
    
    int val, priority, length, cnt;
    node *lchild;
    node *rchild;

    node() : val(0), length(1), cnt(1), lchild(nullptr), rchild(nullptr) {
    
    
        srand((unsigned) time(NULL));//记得重新设定种子
        priority = rand();
    }

    node(int val) : val(val), length(1), cnt(1), lchild(nullptr), rchild(nullptr) {
    
    
        srand((unsigned) time(NULL));
        priority = rand();
    }

    void update() {
    
    
        length = cnt;
        if (lchild != nullptr)length += lchild->length;
        if (rchild != nullptr)length += rchild->length;
    }
};

class Treap {
    
    
    node *head;
    int length;
public:
    /*construct&destruct*/
    Treap() : head(nullptr), length(0) {
    
    }

    Treap(int val) : head(new node(val)), length(1) {
    
    }
    ~Treap(){
    
    
        destroy(head);
    }

public://内部类设计迭代器
    class iterator {
    
    
        node *head;
        node *root;
    public:
        /*迭代器部分*/
        iterator(node *head, node *root) : head(head), root(root) {
    
    }

        iterator &operator++() {
    
    
            root = queryNext(head, root->val);
            return *this;
        }

        iterator operator++(int) {
    
    
            iterator t = *this;
            root = queryNext(head, root->val);
            return t;
        }

        iterator &operator--() {
    
    
            root = queryPre(head, root->val);
            return *this;
        }

        iterator operator--(int) {
    
    
            iterator t = *this;
            root = queryPre(head, root->val);
            return t;
        }

        int operator*() {
    
    
            return root->val;
        }

        bool operator!=(const iterator &t) {
    
    
            return t.root != root;
        }
    };

private:
    /*static function*/
    /*rotate*/
    static node *rotateLeft(node *root);

    static node *rotateRight(node *root);

    /*insert&remove*/
    static node *insert(node *root, int val, int &size);

    static node *remove(node *root, int val, int &size);

    /*query rank&value*/
    static int getLength(node *root);

    static int queryRank(node *root, int val);//快速查询val的排名
    static int queryValue(node *root, int rank);//快速查询排名为rank的数
    /*query pre&next*/
    static node *queryPre(node *root, int val);

    static node *queryNext(node *root, int val);

    static void inorder_print(node *root);

    static void destroy(node *root);

public:
    /*public function*/
    /*insert&remove*/
    void insert(int val) {
    
    
        head = insert(head, val, length);
    }

    void remove(int val) {
    
    
        head = remove(head, val, length);
    }

    int size() {
    
    
        return length;
    }

    bool isEmpty() {
    
    
        return length == 0;
    }

    /*query rank&value*/
    int queryRank(int val) {
    
    
        return queryRank(head, val);
    }

    int queryValue(int rank) {
    
    
        return queryValue(head, rank);
    }

    void inorder_print() {
    
    
        inorder_print(head);
    }

    /*begin&end*/
    iterator begin() {
    
    
        node *t = head;
        while (t->lchild != nullptr) {
    
    
            t = t->lchild;
        }
        return iterator(head, t);
    }

    iterator end() {
    
    
        return iterator(head, nullptr);
    }
};


#endif //MY_TINY_STL_TREAP_H

Treap.cpp

//
// Created by Alone on 2021/10/14.
//

#include "Treap.h"

node *Treap::rotateLeft(node *root) {
    
    
    node *son = root->rchild;
    root->rchild = son->lchild;
    son->lchild = root;
    root->update();//记得先更新底下的情况
    son->update();
    return son;
}

node *Treap::rotateRight(node *root) {
    
    
    node *son = root->lchild;
    root->lchild = son->rchild;
    son->rchild = root;
    root->update();
    son->update();
    return son;
}

node *Treap::insert(node *root, int val, int &size) {
    
    
    if (root == nullptr) {
    
    
        ++size;
        return new node(val);
    }
    if (root->val == val) {
    
    
        root->cnt++;
        size++;
    } else if (root->val > val) {
    
    
        root->lchild = insert(root->lchild, val, size);
        //根据优先级判断是否右旋,因为只可能在左边增加长度,通过维持优先级的大根堆
        if (root->priority < root->lchild->priority) root = rotateRight(root);
    } else if (root->val < val) {
    
    
        root->rchild = insert(root->rchild, val, size);
        if (root->priority < root->rchild->priority) root = rotateLeft(root);
    }
    root->update();
    return root;
}

node *Treap::remove(node *root, int val, int &size) {
    
    
    if (root == nullptr)return nullptr;//没找到
    if (root->val == val) {
    
    
        //含有多个相同值,直接操作cnt即可
        if (root->cnt > 1) {
    
    
            root->cnt--;
            --size;
        }
            //分为两类情况:叶子结点情况和非叶子结点情况
        else if (root->lchild != nullptr || root->rchild != nullptr) {
    
    
            //只有左子树或者左子树优先级大于右子树情况
            if (root->rchild == nullptr || root->lchild != nullptr && root->lchild->priority > root->rchild->priority) {
    
    
                root = rotateRight(root);//右旋后继续追杀
                root->rchild = remove(root->rchild, val, size);
            } else {
    
    //只有右子树或者右子树优先级大于左子树的情况
                root = rotateLeft(root);//左旋后继续追杀
                root->lchild = remove(root->lchild, val, size);
            }
        } else {
    
    //叶子结点情况,直接删除,然后把
            delete root;
            root = nullptr;
            --size;
        }
    } else if (root->val > val) {
    
    
        root->lchild = remove(root->lchild, val, size);
    } else if (root->val < val) {
    
    
        root->rchild = remove(root->rchild, val, size);
    }
    if (root)
        root->update();
    return root;
}

int Treap::queryRank(node *root, int val) {
    
    //相当于查询有多少个数小于等于val
    int rank = 0;
    while (root != nullptr) {
    
    
        if (root->val == val)return rank + getLength(root->lchild) + root->cnt;
        else if (root->val > val)root = root->lchild;
        else rank += getLength(root->lchild) + root->cnt, root = root->rchild;
    }
    return rank + 1;//如果未找到,则在原来的基础上+1
}

int Treap::queryValue(node *root, int rank) {
    
    //相当于得到第k大的数:支持重复元素是最骚的!!!
    while (root != nullptr) {
    
    
        if (getLength(root->lchild) + root->cnt > rank) {
    
    
            if (getLength(root->lchild) + 1 > rank)
                root = root->lchild;
            else
                return root->val;
        } else if (getLength(root->lchild) + root->cnt < rank) {
    
    
            rank -= getLength(root->lchild) + root->cnt;
            root = root->rchild;
        } else {
    
    //rank与左子树的大小相等的情况
            return root->val;
        }
    }
    return 0;
}

node *Treap::queryPre(node *root, int val) {
    
    //一样的道理:如果有左子树,就是左子树中最大的结点,如果没有则是最接近该结点的父节点(应在它的右侧
    node *res = nullptr;
    while (root != nullptr) {
    
    
        if (root->val < val)res = root, root = root->rchild;
        else root = root->rchild;
    }
    return res;
}

node *Treap::queryNext(node *root, int val) {
    
    //寻找后继
    node *res = nullptr;
    while (root != nullptr) {
    
    
        if (root->val > val)res = root, root = root->lchild;
        else root = root->rchild;
    }
    return res;
}

void Treap::inorder_print(node *root) {
    
    
    if (root == nullptr)
        return;
    inorder_print(root->lchild);
    printf("%d ", root->val);
    inorder_print(root->rchild);
}

void Treap::destroy(node *root) {
    
    
    if (root == nullptr)
        return;
    destroy(root->lchild);
    destroy(root->rchild);
    delete root;
    root = nullptr;
}

inline int Treap::getLength(node *root) {
    
    
    if (root == nullptr)
        return 0;
    return root->length;
}

测试

AVL vs Treap vs 普通BST

测试数据量:1000w
直接来结论:
AVL极端情况下(插入的数据有序),完爆所有平衡树。
Treap随机情况的插入表示不错!大部分时间可以和AVL持平。
BST别想了,这个1000w数据只有随机情况能用几分钟过,如果极端情况直接程序运行出错!

整体小结:

  • 第一轮:insert操作
  1. 随机情况Treap(看运气,毕竟随机事件) >=< AVL>> BST
  2. 极端情况AVL >> Treap, BST直接暴毙
  • 第二轮: remove操作–Treap被AVL吊打,BST就别提了

下面就没得继续展开的比较了,总结就是Treap相对好写一点,效率高不高看运气。

还有就是Treap在直接处理第K大的值具有优势,以及按排位查找具有优势。

优势:由于treap每个结点都含有它整个结点个数的记录,所以可以很快的查出值的排名和排名的值。

猜你喜欢

转载自blog.csdn.net/m0_50945504/article/details/120792596
今日推荐