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结点优先级的值,则需要左旋转(以最小堆为例,最终都是为了让优先级小的值在上面,大的在下面,这也是堆的性质);

  1. 按照BST的性质插入结点到叶子上;
  2. 按照堆的性质维护(左或者右旋转)树;

删除

删除的结点的情况有几种,需要掌握;

  • 叶子结点:直接删除;
  • 只包含一个叶子结点的结点:将叶子结点赋到该结点上;
  • 正常结点:找左右子结点中优先级小的结点,进行反方向的旋转,如果左结点小,则右旋转,如果右结点小,则左旋,当然删除的还是它自己(注意应该先旋转,后删除)

实现

代码用C++实现:

Treap.hpp

  1. //
  2. // Treap.hpp
  3. // Treap
  4. //
  5. // Created by George on 16/10/5.
  6. // Copyright © 2016年 George. All rights reserved.
  7. //
  8. #ifndef Treap_hpp
  9. #define Treap_hpp
  10. #include <stdio.h>
  11. namespace Treap {
  12. class TreeNode {
  13. public:
  14. TreeNode();
  15. TreeNode(int value, int priority);
  16. ~TreeNode();
  17. TreeNode * _left;
  18. TreeNode * _right;
  19. int _key;
  20. int _priority;
  21. };
  22. class Tree {
  23. public:
  24. Tree();
  25. ~Tree();
  26. void Insert(TreeNode* node, TreeNode* &root);
  27. void Delete(int key, TreeNode* &root);
  28. TreeNode* Search(int key, TreeNode* &root);
  29. void Traverse(TreeNode* root);
  30. TreeNode * _root;
  31. private:
  32. void insertNode(TreeNode* node);
  33. void deleteNode(int key);
  34. TreeNode* searchNode(int key);
  35. void traverse();
  36. void rotateLeft(TreeNode* &node);
  37. void rotateRight(TreeNode* &node);
  38. };
  39. void insertNode();
  40. void deleteNode();
  41. void searchNode();
  42. void traverseNode();
  43. }
  44. #endif /* Treap_hpp */

