Treap树理解
title: Treap树理解
comments: true
date: 2016-10-06 07:57:37
categories: 算法
tags:
- Treap树
- 树
Treap树理解
简介
随机平衡二叉查找树
Treap树其实就是BST+Heap,在值的比较上,按照二叉树的性质,在优先度的比较上,按照堆的性质,当然最小最大堆都可以;
Treap树算是平衡树中的一种,但是其又并不满足于平衡树中的性质-“左右子树的高度小于等于1”,当然,我们还知道还有其他的平衡树,比如红黑树,Splay Tree(伸展树),Size Balance Tree,AVL树等(后面打算做个平衡树的总结)
性质
先看二叉树
- 左右子树上结点的值均小于其跟结点的值;
- 左右子树均是二叉搜索树
再看堆(以最小堆为例)
- 任何一个结点的优先级的值都要小于其跟结点的优先级的值(其优先值随机生成);
操作
查找
其操作和BST一样,通过判断结点的值进行左右的查找,不再赘述;
插入
(重点!!!)
我们先来了解一下左旋转和右旋转的概念

上图左边那幅图是X结点优先级的值小于Y结点优先级的值,则需要右旋转,右边那幅图则是Y结点优先级的值小于X结点优先级的值,则需要左旋转(以最小堆为例,最终都是为了让优先级小的值在上面,大的在下面,这也是堆的性质);
- 按照BST的性质插入结点到叶子上;
- 按照堆的性质维护(左或者右旋转)树;
删除
删除的结点的情况有几种,需要掌握;
- 叶子结点:直接删除;
- 只包含一个叶子结点的结点:将叶子结点赋到该结点上;
- 正常结点:找左右子结点中优先级小的结点,进行反方向的旋转,如果左结点小,则右旋转,如果右结点小,则左旋,当然删除的还是它自己(注意应该先旋转,后删除)
实现
代码用C++实现:
Treap.hpp
//
// Treap.hpp
// Treap
//
// Created by George on 16/10/5.
// Copyright © 2016年 George. All rights reserved.
//
#ifndef Treap_hpp
#define Treap_hpp
#include <stdio.h>
namespace Treap {
class TreeNode {
public:
TreeNode();
TreeNode(int value, int priority);
~TreeNode();
TreeNode * _left;
TreeNode * _right;
int _key;
int _priority;
};
class Tree {
public:
Tree();
~Tree();
void Insert(TreeNode* node, TreeNode* &root);
void Delete(int key, TreeNode* &root);
TreeNode* Search(int key, TreeNode* &root);
void Traverse(TreeNode* root);
TreeNode * _root;
private:
void insertNode(TreeNode* node);
void deleteNode(int key);
TreeNode* searchNode(int key);
void traverse();
void rotateLeft(TreeNode* &node);
void rotateRight(TreeNode* &node);
};
void insertNode();
void deleteNode();
void searchNode();
void traverseNode();
}
#endif /* Treap_hpp */
Treap.cpp
//
// Treap.cpp
// Treap
//
// Created by George on 16/10/5.
// Copyright © 2016年 George. All rights reserved.
//
#include "Treap.hpp"
#include <cassert>
#include <stack>
#include <iostream>
#include <random>
#include <ctime>
#include <string>
namespace Treap {
void LogNode(TreeNode* node) {
std::cout << "key :" << node->_key << ", priority :" << node->_priority << std::endl;
}
std::string GetInputString(const std::string content) {
std::cout << content << " :";
std::string input;
std::cin >> input;
return input;
}
TreeNode::TreeNode() : _left(nullptr), _right(nullptr), _key(-1), _priority(-1) {
}
TreeNode::TreeNode(int value, int priority) {
_key = value;
_priority = priority;
_left = nullptr;
_right = nullptr;
}
TreeNode::~TreeNode() {
}
Tree::Tree() {
_root = nullptr;
}
Tree::~Tree() {
}
/**
* 三种情况:小于跟结点和大于跟结点以及跟结点为空
*
* @param node <#node description#>
* @param root <#root description#>
*/
void Tree::Insert(TreeNode* node, TreeNode* &root) {
if (root == nullptr) {
root = node;
root->_left = nullptr;
root->_right = nullptr;
}
else if (node->_key > root->_key) {
// 往右边插入结点
Insert(node, root->_right);
// 向左旋转
if (root->_right->_priority < root->_priority) {
rotateLeft(root);
}
}
else if (node->_key < root->_key) {
// 往左边插入结点
Insert(node, root->_left);
// 向右旋转
if (root->_left->_priority < root->_priority) {
rotateRight(root);
}
}
}
/**
* 重点在于其中决定删除那个结点,也就是翻转的情况
*
* @param node <#node description#>
* @param root <#root description#>
*/
void Tree::Delete(int key, TreeNode* &root) {
if (root != nullptr) {
if (key > root->_key) {
Delete(key, root->_right);
}
else if (key < root->_key) {
Delete(key, root->_left);
}
else {
// 叶子结点
if (root->_left == nullptr && root->_right == nullptr) {
delete root;
root = nullptr;
}
// 包含一个叶子结点的结点
else if (root->_left == nullptr || root->_right == nullptr) {
if (root->_left) {
root = root->_left;
root->_left = nullptr;
}
else if (root->_right) {
root = root->_right;
root->_right = nullptr;
}
}
else {
// 向右翻转
if (root->_left->_priority < root->_right->_priority) {
rotateRight(root);
Delete(key, root->_right);
}
// 向左翻转
else {
rotateLeft(root);
Delete(key, root->_left);
}
}
}
}
}
/**
* 迭代查找
*
* @param key <#key description#>
* @param root <#root description#>
*
* @return <#return value description#>
*/
TreeNode* Tree::Search(int key, TreeNode* &root) {
std::stack<TreeNode *> stack;
stack.push(root);
TreeNode * resultNode = nullptr;
while (!stack.empty()) {
TreeNode * node = stack.top();
stack.pop();
if (node->_key == key) {
resultNode = node;
break;
}
else if (node->_key > key) {
if (node->_left) {
stack.push(node->_left);
}
}
else {
if (node->_right) {
stack.push(node->_right);
}
}
}
return resultNode;
}
/**
* 中序遍历得到顺序值
*
* @param root <#root description#>
*/
void Tree::Traverse(TreeNode* root) {
if (root == nullptr) {
return;
}
if (root->_left) Traverse(root->_left);
LogNode(root);
if (root->_right) Traverse(root->_right);
}
void Tree::insertNode(TreeNode* node) {
assert(node == nullptr);
Insert(node, _root);
}
void Tree::deleteNode(int key) {
Delete(key, _root);
}
TreeNode* Tree::searchNode(int value) {
return Search(value, _root);
}
void Tree::traverse() {
Traverse(_root);
}
void Tree::rotateLeft(TreeNode* &node) {
TreeNode* rNode = node->_right;
node->_right = rNode->_left;
rNode->_left = node;
node = rNode;
}
void Tree::rotateRight(TreeNode* &node) {
TreeNode* lNode = node->_left;
node->_left = lNode->_right;
lNode->_right = node;
node = lNode;
}
double random(double start, double end)
{
return start+(end-start)*rand()/(RAND_MAX + 1.0);
}
static Tree * tree = new Tree();
void insertNode() {
srand(unsigned(time(0)));
for (int i = 1; i <= 10; ++i) {
int priority = int(random(1, 100));
TreeNode * node = new TreeNode(i, priority);
tree->Insert(node, tree->_root);
}
}
void deleteNode() {
std::string input = GetInputString("input the delete key");
int key = atoi(input.c_str());
tree->Delete(key, tree->_root);
}
void searchNode() {
std::string input = GetInputString("input the search key");
int key = atoi(input.c_str());
TreeNode * node = tree->Search(key, tree->_root);
LogNode(node);
}
void traverseNode() {
tree->Traverse(tree->_root);
}
}
main.cpp
//
// main.cpp
// Treap
//
// Created by George on 16/10/5.
// Copyright © 2016年 George. All rights reserved.
//
#include <iostream>
#include "Treap.hpp"
int main(int argc, const char * argv[]) {
// insert code here...
Treap::insertNode();
Treap::traverseNode();
Treap::deleteNode();
Treap::traverseNode();
Treap::searchNode();
return 0;
}
运行结果:

