java实现元素为任意对象的堆
分析与设想
前几日复习了堆的知识,实现了整型堆。但是整型堆仅能用于处理元素为整型的数据,在处理各种的题目的时候往往不便于使用。
于是,为了实现广义上的堆,将元素类型从整型变为对象。我决定利用java多态和便于封装的性质,来实现这个元素为任意类型对象的堆。那么,为了实现这个数据结构,我们就必须弄清楚它和整型堆相比,需要解决的问题在哪里,以及怎样通过设计解决它。
对于原先的堆,由于类型已知所以可以直接定义整型数组或是整型链表用于储存堆。这样的写法,可实现的功能非常单一,仅可以作为整型大(小)根堆使用。
堆的优点,在于它可以通过二叉树的结构在O(logn)的复杂度内完成插入,以O(1)的复杂度得到集合中最大或最小的元素。现在,我们不妨泛化一下两个概念:
- 是将元素从数字泛化为可进行比较的对象。
- 是将数值大小的比较泛化为基于某种预设比较规则的对象比较
这样一来,新的堆就成为了某种对象的集合,并且依据某种预设比较规则组织起来的广义上的堆。这样,我们依旧可以在O(logn)的时间复杂度内完成一次插入(假设比较的复杂度为O(1)),以O(1)的时间复杂度得到对象集合中依据比较规则“最大”或“最小”的元素。
这样一来,我们的工作就可以围绕着泛化两个概念,改造原有堆展开。
主要问题分析与设计
首先,需要解决对象存储的问题
本文提供一种思路,可以借鉴java中许多方法对任意对象适用的设计,利用java中父类可以向下兼容表示子类的多态性质以及Object类是所有类的父类的预设,利用Object类型的数组或链表引用对象元素。这样一来,任何对象都可以兼容,在元素类型方面,实现了存储。
其次,需要解决对象比较的问题
前文提到,堆中的元素需要可比较,根据某种规则比较大小。这里的问题在于,我们需要将比较规则传递给堆。由于我们使用Object类型引用所有的元素,则得知元素是否为可比较对象,且调用其CompareTo方法较为繁琐,且这样的设计使得规则不独立于元素,对于同种元素,仅能创造基于同一种规则的堆,缺少灵活性。
于是,本文采用传入比价器的方式制定规则并用于比较参数。这样一来,实现时仅需要将原堆中比较两元素大小替换为调用比较器对元素进行比较即可。同时,由于比较规则由传入的比较器决定,与元素无关。所以,对于同一类型对象,可以创建多个不同秩序的堆,提升灵活性。
动手实现
以下为堆类的内容:
成员变量:
堆的规模size:整数类型
堆数组heap:Object类型,暂不进行实例化。
比较器judge,Comparator类型,暂不进行实例化。
方法:
构造方法:
提供两个构造方法:
构造方法一:需要传递最大规模和比较器
构造方法二:需要传递比较器。
注:构造方法二默认规模最大为10000,调用构造方法一。
私有方法:
下沉操作down:将下标为k的元素下沉,维护堆的性质。
上浮操作up:将下标为k的元素上浮,维护堆的性质。
公有方法:
添加元素Push:将对象o插入堆中,并维护堆的性质,返回是否插入成功。
弹出堆顶元素Pop:将堆顶元素弹出,并维护堆的性质。
获得堆顶元素Top:返回堆顶元素,若堆中无元素则返回null。
查看是否为空Empty:返回当前堆是否为空。
查看是否堆满Full:返回当前堆包含元素数量是否达到上限。
获得堆规模:返回当前堆中元素个数。
代码
import java.util.Comparator;
public class Heap {
private Object[] heap;
private Comparator judge;
private int size = 0;
public Heap(int size,Comparator judge)
{
this.judge = judge;
heap = new Object[size];
}
public Heap(Comparator judge)
{
this(10001,judge);
}
private void up(int k)
{
int fa = k >> 1;
while(true)
{
if(fa == 0)
{
break;
}
if(judge.compare(heap[k], heap[fa]) >= 0)
{
break;
}
Object o = heap[k];
heap[k] = heap[fa];
heap[fa] = o;
k = fa;
fa = k >> 1;
}
}
private void down(int k)
{
int son = k << 1;
while(true)
{
if(son > size)
{
break;
}
if(son + 1 <= size && judge.compare(heap[son], heap[son + 1]) > 0)
{
son++;
}
if(judge.compare(heap[k], heap[son]) <= 0)
{
break;
}
Object o = heap[k];
heap[k] = heap[son];
heap[son] = o;
k = son;
son = k << 1;
}
}
public int Size()
{
return size;
}
public Object Top()
{
return size != 0 ? heap[1] : null;
}
public boolean Push(Object o)
{
if(size == heap.length - 1)
{
return false;
}
heap[++size] = o;
up(size);
return true;
}
public boolean Pop()
{
if(size == 0)
{
return false;
}
heap[1] = heap[size--];
down(1);
return true;
}
public boolean Empty()
{
return size == 0;
}
public boolean Full()
{
return size == heap.length - 1;
}
}
对象堆使用
最后,使用对象堆,前提条件是提前定义和元素类型及其比较器,对于基本类型,需要使用包装类型并为其编写比较器。
满足前提条件后,引用对象堆类文件,实例化堆对象并传入特定比较器即可。