• 树和树结构(2) : Treap树


    测试题目来自 http://codevs.cn/problem/1164/
    部分内容来自网络

    想要了解treap树,你先要知道什么是二叉搜索树。

    二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
    二叉搜索树算法实现很简单,请各位自行百度。但是,二叉搜索树往往会暴露一些问题。例如当读入数据是高度有序的,如1 2 3 4 5, 树就会变成一条链:

        1
          
           2
            
             3
              
               4
                
                 5

    这样的话,树就变成了一条链,查找和插入效率仅仅相当于链表,这自然是我们不愿意看到的。所以,前人想了一大堆办法使得搜索树尽量扁平,达到O(logn)的期望复杂度。其中有avl rb_tree sbt splay等许多优化方式。而其中最简单易行的就是treap。

    所谓treap,顾名思义就是tree+heap,使得二叉排序树既满足树的性质又满足堆的性质(这里堆不一定是完全二叉树)。树的性质依靠存的数据完成,堆的性质则靠一个随机数据prior存储。我们在插入节点时向新节点分配一个优先值。如果新节点优先级小于其父节点,则违背了小根堆的性质,我们依靠旋转来维护堆性质。

    旋转有两种方式
    ①: 左左情况旋转
    从图中可以看出,当我们插入“节点12”的时候,此时“堆性质”遭到破坏,必须进行旋转,我们发现优先级是6<9,所以就要进行
    左左情况旋转,最终也就形成了我们需要的结果。

    左左旋转 图片转载

    ②: 右右情况旋转
    既然理解了”左左情况旋转“,右右情况也是同样的道理,优先级中发现“6<9”,进行”右右旋转“最终达到我们要的效果。

    右右旋转 图片转载

    void change_right(node<T>* &n)
    {
            /*
            * 向右旋转(顺时针) 把当前节点的左孩子作为父节点.
            */
            node<T> *temp = n->left_child;
            n->left_child = temp->right_child;
            temp->right_child = n;
            n = temp;
            temp = 0;
    }
    void change_left (node<T>* &n)
    {
            /*
            * 向左旋转 与向右相反
            */
            node<T> *temp = n->right_child;
            n->right_child = temp->left_child;
            temp->left_child = n;
            n = temp;
            temp = 0;
    }

    理解了这两种旋转,我们只要对bst的put加以改造就可以维护treap性质:

    void put (node<T>* &n, T data)
    {
            /*节点为空,新建并插入数据*/
            if (!n) {
                    n = new node<T>;
                    n->data = data;
                    n->left_child = n->right_child = 0;
                    n->prior = rand()%1000000007;
                    /*随机数据作为优先值*/
                    return;
            }
            /*否则左右查找*/
            if (data == n->data) {
                    n->data = data;
                    //cout << data.first << " " << data.second << endl;
            } else if (data < n->data) {
                    put (n->left_child, data);
                    if (n->left_child->prior < n->prior)
                            change_right(n);
                    /*如果左节点优先值比当前小, 即破坏了小根堆的性质
                    * 则向右旋转当前节点以维护堆性质
                    */
            } else {
                    put (n->right_child, data);
                    if (n->right_child->prior < n->prior)
                            change_left(n);
                    /*与上面刚好相反*/
            }
    }

    如果你不奢求支持erase,treap就是这样简单了。但为了透彻了解treap,我们支持一下erase操作。删除的方法有两种。这里我们介绍treap独有的——将”结点下旋“,直到该节点成为叶子节点,删除之。首先找到删除的节点。为了支持小根堆性质,我们每次选择优先值较小的节点并向反方向旋转,将当前节点下旋直到没有孩子(叶子节点)。具体代码如下:

        /*删除某个元素*/
            void erase_T(node<T>* &tree, T n)
            {
                    if (!tree)
                            return;
                    /*如果不是则按照bst左右查找*/
                    if (!(tree->data == n)) {
                            if (n < tree->data)
                                    erase_T(tree->left_child, n);
                            else
                                    erase_T(tree->right_child, n);
                            return;
                    }
                    /*如果已经没有左右节点, 删除之*/
                    if (!tree->left_child && !tree->right_child) {
                            delete tree;
                            tree = 0;
                    } else if (tree->left_child && tree->right_child){
                    /*如果有两个孩子 则按照优先级查找删除*/
                            if (tree->left_child->prior <= tree->right_child->prior) {
                                    change_right(tree);
                                    erase_T(tree->right_child, n);
                            } else {
                                    change_left(tree);
                                    erase_T(tree->left_child, n);
                            }
                    } else if (tree->left_child) {
                    /*只有左孩子 相当于右节点prior无穷大*/
                            change_right(tree);
                            erase_T(tree->right_child, n);
                    } else if (tree->right_child) {
                    /*反之同理*/
                            change_left(tree);
                            erase_T(tree->left_child, n);
                    }
            }

    总结
    treap树是一种随机化数据结构。尽管他不能保证树的绝对平衡(avl树可以),但可以很大程度上避免链的产生。一般可以证明,在数据绝对随机的情况下,普通bst的性能是最好的,而treap尽可能通过调整使树趋于随机构建。treap是在严谨的数学证明上建立的,即他的期望复杂度为O(logn)。另外,treap的编程复杂度是所有自调整bst中最简单的,在OI中有着广泛的应用。


    完整代码 对于http://codevs.cn/problem/1164/

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <ctime>
    #include <map>
    using namespace std;
    
    
    template <typename T>
    class node {
            public:
            T data;
            node *left_child, *right_child;
            int prior;
    };
    
    
    template <typename T>
    class treap {
            private:
            node<T> *root;
    
            void change_right(node<T>* &n)
            {
                    /*
                    * 向右旋转(顺时��?) 把当前节点的左孩子作为父节点.
                    */
                    node<T> *temp = n->left_child;
                    n->left_child = temp->right_child;
                    temp->right_child = n;
                    n = temp;
                    temp = 0;
            }
    
            void change_left (node<T>* &n)
            {
                    /*
                    * 向左旋转 与向右相��?
                    */
                    node<T> *temp = n->right_child;
                    n->right_child = temp->left_child;
                    temp->left_child = n;
                    n = temp;
                    temp = 0;
            }
    
            /*私有成员函数*/
            /*insert的私有实��?*/
            void put (node<T>* &n, T data)
            {
                    /*节点为空,新建并插入数据*/
                    if (!n) {
                            n = new node<T>;
                            n->data = data;
                            n->left_child = n->right_child = 0;
                            n->prior = rand()%1000000007;
                            /*随机数据作为优先��?*/
                            return;
                    }
                    /*否则左右查找*/
                    if (data == n->data) {
                            n->data = data;
                            //cout << data.first << " " << data.second << endl;
                    } else if (data < n->data) {
                            put (n->left_child, data);
                            if (n->left_child->prior < n->prior)
                                    change_right(n);
                            /*如果左节点优先值比当前��?, 即破坏了小根堆的性质
                            * 则向右旋转当前节点以维护堆性质
                            */
                    } else {
                            put (n->right_child, data);
                            if (n->right_child->prior < n->prior)
                                    change_left(n);
                            /*与上面刚好相��?*/
                    }
            }
    
            /*find的私有实��?*/
            node<T>* get (node<T>* n, T data)
            {
                    if (!n)
                            return 0;
                    /*找不��?*/
                    if (n->data == data)
                            return n;
                    else if (data < n->data)
                            return get(n->left_child, data);
                    else
                            return get(n->right_child, data);
                    /*左右查找*/
            }
    
            /*析构函数私有实现*/
            void del (node<T> *n)
            {
                    if (n) {
                            /*删除左右节点*/
                            del (n->left_child);
                            del (n->right_child);
                            delete n;
                            /*释放父节��?*/
                    }
            }
    
            /*删除某个元素*/
            void erase_T(node<T>* &tree, T n)
            {
                    if (!tree)
                            return;
                    /*如果不是则按照bst左右查找*/
                    if (!(tree->data == n)) {
                            if (n < tree->data)
                                    erase_T(tree->left_child, n);
                            else
                                    erase_T(tree->right_child, n);
                            return;
                    }
                    /*如果已经没有左右节点, 删除��?*/
                    if (!tree->left_child && !tree->right_child) {
                            delete tree;
                            tree = 0;
                    } else if (tree->left_child && tree->right_child){
                    /*如果有两个孩��? 则按照优先级查找删除*/
                            if (tree->left_child->prior <= tree->right_child->prior) {
                                    change_right(tree);
                                    erase_T(tree->right_child, n);
                            } else {
                                    change_left(tree);
                                    erase_T(tree->left_child, n);
                            }
                    } else if (tree->left_child) {
                    /*只有左孩��? 相当于右节点prior无穷��?*/
                            change_right(tree);
                            erase_T(tree->right_child, n);
                    } else if (tree->right_child) {
                    /*反之同理*/
                            change_left(tree);
                            erase_T(tree->left_child, n);
                    }
            }
    
            /*dfs私有实现*/
            void dfs_lr (node<T>* &n, void (*function)(T t))
            {
                    if (!n) return;
                    /*如果为空直接退��?*/
                    dfs_lr(n->left_child, function);
                    function(n->data);
                    dfs_lr(n->right_child, function);
                    /*否则左右遍历*/
            }
    
            public:
            treap ()
            {
                    root = 0;
                    srand(time(NULL));
            }
    
            ~treap()
            {
                   del (root);
                   /*删除��?*/
            }
    
            /*成员函数*/
    
            /*
            * 函数��? insert(插入)
            * O(log2n) -> O(n)
            * 参数说明 data:插入的数��?
            * 请重��?"<"以自定义数据类型或降��?(默认升序)
            * 例如
            * struct data {
            *         int d;
            *         friend bool operator < (data a, data b) {
            *                 return a.d > b.d;
            *                 //降序
            *         }
            * }
            */
    
            void insert (T data)
            {
                    put (root, data);
                    /*调用私有成员函数*/
            }
    
            /*
            * 函数��? find(查找数据)
            * O(log2n) -> O(n)
            * 参数说明 data:查找的数��?
            * 请重��?"<"以自定义数据类型或降��?(默认升序)
            */
            node<T>* find (T data)
            {
                    return get (root, data);
            }
    
            /*
            * 函数��? empty(判断是否为空��?)
            * O(1)
            */
            bool empty()
            {
                    return (root == 0);
            }
    
            /*
            * 函数��? clear(清空)
            * O(n)
            */
            void clear ()
            {
                    del (root);
            }
    
            /*
            * 函数��? count(返回data是否存在)
            * O(log2n) -> O(n)
            */
    
            bool count(T data)
            {
                    return find(data) != 0;
            }
    
            /*
            * 函数��? eraze(删除元素)
            * O(log2n) -> O(n)
            */
            void erase(T data)
            {
                    erase_T(root, data);
            }
    
            /*
            * 函数��? dfs(深度优先遍历)
            * O(n)
            * 参数说明 function: 一个函数指��?,进行深度优先遍历会执行function(T), T为遍历到的数��?
            */
    
            void dfs (void (*function)(T t))
            {
                    dfs_lr(root, function);
                    /*调用私有成员函数*/
            }
    
    };
    
    struct p {
            int x;
            int times;
            friend bool operator < (p a, p b)
            {
                    return a.x < b.x;
            }
            friend bool operator == (p a, p b)
            {
                    return a.x == b.x;
            }
    
    };
    
    void out(p a)
    {
            printf ("%d %d
    ", a.x, a.times);
    }
    
    int main()
    {
            treap< p > tr;
            int n, temp;
            scanf ("%d", &n);
            for (int i=1; i<=n; i++) {
                    scanf ("%d", &temp);
                    p a; a.x = temp;
                    if (!tr.find(a)) {
                            a.times = 1;
                            tr.insert(a);
                    } else {
                            a.times = tr.find(a)->data.times + 1;
                            tr.insert(a);
                    }
                    //tr.dfs(out);
            }
            tr.dfs(out);
            return 0;
    }
    
  • 相关阅读:
    Could A New Linux Base For Tablets/Smartphones Succeed In 2017?
    使用libhybris,glibc和bionic共存时的TLS冲突的问题
    6 Open Source Mobile OS Alternatives To Android in 2018
    Using MultiROM
    GPU drivers are written by the GPU IP vendors and they only provide Android drivers
    Jolla Brings Wayland Atop Android GPU Drivers
    How to Use Libhybris and Android GPU Libraries with Mer (Linux) on the Cubieboard
    闲聊Libhybris
    【ARM-Linux开发】wayland和weston的介绍
    Wayland and X.org problem : Why not following the Android Solution ?
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684402.html
Copyright © 2020-2023  润新知