概要

前面分别通过C和C++实现了斜堆,本章给出斜堆的Java版本。还是那句老话,三种实现的原理一样,择其一了解即可。

目录
1. 斜堆的介绍
2. 斜堆的基本操作
3. 斜堆的Java实现(完整源码)
4. 斜堆的Java测试程序

转载请注明出处:http://www.cnblogs.com/skywang12345/p/3638552.html


更多内容:数据结构与算法系列 目录

斜堆的介绍

斜堆(Skew heap)也叫自适应堆(self-adjusting heap),它是左倾堆的一个变种。和左倾堆一样,它通常也用于实现优先队列;作为一种自适应的左倾堆,它的合并操作的时间复杂度也是O(lg n)。
它与左倾堆的差别是:
(01) 斜堆的节点没有"零距离"这个属性,而左倾堆则有。
(02) 斜堆的合并操作和左倾堆的合并操作算法不同。

斜堆的合并操作
(01) 如果一个空斜堆与一个非空斜堆合并,返回非空斜堆。
(02) 如果两个斜堆都非空,那么比较两个根节点,取较小堆的根节点为新的根节点。将"较小堆的根节点的右孩子"和"较大堆"进行合并。
(03) 合并后,交换新堆根节点的左孩子和右孩子。
        第(03)步是斜堆和左倾堆的合并操作差别的关键所在,如果是左倾堆,则合并后要比较左右孩子的零距离大小,若右孩子的零距离 > 左孩子的零距离,则交换左右孩子;最后,在设置根的零距离。

斜堆的基本操作

1. 基本定义

public class SkewHeap<T extends Comparable<T>> {

    private SkewNode<T> mRoot;    // 根结点

    private class SkewNode<T extends Comparable<T>> {
T key; // 关键字(键值)
SkewNode<T> left; // 左孩子
SkewNode<T> right; // 右孩子 public SkewNode(T key, SkewNode<T> left, SkewNode<T> right) {
this.key = key;
this.left = left;
this.right = right;
} public String toString() {
return "key:"+key;
}
} ...
}

SkewNode是斜堆对应的节点类。
SkewHeap是斜堆类,它包含了斜堆的根节点,以及斜堆的操作。

2. 合并

/*
* 合并"斜堆x"和"斜堆y"
*/
private SkewNode<T> merge(SkewNode<T> x, SkewNode<T> y) {
if(x == null) return y;
if(y == null) return x; // 合并x和y时,将x作为合并后的树的根;
// 这里的操作是保证: x的key < y的key
if(x.key.compareTo(y.key) > 0) {
SkewNode<T> tmp = x;
x = y;
y = tmp;
} // 将x的右孩子和y合并,
// 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
SkewNode<T> tmp = merge(x.right, y);
x.right = x.left;
x.left = tmp; return x;
} public void merge(SkewHeap<T> other) {
this.mRoot = merge(this.mRoot, other.mRoot);
}

merge(x, y)是内部接口,作用是合并x和y这两个斜堆,并返回得到的新堆的根节点。
merge(other)是外部接口,作用是将other合并到当前堆中。

3. 添加

/*
* 新建结点(key),并将其插入到斜堆中
*
* 参数说明:
* key 插入结点的键值
*/
public void insert(T key) {
SkewNode<T> node = new SkewNode<T>(key,null,null); // 如果新建结点失败,则返回。
if (node != null)
this.mRoot = merge(this.mRoot, node);
}

insert(key)的作用是新建键值为key的节点,并将其加入到当前斜堆中。

4. 删除

/*
* 删除根结点
*
* 返回值:
* 返回被删除的节点的键值
*/
public T remove() {
if (this.mRoot == null)
return null; T key = this.mRoot.key;
SkewNode<T> l = this.mRoot.left;
SkewNode<T> r = this.mRoot.right; this.mRoot = null; // 删除根节点
this.mRoot = merge(l, r); // 合并左右子树 return key;
}

remove()的作用是删除斜堆的最小节点。

注意:关于斜堆的"前序遍历"、"中序遍历"、"后序遍历"、"打印"、"销毁"等接口就不再单独介绍了。后文的源码中有给出它们的实现代码,Please RTFSC(Read The Fucking Source Code)!

斜堆的Java实现(完整源码)