Treap.cpp

  1. //
  2. // Treap.cpp
  3. // Treap
  4. //
  5. // Created by George on 16/10/5.
  6. // Copyright © 2016年 George. All rights reserved.
  7. //
  8. #include "Treap.hpp"
  9. #include <cassert>
  10. #include <stack>
  11. #include <iostream>
  12. #include <random>
  13. #include <ctime>
  14. #include <string>
  15. namespace Treap {
  16. void LogNode(TreeNode* node) {
  17. std::cout << "key :" << node->_key << ", priority :" << node->_priority << std::endl;
  18. }
  19. std::string GetInputString(const std::string content) {
  20. std::cout << content << " :";
  21. std::string input;
  22. std::cin >> input;
  23. return input;
  24. }
  25. TreeNode::TreeNode() : _left(nullptr), _right(nullptr), _key(-1), _priority(-1) {
  26. }
  27. TreeNode::TreeNode(int value, int priority) {
  28. _key = value;
  29. _priority = priority;
  30. _left = nullptr;
  31. _right = nullptr;
  32. }
  33. TreeNode::~TreeNode() {
  34. }
  35. Tree::Tree() {
  36. _root = nullptr;
  37. }
  38. Tree::~Tree() {
  39. }
  40. /**
  41. * 三种情况:小于跟结点和大于跟结点以及跟结点为空
  42. *
  43. * @param node <#node description#>
  44. * @param root <#root description#>
  45. */
  46. void Tree::Insert(TreeNode* node, TreeNode* &root) {
  47. if (root == nullptr) {
  48. root = node;
  49. root->_left = nullptr;
  50. root->_right = nullptr;
  51. }
  52. else if (node->_key > root->_key) {
  53. // 往右边插入结点
  54. Insert(node, root->_right);
  55. // 向左旋转
  56. if (root->_right->_priority < root->_priority) {
  57. rotateLeft(root);
  58. }
  59. }
  60. else if (node->_key < root->_key) {
  61. // 往左边插入结点
  62. Insert(node, root->_left);
  63. // 向右旋转
  64. if (root->_left->_priority < root->_priority) {
  65. rotateRight(root);
  66. }
  67. }
  68. }
  69. /**
  70. * 重点在于其中决定删除那个结点,也就是翻转的情况
  71. *
  72. * @param node <#node description#>
  73. * @param root <#root description#>
  74. */
  75. void Tree::Delete(int key, TreeNode* &root) {
  76. if (root != nullptr) {
  77. if (key > root->_key) {
  78. Delete(key, root->_right);
  79. }
  80. else if (key < root->_key) {
  81. Delete(key, root->_left);
  82. }
  83. else {
  84. // 叶子结点
  85. if (root->_left == nullptr && root->_right == nullptr) {
  86. delete root;
  87. root = nullptr;
  88. }
  89. // 包含一个叶子结点的结点
  90. else if (root->_left == nullptr || root->_right == nullptr) {
  91. if (root->_left) {
  92. root = root->_left;
  93. root->_left = nullptr;
  94. }
  95. else if (root->_right) {
  96. root = root->_right;
  97. root->_right = nullptr;
  98. }
  99. }
  100. else {
  101. // 向右翻转
  102. if (root->_left->_priority < root->_right->_priority) {
  103. rotateRight(root);
  104. Delete(key, root->_right);
  105. }
  106. // 向左翻转
  107. else {
  108. rotateLeft(root);
  109. Delete(key, root->_left);
  110. }
  111. }
  112. }
  113. }
  114. }
  115. /**
  116. * 迭代查找
  117. *
  118. * @param key <#key description#>
  119. * @param root <#root description#>
  120. *
  121. * @return <#return value description#>
  122. */
  123. TreeNode* Tree::Search(int key, TreeNode* &root) {
  124. std::stack<TreeNode *> stack;
  125. stack.push(root);
  126. TreeNode * resultNode = nullptr;
  127. while (!stack.empty()) {
  128. TreeNode * node = stack.top();
  129. stack.pop();
  130. if (node->_key == key) {
  131. resultNode = node;
  132. break;
  133. }
  134. else if (node->_key > key) {
  135. if (node->_left) {
  136. stack.push(node->_left);
  137. }
  138. }
  139. else {
  140. if (node->_right) {
  141. stack.push(node->_right);
  142. }
  143. }
  144. }
  145. return resultNode;
  146. }
  147. /**
  148. * 中序遍历得到顺序值
  149. *
  150. * @param root <#root description#>
  151. */
  152. void Tree::Traverse(TreeNode* root) {
  153. if (root == nullptr) {
  154. return;
  155. }
  156. if (root->_left) Traverse(root->_left);
  157. LogNode(root);
  158. if (root->_right) Traverse(root->_right);
  159. }
  160. void Tree::insertNode(TreeNode* node) {
  161. assert(node == nullptr);
  162. Insert(node, _root);
  163. }
  164. void Tree::deleteNode(int key) {
  165. Delete(key, _root);
  166. }
  167. TreeNode* Tree::searchNode(int value) {
  168. return Search(value, _root);
  169. }
  170. void Tree::traverse() {
  171. Traverse(_root);
  172. }
  173. void Tree::rotateLeft(TreeNode* &node) {
  174. TreeNode* rNode = node->_right;
  175. node->_right = rNode->_left;
  176. rNode->_left = node;
  177. node = rNode;
  178. }
  179. void Tree::rotateRight(TreeNode* &node) {
  180. TreeNode* lNode = node->_left;
  181. node->_left = lNode->_right;
  182. lNode->_right = node;
  183. node = lNode;
  184. }
  185. double random(double start, double end)
  186. {
  187. return start+(end-start)*rand()/(RAND_MAX + 1.0);
  188. }
  189. static Tree * tree = new Tree();
  190. void insertNode() {
  191. srand(unsigned(time(0)));
  192. for (int i = 1; i <= 10; ++i) {
  193. int priority = int(random(1, 100));
  194. TreeNode * node = new TreeNode(i, priority);
  195. tree->Insert(node, tree->_root);
  196. }
  197. }
  198. void deleteNode() {
  199. std::string input = GetInputString("input the delete key");
  200. int key = atoi(input.c_str());
  201. tree->Delete(key, tree->_root);
  202. }
  203. void searchNode() {
  204. std::string input = GetInputString("input the search key");
  205. int key = atoi(input.c_str());
  206. TreeNode * node = tree->Search(key, tree->_root);
  207. LogNode(node);
  208. }
  209. void traverseNode() {
  210. tree->Traverse(tree->_root);
  211. }
  212. }

main.cpp

  1. //
  2. // main.cpp
  3. // Treap
  4. //
  5. // Created by George on 16/10/5.
  6. // Copyright © 2016年 George. All rights reserved.
  7. //
  8. #include <iostream>
  9. #include "Treap.hpp"
  10. int main(int argc, const char * argv[]) {
  11. // insert code here...
  12. Treap::insertNode();
  13. Treap::traverseNode();
  14. Treap::deleteNode();
  15. Treap::traverseNode();
  16. Treap::searchNode();
  17. return 0;
  18. }

运行结果:

应用

在别的地方看到的应用

[问题描述] 有一个游戏排名系统,通常要应付三种请求:上传一条新的得分记录、查询某个玩家的当前 排名以及返回某个区段内的排名记录。当某个玩家上传自己最新的得分记录时,他原有的得 分记录会被删除。为了减轻服务器负担,在返回某个区段内的排名记录时,最多返回 10 条 记录。[求]

  1. 更新玩家的得分记录
  2. 查询玩家排名(如果两个玩家的得分相同, 则先得到该得分的玩家排在前面。)
  3. 查询第 Index 名开始的最多 10 名玩家名字

[解]

