斜堆(一)之 C语言的实现
概要
本章介绍斜堆。和以往一样,本文会先对斜堆的理论知识进行简单介绍,然后给出C语言的实现。后续再分别给出C++和Java版本的实现;实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可。若文章有错误或不足的地方,请不吝指出!
目录
1. 斜堆的介绍
2. 斜堆的基本操作
3. 斜堆的C实现(完整源码)
4. 斜堆的C测试程序
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3638493.html
更多内容:数据结构与算法系列 目录
斜堆的介绍
斜堆(Skew heap)也叫自适应堆(self-adjusting heap),它是左倾堆的一个变种。和左倾堆一样,它通常也用于实现优先队列。它的合并操作的时间复杂度也是O(lg n)。
相比于左倾堆,斜堆的节点没有"零距离"这个属性。除此之外,它们斜堆的合并操作也不同。斜堆的合并操作算法如下:
(01) 如果一个空斜堆与一个非空斜堆合并,返回非空斜堆。
(02) 如果两个斜堆都非空,那么比较两个根节点,取较小堆的根节点为新的根节点。将"较小堆的根节点的右孩子"和"较大堆"进行合并。
(03) 合并后,交换新堆根节点的左孩子和右孩子。
第(03)步是斜堆和左倾堆的合并操作差别的关键所在,如果是左倾堆,则合并后要比较左右孩子的零距离大小,若右孩子的零距离 > 左孩子的零距离,则交换左右孩子;最后,在设置根的零距离。
斜堆的基本操作
1. 头文件
#ifndef _SKEW_HEAP_H_
#define _SKEW_HEAP_H_ typedef int Type; typedef struct _SkewNode{
Type key; // 关键字(键值)
struct _SkewNode *left; // 左孩子
struct _SkewNode *right; // 右孩子
}SkewNode, *SkewHeap; // 前序遍历"斜堆"
void preorder_skewheap(SkewHeap heap);
// 中序遍历"斜堆"
void inorder_skewheap(SkewHeap heap);
// 后序遍历"斜堆"
void postorder_skewheap(SkewHeap heap); // 获取最小值(保存到pval中),成功返回0,失败返回-1。
int skewheap_minimum(SkewHeap heap, int *pval);
// 合并"斜堆x"和"斜堆y",并返回合并后的新树
SkewNode* merge_skewheap(SkewHeap x, SkewHeap y);
// 将结点插入到斜堆中,并返回根节点
SkewNode* insert_skewheap(SkewHeap heap, Type key);
// 删除结点(key为节点的值),并返回根节点
SkewNode* delete_skewheap(SkewHeap heap); // 销毁斜堆
void destroy_skewheap(SkewHeap heap); // 打印斜堆
void print_skewheap(SkewHeap heap); #endif
SkewNode是斜堆对应的节点类。
2. 合并
/*
* 合并"斜堆x"和"斜堆y"
*
* 返回值:
* 合并得到的树的根节点
*/
SkewNode* merge_skewheap(SkewHeap x, SkewHeap y)
{
if(x == NULL)
return y;
if(y == NULL)
return x; // 合并x和y时,将x作为合并后的树的根;
// 这里的操作是保证: x的key < y的key
if(x->key > y->key)
swap_skewheap_node(x, y); // 将x的右孩子和y合并,
// 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
SkewNode *tmp = merge_skewheap(x->right, y);
x->right = x->left;
x->left = tmp; return x;
}
merge_skewheap(x, y)的作用是合并x和y这两个斜堆,并返回得到的新堆。merge_skewheap(x, y)是递归实现的。
3. 添加
/*
* 新建结点(key),并将其插入到斜堆中
*
* 参数说明:
* heap 斜堆的根结点
* key 插入结点的键值
* 返回值:
* 根节点
*/
SkewNode* insert_skewheap(SkewHeap heap, Type key)
{
SkewNode *node; // 新建结点 // 如果新建结点失败,则返回。
if ((node = (SkewNode *)malloc(sizeof(SkewNode))) == NULL)
return heap;
node->key = key;
node->left = node->right = NULL; return merge_skewheap(heap, node);
}
insert_skewheap(heap, key)的作用是新建键值为key的结点,并将其插入到斜堆中,并返回堆的根节点。
4. 删除
/*
* 取出根节点
*
* 返回值:
* 取出根节点后的新树的根节点
*/
SkewNode* delete_skewheap(SkewHeap heap)
{
SkewNode *l = heap->left;
SkewNode *r = heap->right; // 删除根节点
free(heap); return merge_skewheap(l, r); // 返回左右子树合并后的新树
}
delete_skewheap(heap)的作用是删除斜堆的最小节点,并返回删除节点后的斜堆根节点。
注意:关于斜堆的"前序遍历"、"中序遍历"、"后序遍历"、"打印"、"销毁"等接口就不再单独介绍了。后文的源码中有给出它们的实现代码,Please RTFSC(Read The Fucking Source Code)!
斜堆的C实现(完整源码)
斜堆的头文件(skewheap.h)
#ifndef _SKEW_HEAP_H_
#define _SKEW_HEAP_H_ typedef int Type; typedef struct _SkewNode{
Type key; // 关键字(键值)
struct _SkewNode *left; // 左孩子
struct _SkewNode *right; // 右孩子
}SkewNode, *SkewHeap; // 前序遍历"斜堆"
void preorder_skewheap(SkewHeap heap);
// 中序遍历"斜堆"
void inorder_skewheap(SkewHeap heap);
// 后序遍历"斜堆"
void postorder_skewheap(SkewHeap heap); // 获取最小值(保存到pval中),成功返回0,失败返回-1。
int skewheap_minimum(SkewHeap heap, int *pval);
// 合并"斜堆x"和"斜堆y",并返回合并后的新树
SkewNode* merge_skewheap(SkewHeap x, SkewHeap y);
// 将结点插入到斜堆中,并返回根节点
SkewNode* insert_skewheap(SkewHeap heap, Type key);
// 删除结点(key为节点的值),并返回根节点
SkewNode* delete_skewheap(SkewHeap heap); // 销毁斜堆
void destroy_skewheap(SkewHeap heap); // 打印斜堆
void print_skewheap(SkewHeap heap); #endif
斜堆的实现文件(skewheap.c)
/**
* C语言实现的斜堆
*
* @author skywang
* @date 2014/03/31
*/ #include <stdio.h>
#include <stdlib.h>
#include "skewheap.h" /*
* 前序遍历"斜堆"
*/
void preorder_skewheap(SkewHeap heap)
{
if(heap != NULL)
{
printf("%d ", heap->key);
preorder_skewheap(heap->left);
preorder_skewheap(heap->right);
}
} /*
* 中序遍历"斜堆"
*/
void inorder_skewheap(SkewHeap heap)
{
if(heap != NULL)
{
inorder_skewheap(heap->left);
printf("%d ", heap->key);
inorder_skewheap(heap->right);
}
} /*
* 后序遍历"斜堆"
*/
void postorder_skewheap(SkewHeap heap)
{
if(heap != NULL)
{
postorder_skewheap(heap->left);
postorder_skewheap(heap->right);
printf("%d ", heap->key);
}
} /*
* 交换两个节点的内容
*/
static void swap_skewheap_node(SkewNode *x, SkewNode *y)
{
SkewNode tmp = *x;
*x = *y;
*y = tmp;
} /*
* 获取最小值
*
* 返回值:
* 成功返回0,失败返回-1
*/
int skewheap_minimum(SkewHeap heap, int *pval)
{
if (heap == NULL)
return -; *pval = heap->key; return ;
} /*
* 合并"斜堆x"和"斜堆y"
*
* 返回值:
* 合并得到的树的根节点
*/
SkewNode* merge_skewheap(SkewHeap x, SkewHeap y)
{
if(x == NULL)
return y;
if(y == NULL)
return x; // 合并x和y时,将x作为合并后的树的根;
// 这里的操作是保证: x的key < y的key
if(x->key > y->key)
swap_skewheap_node(x, y); // 将x的右孩子和y合并,
// 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
SkewNode *tmp = merge_skewheap(x->right, y);
x->right = x->left;
x->left = tmp; return x;
} /*
* 新建结点(key),并将其插入到斜堆中
*
* 参数说明:
* heap 斜堆的根结点
* key 插入结点的键值
* 返回值:
* 根节点
*/
SkewNode* insert_skewheap(SkewHeap heap, Type key)
{
SkewNode *node; // 新建结点 // 如果新建结点失败,则返回。
if ((node = (SkewNode *)malloc(sizeof(SkewNode))) == NULL)
return heap;
node->key = key;
node->left = node->right = NULL; return merge_skewheap(heap, node);
} /*
* 取出根节点
*
* 返回值:
* 取出根节点后的新树的根节点
*/
SkewNode* delete_skewheap(SkewHeap heap)
{
SkewNode *l = heap->left;
SkewNode *r = heap->right; // 删除根节点
free(heap); return merge_skewheap(l, r); // 返回左右子树合并后的新树
} /*
* 销毁斜堆
*/
void destroy_skewheap(SkewHeap heap)
{
if (heap==NULL)
return ; if (heap->left != NULL)
destroy_skewheap(heap->left);
if (heap->right != NULL)
destroy_skewheap(heap->right); free(heap);
} /*
* 打印"斜堆"
*
* heap -- 斜堆的节点
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
static void skewheap_print(SkewHeap heap, Type key, int direction)
{
if(heap != NULL)
{
if(direction==) // heap是根节点
printf("%2d is root\n", heap->key);
else // heap是分支节点
printf("%2d is %2d's %6s child\n", heap->key, key, direction==?"right" : "left"); skewheap_print(heap->left, heap->key, -);
skewheap_print(heap->right,heap->key, );
}
} void print_skewheap(SkewHeap heap)
{
if (heap != NULL)
skewheap_print(heap, heap->key, );
}
斜堆的测试程序(skewheap_test.c)
/**
* C语言实现的斜堆
*
* @author skywang
* @date 2014/03/31
*/ #include <stdio.h>
#include "skewheap.h" #define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) ) void main()
{
int i;
int a[]= {,,,,,,,};
int b[]= {,,,,,,};
int alen=LENGTH(a);
int blen=LENGTH(b);
SkewHeap ha,hb; ha=hb=NULL; printf("== 斜堆(ha)中依次添加: ");
for(i=; i<alen; i++)
{
printf("%d ", a[i]);
ha = insert_skewheap(ha, a[i]);
}
printf("\n== 斜堆(ha)的详细信息: \n");
print_skewheap(ha); printf("\n== 斜堆(hb)中依次添加: ");
for(i=; i<blen; i++)
{
printf("%d ", b[i]);
hb = insert_skewheap(hb, b[i]);
}
printf("\n== 斜堆(hb)的详细信息: \n");
print_skewheap(hb); // 将"斜堆hb"合并到"斜堆ha"中。
ha = merge_skewheap(ha, hb);
printf("\n== 合并ha和hb后的详细信息: \n");
print_skewheap(ha); // 销毁斜堆
destroy_skewheap(ha);
}
斜堆的C测试程序
斜堆的测试程序已经包含在它的实现文件(skewheap_test.c)中了,这里仅给出它的运行结果:
== 斜堆(ha)中依次添加:
== 斜堆(ha)的详细信息:
is root
is 's left child
is 's left child
is 's left child
is 's left child
is 's right child
is 's left child
is 's left child == 斜堆(hb)中依次添加:
== 斜堆(hb)的详细信息:
is root
is 's left child
is 's left child
is 's left child
is 's right child
is 's right child
is 's left child == 合并ha和hb后的详细信息:
is root
is 's left child
is 's left child
is 's left child
is 's left child
is 's right child
is 's left child
is 's right child
is 's left child
is 's left child
is 's right child
is 's right child
is 's left child
is 's left child
is 's left child
斜堆(一)之 C语言的实现的更多相关文章
- 斜堆(二)之 C++的实现
概要 上一章介绍了斜堆的基本概念,并通过C语言实现了斜堆.本章是斜堆的C++实现. 目录1. 斜堆的介绍2. 斜堆的基本操作3. 斜堆的C++实现(完整源码)4. 斜堆的C++测试程序 转载请注明出处 ...
- 斜堆(三)之 Java的实现
概要 前面分别通过C和C++实现了斜堆,本章给出斜堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 斜堆的介绍2. 斜堆的基本操作3. 斜堆的Java实现(完整源码)4. ...
- bzoj1078【SCOI2008】斜堆
题意: 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值都比它父亲大.因此在整棵斜堆中,根的值最小.但斜堆不必是平衡的,每个结点的左右儿子的大 ...
- BZOJ 1078: [SCOI2008]斜堆
1078: [SCOI2008]斜堆 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 770 Solved: 422[Submit][Status][ ...
- 【bzoj1078】[SCOI2008]斜堆
2016-05-31 16:34:09 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1078 挖掘斜堆的性质233 http://www.cp ...
- 斜堆,非旋转treap,替罪羊树
一.斜堆 斜堆是一种可以合并的堆 节点信息: struct Node { int v; Node *ch[]; }; 主要利用merge函数 Node *merge(Node *x, Node *y) ...
- BZOJ 2809: [Apio2012]dispatching [斜堆]
题意:主席树做法见上一题 我曾发过誓再也不写左偏树(期末考试前一天下午5个小时没写出棘手的操作) 于是我来写斜堆啦 从叶子往根合并,维护斜堆就行了 题目连拓扑序都给你了... 说一下斜堆的操作: 合并 ...
- [SCOI2008]斜堆
题目大意 1.题目描述 斜堆(skew heap)是一种常用的数据结构. 它也是二叉树,且满足与二叉堆相同的堆性质: 每个非根结点的值都比它父亲大.因此在整棵斜堆中,根的值最小. . 但斜堆不必是平衡 ...
- BZOJ1078 [SCOI2008]斜堆 堆
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1078 题意概括 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的 ...
随机推荐
- python中os和sys模块的详解
平时在工作中经常会用到os模块和sys模块的一些特性,下面是这些特性的一些相关解释,希望对大家有所帮助 os模块 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os. ...
- 打包.a 文件时, build phases- Link Binary With Libraries
打包.a 文件时, 如果是两个Targets那么可运行调试的 build phases- Link Binary With Libraries 如果加入.a 文件. 那么就不需要在Compile ...
- Js 数据容量单位转换(kb,mb,gb,tb)
function bytesToSize(bytes) { if (bytes === 0) return '0 B'; var k = 1000, // or 1024 sizes = ['B', ...
- The web application [] appears to have started a thread named [Abandoned connection cleanup thread] com.mysql.jdbc.AbandonedConnectionCleanupThread
01-Jul-2016 14:25:30.937 WARNING [localhost-startStop-1] org.apache.catalina.loader.WebappClassLoade ...
- gpg的一些常用操作
(1)列出keys # gpg --list-keys /root/.gnupg/pubring.gpg ------------------------ pub 2048R/98681A63 2 ...
- [Scheme]一个Scheme的Metacircular evaluator
这个解释器可以用来跑前面两篇文章的例子,所以一并扔出来,三部曲哈哈. Lisp内置的S-expression相当于解析好的语法树,而借助quasiquote和unquote又很容易进行语法树层面的变换 ...
- 分享一个点赞超过100的漂亮ASP.NET MVC蓝色界面框架
从 陈贞宝 博客中看到一个MVC模板感觉特别漂亮就尝试着分离出来,直接拿来用啦,直接拷贝到自己的常用的代码库里收藏起来,地址是http://www.cnblogs.com/baihmpgy/p/381 ...
- MyEclipse中拷贝J2EE项目,发布到tomcat中名字一样的解决办法
修改Eclipse工作空间下新拷贝项目下.settings文件夹中org.eclipse.wst.common.component的两个属性值. 为新项目名字: <?xml version=&q ...
- spring中配置了事务,数据业务层捕获异常,事务配置不成功?
原理:spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常 ...
- HTML5 Canvas实战之刮奖效果
近年来由于移动设备对HTML5的较好支持,经常有活动用刮奖的效果,最近也在看H5方面的内容,就自己实现了一个,现分享出来跟大家交流. 1.效果 2.原理 原理很简单,就是在刮奖区添加两个canvas, ...