斜堆的实现文件(SkewHeap.java)

 /**
* Java 语言: 斜堆
*
* @author skywang
* @date 2014/03/31
*/ public class SkewHeap<T extends Comparable<T>> { private SkewNode<T> mRoot; // 根结点 private class SkewNode<T extends Comparable<T>> {
T key; // 关键字(键值)
SkewNode<T> left; // 左孩子
SkewNode<T> right; // 右孩子 public SkewNode(T key, SkewNode<T> left, SkewNode<T> right) {
this.key = key;
this.left = left;
this.right = right;
} public String toString() {
return "key:"+key;
}
} public SkewHeap() {
mRoot = null;
} /*
* 前序遍历"斜堆"
*/
private void preOrder(SkewNode<T> heap) {
if(heap != null) {
System.out.print(heap.key+" ");
preOrder(heap.left);
preOrder(heap.right);
}
} public void preOrder() {
preOrder(mRoot);
} /*
* 中序遍历"斜堆"
*/
private void inOrder(SkewNode<T> heap) {
if(heap != null) {
inOrder(heap.left);
System.out.print(heap.key+" ");
inOrder(heap.right);
}
} public void inOrder() {
inOrder(mRoot);
} /*
* 后序遍历"斜堆"
*/
private void postOrder(SkewNode<T> heap) {
if(heap != null)
{
postOrder(heap.left);
postOrder(heap.right);
System.out.print(heap.key+" ");
}
} public void postOrder() {
postOrder(mRoot);
} /*
* 合并"斜堆x"和"斜堆y"
*/
private SkewNode<T> merge(SkewNode<T> x, SkewNode<T> y) {
if(x == null) return y;
if(y == null) return x; // 合并x和y时,将x作为合并后的树的根;
// 这里的操作是保证: x的key < y的key
if(x.key.compareTo(y.key) > 0) {
SkewNode<T> tmp = x;
x = y;
y = tmp;
} // 将x的右孩子和y合并,
// 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
SkewNode<T> tmp = merge(x.right, y);
x.right = x.left;
x.left = tmp; return x;
} public void merge(SkewHeap<T> other) {
this.mRoot = merge(this.mRoot, other.mRoot);
} /*
* 新建结点(key),并将其插入到斜堆中
*
* 参数说明:
* key 插入结点的键值
*/
public void insert(T key) {
SkewNode<T> node = new SkewNode<T>(key,null,null); // 如果新建结点失败,则返回。
if (node != null)
this.mRoot = merge(this.mRoot, node);
} /*
* 删除根结点
*
* 返回值:
* 返回被删除的节点的键值
*/
public T remove() {
if (this.mRoot == null)
return null; T key = this.mRoot.key;
SkewNode<T> l = this.mRoot.left;
SkewNode<T> r = this.mRoot.right; this.mRoot = null; // 删除根节点
this.mRoot = merge(l, r); // 合并左右子树 return key;
} /*
* 销毁斜堆
*/
private void destroy(SkewNode<T> heap) {
if (heap==null)
return ; if (heap.left != null)
destroy(heap.left);
if (heap.right != null)
destroy(heap.right); heap=null;
} public void clear() {
destroy(mRoot);
mRoot = null;
} /*
* 打印"斜堆"
*
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
private void print(SkewNode<T> heap, T key, int direction) { if(heap != null) { if(direction==0) // heap是根节点
System.out.printf("%2d is root\n", heap.key);
else // heap是分支节点
System.out.printf("%2d is %2d's %6s child\n", heap.key, key, direction==1?"right" : "left"); print(heap.left, heap.key, -1);
print(heap.right,heap.key, 1);
}
} public void print() {
if (mRoot != null)
print(mRoot, mRoot.key, 0);
}
}

斜堆的测试程序(SkewHeapTest.java)

 /**
* Java 语言: 斜堆
*
* @author skywang
* @date 2014/03/31
*/ public class SkewHeapTest { public static void main(String[] args) { int a[]= {10,40,24,30,36,20,12,16};
int b[]= {17,13,11,15,19,21,23};
SkewHeap<Integer> ha=new SkewHeap<Integer>();
SkewHeap<Integer> hb=new SkewHeap<Integer>(); System.out.printf("== 斜堆(ha)中依次添加: ");
for(int i=0; i<a.length; i++) {
System.out.printf("%d ", a[i]);
ha.insert(a[i]);
}
System.out.printf("\n== 斜堆(ha)的详细信息: \n");
ha.print(); System.out.printf("\n== 斜堆(hb)中依次添加: ");
for(int i=0; i<b.length; i++) {
System.out.printf("%d ", b[i]);
hb.insert(b[i]);
}
System.out.printf("\n== 斜堆(hb)的详细信息: \n");
hb.print(); // 将"斜堆hb"合并到"斜堆ha"中。
ha.merge(hb);
System.out.printf("\n== 合并ha和hb后的详细信息: \n");
ha.print();
}
}

斜堆的Java测试程序

斜堆的测试程序已经包含在它的实现文件(SkewHeapTest.java)中了,这里仅给出它的运行结果:

== 斜堆(ha)中依次添加: 10 40 24 30 36 20 12 16
== 斜堆(ha)的详细信息:
10 is root
16 is 10's left child
20 is 16's left child
30 is 20's left child
40 is 30's left child
12 is 10's right child
24 is 12's left child
36 is 24's left child == 斜堆(hb)中依次添加: 17 13 11 15 19 21 23
== 斜堆(hb)的详细信息:
11 is root
13 is 11's left child
17 is 13's left child
23 is 17's left child
19 is 13's right child
15 is 11's right child
21 is 15's left child == 合并ha和hb后的详细信息:
10 is root
11 is 10's left child
12 is 11's left child
15 is 12's left child
21 is 15's left child
24 is 12's right child
36 is 24's left child
13 is 11's right child
17 is 13's left child
23 is 17's left child
19 is 13's right child
16 is 10's right child
20 is 16's left child
30 is 20's left child
40 is 30's left child

