洛谷P3369题解:Treap数据结构从入门到精通

贾蔷
• 阅读 9

洛谷P3369题解:Treap数据结构从入门到精通

一、数据结构概述

Treap是一种同时具备二叉搜索树(BST)和(Heap)性质的数据结构,通过随机优先级维护平衡性,实现高效的插入、删除和查询操作。

二、核心实现解析

  1. 节点结构:包含值(val)、计数(cnt)、子树大小(size)和随机优先级(priority)
  2. 旋转操作:左旋(rotateLeft)和右旋(rotateRight)维护堆性质
  3. 更新机制:update函数维护子树大小信息
  4. 六大核心操作:插入、删除、查排名、查值、前驱、后继

三、完整代码实现(带详细注释)

#include <iostream>
#include <cstdlib>
#include <climits>
using namespace std;

const int INF = 1e8; // 处理大数值范围

struct Node {
    int val, size, cnt;
    int priority;
    Node *l, *r;
    Node(int v) : val(v), size(1), cnt(1), l(nullptr), r(nullptr) {
        priority = rand() % INF; // 随机优先级保证平衡
    }
};

class Treap {
private:
    Node *root;

    // 更新节点子树大小
    void update(Node *node) {
        if(!node) return;
        node->size = node->cnt;
        if(node->l) node->size += node->l->size;
        if(node->r) node->size += node->r->size;
    }

    // 左旋操作
    void rotateLeft(Node *&node) {
        Node *temp = node->r;
        node->r = temp->l;
        temp->l = node;
        update(node);
        update(temp);
        node = temp;
    }

    // 右旋操作
    void rotateRight(Node *&node) {
        Node *temp = node->l;
        node->l = temp->r;
        temp->r = node;
        update(node);
        update(temp);
        node = temp;
    }

    // 插入操作
    void insert(Node *&node, int val) {
        if(!node) {
            node = new Node(val);
            return;
        }
        if(val == node->val) {
            node->cnt++; // 重复值计数
        } else if(val < node->val) {
            insert(node->l, val);
            if(node->l->priority > node->priority)
                rotateRight(node); // 维护堆性质
        } else {
            insert(node->r, val);
            if(node->r->priority > node->priority)
                rotateLeft(node); // 维护堆性质
        }
        update(node);
    }

    // 删除操作
    void remove(Node *&node, int val) {
        if(!node) return;
        if(val < node->val) {
            remove(node->l, val);
        } else if(val > node->val) {
            remove(node->r, val);
        } else {
            if(node->cnt > 1) {
                node->cnt--; // 减少计数
            } else {
                if(!node->l || !node->r) {
                    Node *temp = node->l ? node->l : node->r;
                    delete node;
                    node = temp; // 单子树情况直接替换
                } else {
                    // 选择优先级高的子树旋转
                    if(node->l->priority > node->r->priority) {
                        rotateRight(node);
                        remove(node->r, val);
                    } else {
                        rotateLeft(node);
                        remove(node->l, val);
                    }
                }
            }
        }
        if(node) update(node);
    }

    // 获取排名
    int getRank(Node *node, int val) {
        if(!node) return 0;
        if(val < node->val) return getRank(node->l, val);
        int leftSize = node->l ? node->l->size : 0;
        if(val == node->val) return leftSize + 1;
        return leftSize + node->cnt + getRank(node->r, val);
    }

    // 根据排名获取值
    int getValue(Node *node, int rank) {
        if(!node) return INF;
        int leftSize = node->l ? node->l->size : 0;
        if(rank <= leftSize) return getValue(node->l, rank);
        if(rank <= leftSize + node->cnt) return node->val;
        return getValue(node->r, rank - leftSize - node->cnt);
    }

    // 获取前驱
    int getPre(Node *node, int val) {
        if(!node) return -INF;
        if(node->val >= val) return getPre(node->l, val);
        return max(node->val, getPre(node->r, val));
    }

    // 获取后继
    int getNext(Node *node, int val) {
        if(!node) return INF;
        if(node->val <= val) return getNext(node->r, val);
        return min(node->val, getNext(node->l, val));
    }

public:
    Treap() : root(nullptr) { srand(time(0)); }

