树堆结构
来源:互联网 发布:武汉人工智能创业公司 编辑:程序博客网 时间:2024/06/05 23:58
树堆是节点带优先级的二叉查找树。
设计原因:
二叉查找树是为了让查找的平均效率保持在O(lgn)。节点带优先级的原因是为了让增删节点后的树的高度大致保持在ln(n),避免查找的效率退化成O(n)。
树堆有两个特性:
(1) 二叉查找树特性
1)如果v是u的左节点,则v的key小于u的key
2)如果v是u的右节点,则v的key大于u的key
(2)最小(大)堆特性
如果v是u的孩子,则v的priority大于 u的 priority
如下图:
树堆的构造:
假设插入关联关键字的结点x1,x2,...,xn到一棵树堆内。结果的树堆是将这些结点以它们的优先级(随机选取)的顺序插入一棵正常的二叉查找树形成的,亦即priority[xi] < priority[xj]表示xi在xj之前被插入。
在算法导论的12.4节中,其证明了随机构造的二叉查找树的期望高度为O(lgn),因而树堆的期望高度亦是O(lgn)。
由于旋转是O(1)的,最多进行h次(h是树的高度),插入的复杂度是O(h)的,在期望情况下h=O(log n),所以它的期望复杂度是O(log n)。
树堆插入操作:
1.按照二叉树的插入方法,将节点插入到树中
2.根据堆的性质(这里是最小堆)和优先级的大小调整节点位置:
1)先根据符节点选择插入的方向
2)根据优先级选择是否翻转节点
树堆删除操作:
1)找到键相等的结点
2)若该节点为叶子节点,则删除;
3)若该节点只有一个子节点,则将其叶子节点赋值给它;
4)若该节点有两个子节点,则进行相应的旋转,直到该节点出现2)或3)的情况,然后删除。
旋转情况包括:右旋转和左旋转
如图:
插入和删除需要翻转节点,每次翻转存在4个步骤,所以一次操作复杂度估算是4lnN。插入和删除操作较多的情况下并不划算,只有在查询较多的情况下才适用。
总的来说,树堆的实际应用价值有待思考。因为插入和删除节点的复杂度增加了,是为了保持查询的复杂度为O(lnN)。但二分查询是基于优先级的,而优先级又是随机的,这样查询的基准就成了一个问题。
树堆的构造、遍历、删除节点操作代码:
#include <stdio.h>#include <stdlib.h>#include <time.h>struct node_t{ node_t* left;//左节点 node_t* right;//右节点 int priority;//优先级 int key;//关键字};struct treap_t{ node_t* root;};//右旋转本节点void rotate_right(node_t* &node){ node_t* x = node->left;//记下左节点 node->left = x->right;//左节点的右节点作为本节点的左节点 x->right = node;//把本节点作为左节点的右节点 node = x;//把左节点作为本节点}//左旋转本节点void rotate_left(node_t* &node){ node_t* x = node->right; node->right = x->left; x->left = node; node = x;}//插入操作(父节点为空则建立父节点;比父节点小则建立左节点,否则右节点)void treap_insert(node_t* &root, int key, int priority){ //根节点为NULL,则创建结点作为根结点 if (!root) { root = (node_t*)malloc(sizeof(struct node_t)); root->left = NULL; root->right = NULL; root->priority = priority; root->key = key; } else if (key < root->key)//向左插入结点 { treap_insert(root->left, key, priority); if (root->left->priority < root->priority)// 如果本节点优先级大于左节点优先级则向右旋转(左节点则会转上来) rotate_right(root); } else//向右插入结点 { treap_insert(root->right, key, priority); if (root->right->priority < root->priority)// 如果本节点优先级大于右节点优先级则向左旋转(右节点则会转上来) rotate_left(root); }}//删除指定键的节点void treap_delete(node_t* &root, int key){ if (!root) { if (key < root->key) treap_delete(root->left, key); else if (key > root->key) treap_delete(root->right, key); else//键相等的 { if (!root->left && !root->right)//没有子节点的就直接释放 { free(root); root = NULL; return; } if (root->left == NULL)//没有左节点 root = root->right; else if (root->right == NULL)//没有右节点 root = root->left; else { if (root->left->priority < root->right->priority)//判断左右节点的优先级,转向有衔接较大的那边 { //先旋转节点,然后再删除节点 rotate_right(root);//右旋转本节点 treap_delete(root->right, key); //root->right 是旋转后的原来的节点 } else { rotate_left(root);//左旋转本节点 treap_delete(root->left, key);//root->left 是旋转后的原来的节点 } } } }}//中序遍历void mid_order_traverse(node_t* root){if (root != NULL){ mid_order_traverse(root->left); printf("%d\t", root->key); mid_order_traverse(root->right);}}//计算树的高度int depth(node_t* node){ if(node == NULL) return -1; int l = depth(node->left);//左边的子树的高度 int r = depth(node->right);//右边的子树的高度 return (l < r)?(r+1):(l+1);// 返回本层的高度(最低层是0层)}int main(){treap_t* treap = (treap_t*)malloc(sizeof(struct treap_t));treap->root = NULL;int i = 0;srand(time(0));for (i = 0; i < 100; i++) treap_insert(treap->root, i, rand());mid_order_traverse(treap->root);printf("\n高度:%d\n", depth(treap->root));printf("删除操作\n");for (i = 23; i < 59; i++) treap_delete(treap->root, i);mid_order_traverse(treap->root);printf("\n树堆高度:%d\n", depth(treap->root));return 0;}
- 树堆结构
- 树-堆结构
- 堆结构
- 堆结构
- 树和树结构(1) : 二叉堆和堆排序
- 堆结构的实现
- 堆结构的运用
- 数据结构--堆结构
- 1:实现堆结构
- 链式结构实现堆
- 实现堆结构
- 实现堆结构
- 实现堆结构
- 4078:实现堆结构
- C++构建堆结构
- JVM堆内存结构
- 【模板】堆的结构
- 树型结构1 堆排序 线索二叉树 huffman
- 解二元方程
- Linux驱动开发学习的一些必要步骤.
- DWR框架集成到J2EE中
- 判断是否是中文
- 建立表格示例的SQL表及插入数据语法
- 树堆结构
- 【线性扫描ijk_贪心】candy 最少蛋糕分配、Trapping Rain water
- 三个数求最大数
- HDU 2013 蟠桃记【递推】【递归】
- U-boot的环境变量值得注意的有两个: bootcmd 和bootargs
- VC各种格式转换
- 设计模式实现(十六)---迭代器模式(Iterator)
- servlet
- 串口收发数据时字符、十六进制、二进制格式详细区分