POJ 1442 (treap 求出名次入门)

Treap树解决名次查询
本文介绍使用Treap树解决动态名次查询问题的方法。通过构造Treap树,可以高效地进行插入、删除和查询操作,实现快速查找指定名次的元素。Treap树结合了平衡二叉搜索树和堆的特点,保证了良好的树形结构。

题意:

给出一列数字,有m个问题,分别是数列前ai个数字中第i小的数字是多少。
思路:
正常的算法是每次都遍历一边找到第i小的数字,但是在数字范围大的时候会超时,所
不赞同用这样的方法。而名次树treap恰好能很方便的求出名次。treap是由平衡二叉
树和堆结合而成的。平衡树中一个节点的左儿子值都小于该节点,右儿子的值都大于该
节点。而堆得存在使得二叉树的深度能够保持的很好,所以在求出名次的时候直接算出
某一个节点的左右儿子的节点个数即可。

treap一般由结构体实现:其有v,s,r:分别代表着数值,左右儿子+自己的个数,优先级别。

函数组成有:插入、翻转、删除。
    #include <iostream>
    #include <cstdio>
    #include <algorithm>

    using namespace std;

    struct Node {
        Node *ch[2];
        int r;  //优先级
        int v;  //值
        int s;  //结点总数

        Node(int v):v(v) {
            ch[0] = ch[1] = NULL;
            r = rand();
            s = 1;
        }

        bool operator < (const Node& rhs) const {
            return r < rhs.r;
        }

        int cmp(int x) const {
            if(x==v) return -1;
            return x < v ? 0:1;
        }

        void maintain() {
            s = 1;
            if(ch[0]!=NULL) s +=ch[0]->s;
            if(ch[1]!=NULL) s +=ch[1]->s;
        }
    };

    //旋转
    void rotate(Node* &o,int d) {
        Node* k = o->ch[d^1];
        o->ch[d^1] = k->ch[d];
        k->ch[d] = o;
        o->maintain();
        k->maintain();
        o = k;
    }

    //插入
    void insert(Node* &o,int x) {
        if(o==NULL) o = new Node(x);
        else {
            int d = (x< o->v?0:1);  //可能有相同的元素要插入
            insert(o->ch[d],x);
            if(o->ch[d]->r > o->r)
                rotate(o,d^1);
        }
        o->maintain();
    }

    int get(Node* &o)
    {
        if(o == NULL) return 0;
        return o->s;
    }

    int Find(Node* &o,int k)
    {
        int temp = get(o->ch[0]) + 1;
        if(temp == k) return o->v;
        if(temp > k) return Find(o->ch[0],k);
        return Find(o->ch[1],k-temp);
    }

    const int maxn = 100005;
    int n,m;

    int a[maxn];

    int main()
    {
        //freopen("in.txt","r",stdin);

        Node* root = NULL;
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n; i++) scanf("%d",&a[i]);
        int j = 1;
        for(int i = 1;i <= m; i++) {
            int x;
            scanf("%d",&x);
            for(;j <= x; j++) {
                insert(root,a[j]);
            }
            printf("%d\n",Find(root,i));
        }
        return 0;
    }
  • 自己写了一遍之后,多余的删除了。

#include <iostream>
#include <cstdio>
#include <math.h>
#include <algorithm>

using namespace std;

const int maxn = 100005;

struct Node
{
    Node* ch[2];
    int s,r,v;

    Node (int v) : v(v) {
        ch[0] = ch[1] = NULL;
        r = rand();
        s = 1;
    }

    void maintain() {
        s = 1;
        if(ch[0] != NULL) s += ch[0]->s;
        if(ch[1] != NULL) s += ch[1]->s;
    }

};

void rotate(Node* &o,int d)
{
    Node *k = o->ch[d^1];
    o->ch[d^1] = k->ch[d];
    k->ch[d] = o;
    k->maintain();
    o->maintain();
    o = k;
}

void insert(Node* &o,int x)
{
    if(o == NULL) o = new Node(x);
    else {
        int d = x > o->v ? 1:0;
        insert(o->ch[d],x);
        if(o->ch[d]->r > o->r)
            rotate(o,d^1);
    }
    o->maintain();
}

int get(Node* &o)
{
    if(o == NULL) return 0;
    else return o->s;
}