    // 公开接口
    void insert(int val) { insert(root, val); }
    void remove(int val) { remove(root, val); }
    int getRank(int val) { return getRank(root, val); }
    int getValue(int rank) { return getValue(root, rank); }
    int getPre(int val) { return getPre(root, val); }
    int getNext(int val) { return getNext(root, val); }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    Treap treap;
    int n, opt, x;
    cin >> n;
    while(n--) {
        cin >> opt >> x;
        switch(opt) {
            case 1: treap.insert(x); break;
            case 2: treap.remove(x); break;
            case 3: cout << treap.getRank(x) << '\n'; break;
            case 4: cout << treap.getValue(x) << '\n'; break;
            case 5: cout << treap.getPre(x) << '\n'; break;
            case 6: cout << treap.getNext(x) << '\n'; break;
        }
    }
    return 0;
}

四、实际应用场景

  1. 动态排名系统
  2. 数据库索引
  3. 游戏排行榜
  4. 实时数据分析

五、学习建议

  1. 先理解BST和堆的基本原理
  2. 手动模拟小规模数据操作
  3. 尝试实现其他平衡树如AVL、红黑树
  4. 练习相关题目巩固理解

来源:洛谷题解

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java线程优先级
java中的线程优先级的范围是1~10,默认的优先级是5。10最高。MIN\_PRIORITY 1MAX\_PRIORITY10NORM\_PRIORITY5优先级高的获得cpu的几率更大些,不是优先级高的就先执行完,线程优先级随机特性在java中,线程的优先级具有继承性,例如A线程启动B线程,则A和B的优先级是一样的线程创建
灯灯灯灯 灯灯灯灯
4年前
「JDK——ArrayList源码」超强解析,图文详解
ArrayList源码解析简介ArrayList是Java集合框架中非常常用的一种数据结构。继承自AbstractList,实现了List接口。底层基于数组来实现动态容量大小的控制,允许null值的存在。同时还实现了RandomAccess、Cloneable、Serializable接口,支持快速访问、复制、序列化操作。了解数组数组简单来说就是将所有的
Wesley13 Wesley13
3年前
01.Java数据结构和多线程
数据结构数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。不同的数据结构的操作性能是不同的:(有的查询性能很快,有的插入速度很快,有的是插入头和尾速度很快,有的做等值判断很快,有的做范围查找很快,有的允许元素重复,有的不允许重复等等),在开发中如何选择,要根据具体的需求来选择.
深度学习 深度学习
3星期前
力扣701题:二叉搜索树插入操作 - 递归解法详解
一、内容简介本文详细解析了力扣701题"二叉搜索树中的插入操作"的递归实现方法。通过遵循二叉搜索树的性质,展示了如何高效地在BST中插入新节点。文章包含完整注释代码、算法思路讲解和复杂度分析,帮助读者掌握BST操作的核心技巧。二、算法思路‌递归终止条件‌:
深度学习 深度学习
3星期前
链表栈实现指南:从基础到实践
一、简介和特点链表栈是一种使用链表实现的栈数据结构,遵循后进先出(LIFO)原则。本文实现的链表栈通过动态内存分配节点,避免了数组实现的固定大小限制。‌主要特点‌:1.动态大小:无需预先分配固定空间2.高效操作:入栈和出栈都是O(1)时间复杂度3.内存效率
贾蔷 贾蔷
3星期前
手把手教你实现哈希表:从代码到原理的新手友好指南
一、简介和应用哈希表(HashTable)是一种高效的数据结构,通过哈希函数将键(Key)映射到存储位置,实现O(1)时间复杂度的查找、插入和删除操作。它广泛应用于缓存系统、数据库索引、字典查询等场景。例如,在编程中需要快速根据用户ID查找信息时,哈希表能
贾蔷 贾蔷
1星期前
洛谷P3365 改造二叉树:从问题分析到代码实现
一、问题分析题目要求我们计算将修改为(BST)所需的最少修改次数。二叉搜索树的性质是:对于任意节点,其左子树所有节点的值都小于该节点的值,右子树所有节点的值都大于该节点的值。二、解题思路1.‌序列‌:BST的中序遍历结果是一个严格1.‌问题转化‌:将原二叉
贾蔷 贾蔷
3星期前
二叉树入门指南:从零开始理解树形数据结构
一、简介和应用二叉树是一种重要的非线性数据结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。它在计算机科学中有广泛的应用,是许多高级数据结构的基础。‌应用场景‌:1.数据库索引(如B树、B树)2.文件系统目录结构3.表达式树(用于编译器实现)4
贾蔷 贾蔷
3星期前
哈希表实现指南:从原理到C++实践
一、简介和应用哈希表(HashTable)是一种高效的数据结构,通过键值对(keyvalue)存储数据,提供快速的插入、删除和查找操作。它使用哈希函数将键映射到表中的位置,使得平均时间复杂度可以达到O(1)。‌应用场景‌:数据库索引、缓存实现(如Redis