因为作为字符串的姓名是不便于处理的,我们给每个玩家都制定一个ID,首先要建立一个由姓名到玩家ID的映射数据结构。为了查找快速,可以用Trie树。之后我们建立一个双关键字的Treap,关键字1为得分从小到大,关键字2为时间戳从大到小,这种排列方式的逆序,恰好是我们要的顺序(也可以直接就是逆序)。

对于问题(1),先查询玩家是否已经存在,如果已经存在,在Treap中更新对应已经存在的记录。

对于问题(2),就是基本的求排名操作。

对于问题(3),就是分别查找第(总记录数 + 1 – k)小的记录。

Treap树理解的更多相关文章

  1. 6天通吃树结构—— 第三天 Treap树

    原文:6天通吃树结构-- 第三天 Treap树 我们知道,二叉查找树相对来说比较容易形成最坏的链表情况,所以前辈们想尽了各种优化策略,包括AVL,红黑,以及今天 要讲的Treap树. Treap树算是 ...

  2. POJ-3481 Double Queue,Treap树和set花式水过!

                                                    Double Queue 本打算学二叉树,单纯的二叉树感觉也就那几种遍历了, 无意中看到了这个题,然后就 ...

  3. bzoj2141 树状数组套Treap树

    题目大意是在能够改变两个数的位置的情况下计算逆序对数 这因为是动态记录逆序对 本来单纯逆序对只要用树状数组计算即可,但这里因为更新,所以利用TReap树的删点和增加点来进行更新 大致是把每个树状数组所 ...

  4. treap树模板

    ///treap树模板 typedef struct Node ///节点的结构体 { Node *l,*r; int val,pri; ///节点的值和优先级 int sz; ///节点子树的节点数 ...

  5. poj 2761 Feed the dogs (treap树)

    /************************************************************* 题目: Feed the dogs(poj 2761) 链接: http: ...

  6. treap树---营业额统计

    台州学院  2924 描述 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况.Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额 ...

  7. treap树---Double Queue

    HDU   1908 Description The new founded Balkan Investment Group Bank (BIG-Bank) opened a new office i ...

  8. Treap树

    Treap树算是一种简单的优化策略,这名字大家也能猜到,树和堆的合体,其实原理比较简单,在树中维护一个"优先级“,”优先级“ 采用随机数的方法,但是”优先级“必须满足根堆的性质,当然是“大根 ...

  9. BZOJ3224/LOJ104 普通平衡树 treap(树堆)

    您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x2. 删除x(若有多个相同的数,因只删除一个)3. 查询x的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. ...

随机推荐

  1. 【黑客免杀攻防】读书笔记18-最终章Anti Rootkit

    1.免杀技巧的遏制 1.1.PE文件 入口点不在第一个区段或在最后一个区段 入口点处代码附近只有一小段代码 入口点在正常范围之外 入口点为一个无效的值,实际入口点为TLS的入口点 区段名重复或者不属于 ...

  2. PostgreSQL内核分析——BTree索引

    文中附图参考至<PostgreSQL数据库内核分析> (一)概念描述 B+树是一种索引数据结构,其一个特征在于非叶子节点用于描述索引,而叶子节点指向具体的数据存储位置.在PostgreSQ ...

  3. Linux移植随笔:对tslib库的ts_test测试程序代码的一点分析【转】

    转自:http://www.latelee.org/embedded-linux/porting-linux-tstest-code.html 本文是作者对tslib库的ts_test.c文件进行分析 ...

  4. nginx自定义500,502,504错误页面无法跳转【转】

    1.自定一个页面,这个页面是一个链接地址可以直接访问的. 以下是nginx的配置: location / {            proxy_pass http://tomcat_app108;   ...

  5. js实现ctrl+v粘贴图片或是截图

    浏览器环境:谷歌浏览器 1.ctrl+v粘贴图片都是监听paste时间实现的,复制的数据都存在clipboardData下面,虽然打印显示数据长度为0,但是还是可以获取数据的 2.打印clipboar ...

  6. Codeforces 580D Kefa and Dishes(状态压缩DP)

    题目链接:http://codeforces.com/problemset/problem/580/D 题目大意:有n盘菜每个菜都有一个满意度,k个规则,每个规则由x y c组成,表示如果再y之前吃x ...

  7. (一)问候 HttpClient

    第一节: HttpClient 简介 HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的.最新的.功能丰富的支持 HTTP 协议的客户端编程工具包,并 ...

  8. iOS中URL的解码和转义问题

    在iOS开发中,使用NSURLConnection去请求google places api时,如果请求的url中包含中文,则返回的结果为空,URL不能被google识别.NSString *_urlS ...

  9. 二进制方式部署Kubernetes 1.6.0集群(开启TLS)

    本节内容: Kubernetes简介 环境信息 创建TLS加密通信的证书和密钥 下载和配置 kubectl(kubecontrol) 命令行工具 创建 kubeconfig 文件 创建高可用 etcd ...

  10. bash101总结

    看了 bash101 ,做的一些总结吧,都是些常见用法,易错 1. 有空格会显示多行 2.contine 书里太细了,有空补起来