int Find(Node* &o,int k)
{
    int temp = get(o->ch[0]) + 1;
    if(temp == k) return o->v;
    else if(temp > k) return Find(o->ch[0],k);
    return Find(o->ch[1],k - temp);
}

int n,m;
int a[maxn];

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n; i++) {
        scanf("%d",&a[i]);
    }
    int j = 1;
    Node *root = NULL;
    for(int i = 1;i <= m; i++) {
        int x;
        scanf("%d",&x);
        for(;j <= x; j++) {
            insert(root,a[j]);
        }
        printf("%d\n",Find(root,i));
    }
    return 0;
}
### POJ1442 名次问题的C++实现时间复杂度分析 POJ1442 名次(Rank Tree)问题涉及构建和查询一种特殊的数据结构,通常称为平衡二叉搜索或基于数组的堆式结构。在C++中实现时,需要对插入、删除、查找等操作的时间复杂度进行详细分析。 #### 1. 插入操作的时间复杂度 在名次中,插入一个新元素的操作通常涉及维护的平衡性。如果使用的是自平衡二叉搜索(如AVL或红黑),插入操作的时间复杂度为 \(O(\log n)\) [^1]。这是因为每次插入后最多需要调整 \(O(\log n)\) 层节点以恢复的平衡性。 #### 2. 删除操作的时间复杂度 类似地,删除操作也需要维护的平衡性。在自平衡二叉搜索中,删除操作的时间复杂度同样为 \(O(\log n)\) [^1]。删除操作可能涉及重新分配子,但这仍然可以通过 \(O(\log n)\) 的调整完成。 #### 3. 查找操作的时间复杂度 查找操作在平衡二叉搜索中的时间复杂度为 \(O(\log n)\) [^1],因为从根节点到目标节点的路径长度最多为的高度,而平衡二叉树的高度为 \(O(\log n)\)。 #### 4. 构建整个的时间复杂度 如果初始时需要将 \(n\) 个元素插入到空中,则总的时间复杂度为 \(O(n \log n)\) [^1]。这是因为在插入每个元素时,都需要执行 \(O(\log n)\) 的调整操作。 #### 5. 查询排名的时间复杂度 在名次中,查询某个值的排名通常需要从根节点开始向下遍历,并根据左子的大小计算排名。假设是完全平衡的,则查询排名的时间复杂度为 \(O(\log n)\) [^1]。 #### 6. 综合时间复杂度 综合来看,名次的基本操作(插入、删除、查找、排名查询)的时间复杂度均为 \(O(\log n)\),而在构建整个时的时间复杂度为 \(O(n \log n)\)。 ```cpp #include <bits/stdc++.h> using namespace std; struct Node { int val, size; Node *left, *right; Node(int v) : val(v), size(1), left(nullptr), right(nullptr) {} }; int getSize(Node* node) { return node ? node->size : 0; } void updateSize(Node* node) { if (node) node->size = 1 + getSize(node->left) + getSize(node->right); } Node* rotateRight(Node* y) { Node* x = y->left; Node* T2 = x->right; x->right = y; y->left = T2; updateSize(y); updateSize(x); return x; } Node* rotateLeft(Node* x) { Node* y = x->right; Node* T2 = y->left; y->left = x; x->right = T2; updateSize(x); updateSize(y); return y; } Node* splay(Node* root, int key) { if (!root || root->val == key) return root; if (key < root->val) { if (!root->left) return root; if (key < root->left->val) { root->left->left = splay(root->left->left, key); root = rotateRight(root); } else if (key > root->left->val) { root->left->right = splay(root->left->right, key); if (root->left->right) root->left = rotateLeft(root->left); } return root->left ? rotateRight(root) : root; } else { if (!root->right) return root; if (key < root->right->val) { root->right->left = splay(root->right->left, key); if (root->right->left) root->right = rotateRight(root->right); } else if (key > root->right->val) { root->right->right = splay(root->right->right, key); root = rotateLeft(root); } return root->right ? rotateLeft(root) : root; } } Node* insert(Node* root, int key) { if (!root) return new Node(key); root = splay(root, key); if (root->val == key) return root; Node* newNode = new Node(key); if (key < root->val) { newNode->right = root; newNode->left = root->left; root->left = nullptr; } else { newNode->left = root; newNode->right = root->right; root->right = nullptr; } updateSize(newNode); return newNode; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值