应用
在别的地方看到的应用
[问题描述] 有一个游戏排名系统,通常要应付三种请求:上传一条新的得分记录、查询某个玩家的当前 排名以及返回某个区段内的排名记录。当某个玩家上传自己最新的得分记录时,他原有的得 分记录会被删除。为了减轻服务器负担,在返回某个区段内的排名记录时,最多返回 10 条 记录。[求]
- 更新玩家的得分记录
- 查询玩家排名(如果两个玩家的得分相同, 则先得到该得分的玩家排在前面。)
- 查询第 Index 名开始的最多 10 名玩家名字
[解]
因为作为字符串的姓名是不便于处理的,我们给每个玩家都制定一个ID,首先要建立一个由姓名到玩家ID的映射数据结构。为了查找快速,可以用Trie树。之后我们建立一个双关键字的Treap,关键字1为得分从小到大,关键字2为时间戳从大到小,这种排列方式的逆序,恰好是我们要的顺序(也可以直接就是逆序)。
对于问题(1),先查询玩家是否已经存在,如果已经存在,在Treap中更新对应已经存在的记录。
对于问题(2),就是基本的求排名操作。
对于问题(3),就是分别查找第(总记录数 + 1 – k)小的记录。
Treap树理解的更多相关文章
- 6天通吃树结构—— 第三天 Treap树
原文:6天通吃树结构-- 第三天 Treap树 我们知道,二叉查找树相对来说比较容易形成最坏的链表情况,所以前辈们想尽了各种优化策略,包括AVL,红黑,以及今天 要讲的Treap树. Treap树算是 ...
- POJ-3481 Double Queue,Treap树和set花式水过!
Double Queue 本打算学二叉树,单纯的二叉树感觉也就那几种遍历了, 无意中看到了这个题,然后就 ...
- bzoj2141 树状数组套Treap树
题目大意是在能够改变两个数的位置的情况下计算逆序对数 这因为是动态记录逆序对 本来单纯逆序对只要用树状数组计算即可,但这里因为更新,所以利用TReap树的删点和增加点来进行更新 大致是把每个树状数组所 ...
- treap树模板
///treap树模板 typedef struct Node ///节点的结构体 { Node *l,*r; int val,pri; ///节点的值和优先级 int sz; ///节点子树的节点数 ...
- poj 2761 Feed the dogs (treap树)
/************************************************************* 题目: Feed the dogs(poj 2761) 链接: http: ...
- treap树---营业额统计
台州学院 2924 描述 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况.Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额 ...
- treap树---Double Queue
HDU 1908 Description The new founded Balkan Investment Group Bank (BIG-Bank) opened a new office i ...
- Treap树
Treap树算是一种简单的优化策略,这名字大家也能猜到,树和堆的合体,其实原理比较简单,在树中维护一个"优先级“,”优先级“ 采用随机数的方法,但是”优先级“必须满足根堆的性质,当然是“大根 ...
- BZOJ3224/LOJ104 普通平衡树 treap(树堆)
您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x2. 删除x(若有多个相同的数,因只删除一个)3. 查询x的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. ...
随机推荐
- 生成Word/ATU报表提示 font family not found
1.先从你本机 C:\Windows\Fonts 拷贝或者网络上下载你想要安装的字体文件(*.ttf文件)到 /usr/share/fonts/chinese/TrueType 目录下(如果系统中没有 ...
- 管道和xargs区别
一直弄不懂,管道不就是把前一个命令的结果作为参数给下一个命令吗,那在 | 后面加不加xargs有什么区别 NewUserFF 写道: 懒蜗牛Gentoo 写道: 管道是实现“将前面的标准输出作为后面的 ...
- quartz的一个误导
quartz文档提到,如果在集群环境下,最好将配置项org.quartz.jobStore.txIsolationLevelSerializable设置为true 问题: 这个选项在mysql下会非常 ...
- docker容器配置独立ip
一般安装docker后都会通过端口转发的方式使用网络,比如 “-p 2294:22” 就将2294抓发到22端口来提供sftp服务,这样使用起来没有问题.但端口号很难记忆,如果前边有nginx等抓发工 ...
- 洛谷P3385负环
传送门 #include <iostream> #include <cstdio> #include <cstring> #include <algorith ...
- T-sql语句修改数据库逻辑名、数据库名、物理名(sql2000)
--更改MSSQL数据库物理文件名Sql语句的写法 --注意:要在活动监视器里面确保没有进程连接你要改名的数据库!!!!!!!!!!!!!!!!!!!! -- Sql语句如下 USE master - ...
- sql server中分布式查询随笔(链接服务器(sp_addlinkedserver)和远程登录映射(sp_addlinkedsrvlogin)使用小总结)
由于业务逻辑的多样性,经常得在sql server中查询不同数据库中数据,这就产生了分布式查询的需求 现我将开发中遇到的几种查询总结如下: 1.access版本 --建立连接服务器 EXEC sp_a ...
- 20165333 学习基础和C语言学习基础
说实话,我并没有什么技能比90%以上的人更好,非要拿一个出来的话,篮球勉强好一点吧.最初接触篮球是小学的时候跟着哥哥看他打球,哥哥的球技在同龄人中算是好的,每次看他各种突破过人,我都觉得特别潇洒帅气, ...
- SqlServer性能优化 Sql语句优化(十四)
一:在较小的结果集上上操作 1.仅返回需要的列 2.分页获取数据 EF实现分页: public object getcp(int skiprows,int currentpagerows) { HRU ...
- 【51nod】1559 车和矩形
题解 离线读入,我们发现一个矩形能被保护,矩形内部所有列上必定有一辆车,或者所有行上必定有一辆车 分两次进行处理 第一次按照横坐标把车加进去,然后查询最大横坐标在这个位置的矩形,纵坐标区间里的车出现位 ...