斜堆(三)之 Java的实现的更多相关文章

  1. 二叉堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了二叉堆,本章给出二叉堆的Java版本.还是那句话,它们的原理一样,择其一了解即可. 目录1. 二叉堆的介绍2. 二叉堆的图文解析3. 二叉堆的Java实现(完整源码) ...

  2. 左倾堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了左倾堆,本章给出左倾堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 左倾堆的介绍2. 左倾堆的图文解析3. 左倾堆的Java实现(完整 ...

  3. 斐波那契堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了斐波那契堆,本章给出斐波那契堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆 ...

  4. 关于博主skywang123456文章——二叉堆(三)之 Java的实现的质疑

    博客园博主skywang123456(以下简称s博主)是一个大牛级的人物,相信很多程序员都拜读过他的博客,我也不例外,并且受益匪浅.但是对于文章二叉堆(三)之 Java的实现我有一些疑惑,写在这里,供 ...

  5. 二项堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了二项堆,本章给出二项堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 二项树的介绍2. 二项堆的介绍3. 二项堆的基本操作4. 二项堆的 ...

  6. 斜堆(一)之 C语言的实现

    概要 本章介绍斜堆.和以往一样,本文会先对斜堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若文 ...

  7. 斜堆,非旋转treap,替罪羊树

    一.斜堆 斜堆是一种可以合并的堆 节点信息: struct Node { int v; Node *ch[]; }; 主要利用merge函数 Node *merge(Node *x, Node *y) ...

  8. 【朝花夕拾】Android性能篇之(三)Java内存回收

    在上一篇日志([朝花夕拾]Android性能篇之(二)Java内存分配)中有讲到,JVM内存由程序计数器.虚拟机栈.本地方法栈.GC堆,方法区五个部分组成.其中GC堆是一块多线程的共享区域,它存在的作 ...

  9. 2017-2018-2 20165318 实验三《Java面向对象程序设计》实验报告

    2017-2018-2 20165318 实验三<Java面向对象程序设计>实验报告 一.实验报告封面 课程:Java程序设计        班级:1653班        姓名:孙晓暄  ...

随机推荐

  1. Which Clang Warning Is Generating This Message?

    Which Clang Warning Is Generating This Message? 根据前面页面制作的pdf:clangwarninglist.pdf 百度网盘:http://pan.ba ...

  2. 正在开发纯BS的可在线编辑内容的电子病历编辑器

    在线电子病历编辑器功能预览,支持Firefox/Chrome/Opera/UC/IE/Safari.演示地址 http://www.dcwriter.cn:9090/ 在WINFORM.NET中的效果 ...

  3. Node + Express + Mysql的CMS小结

    因为之前用过上述的组合完成过很多系统,而这一次是为了实现一个帮助系统的静态网页发布.因为很久不写,重点说遇到的几个坑: 1.库版本的问题 比如mysql连接数据库一直报错,因为系统重装过,所以重新安装 ...

  4. 使用Installutil安装系统服务方法

    系统必须装有.net Framework2.0然后点击开始-运行输入以下指令即可完成相应操作安装服务:C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Ins ...

  5. ELK——在 CentOS/Linux 把 Kibana 3.0 部署在 Nginx 1.9.12

    上一篇文章<安装 logstash 2.2.0.elasticsearch 2.2.0 和 Kibana 3.0>,介绍了如何安装 Logstash.Elasticsearch 以及用 P ...

  6. [Scheme]一个Scheme的Metacircular evaluator

    这个解释器可以用来跑前面两篇文章的例子,所以一并扔出来,三部曲哈哈. Lisp内置的S-expression相当于解析好的语法树,而借助quasiquote和unquote又很容易进行语法树层面的变换 ...

  7. awk语法

    awk是一个非常棒的数字处理工具.相比于sed常常作用于一整行的处理,awk则比较倾向于将一行分为数个“字段”来处理.运行效率高,而且代码简单,对格式化的文本处理能力超强.先来一个例子: 文件a,统计 ...

  8. josephus Problem 中级(使用数组模拟链表,提升效率)

    问题描写叙述: 在<josephus Problem 0基础(使用数组)>中.我们提出了一种最简单直接的解决方式. 可是,细致审视代码之后.发现此种方案的效率并不高,详细体如今.当有人出局 ...

  9. C# Use Pop3Client to read gmail

    host = "pop.gmail.com" user = "xxxxx@gmail.com" password = "xxxx" port ...

  10. Entity Framework: Joining in memory data with DbSet

    转载自:https://ilmatte.wordpress.com/2013/01/06/entity-framework-joining-in-memory-data-with-dbset/ The ...