表、栈和队列是最简单和最基本的三种数据结构。实际上,每一个有意义的程序都将显式地至少使用一种这样的数据结构,而栈则在程序中总是要被间接地用到,不管我们在程序中是否做了声明。
抽象数据类型(abstract data type,ADT):是带有一组操作的一些对象的集合。抽象数据类型是数学的抽象;在ADT的定义中没有的地方提到关于这组操作是如何实现的任何解释。如表、集合、图以及与它们各自的操作一起形成的这些对象都可以被看做是抽象数据类型,就像整数、实数、布尔数都是数据类型一样。整数、实数和布尔数各自都有与之相关的操作,而抽象数据类型也是如此。
对于集合ADT,可以有像添加(add)、删除(remove)以及包含(contain)这样一些操作。当然,也可以只要两种操作并(union)和查找(find),这两种操作又在这个集合上定义了一种不同的ADT。
Java中也考虑到ADT的实现,不过适当地隐藏了实现的细节。这样,程序中需要对ADT实施操作的任何其他部分可以通过调用适当的方法来进行。如果由于某种原因需要改变实现的细节,那么通过仅仅改变执行这些ADT操作的例程应该是很容易做到的。这种改变对于程序的其余部分是完全透明的。
对于每种ADT并不存在什么法则来告诉我们必须要有哪些操作。当它们的方法被正确地实现之后,使用它们的程序并没有必要知道它们是如何实现的。
线性表(linear list)是n个类型相同数据元素的有限序列,通常记作(a0,a1,…,ai-1,ai,ai+1,…,an-1)。
线性表是最简单,也是最常用的数据结构之一。
其线性结构的特点是:在数据元素的有限集中,除第一个元素无直接前驱,最后一个元素无直接后续以外,每个数据元素有且仅有一个直接前驱元素和一个直接后续元素。
对表的所有操作都可以通过使用数组来实现。虽然数组是由固定容量创建的,但在需要的时候可以用双倍的容量创建一个不同的数组。它解决由于使用数组而产生最严重的问题,即从历史上看为了使用一个数组,需要对表的大小进行估计。而这种估计在Java都是不必要的。
假设有一个初始长度为10的数组arr,在必要的时候对其长度进行扩展的方式如下:
int[] arr = new int[10];
//对数组arr进行扩容
int[] newArr = new int[arr.length * 2];
for(int i = 0;i < arr.length;i++){
newArr[i] = arr[i];
}
arr = newArr;
数组的实现可以使printList以线性时间被执行,而findKth操作则花费常数时间,这正式我们所能预期的。不过插入和删除的花费却隐藏着巨大的浪费。假如我们在index=0的位置插入一个元素,则我们需要将整个数组后移一个位置用来腾出空间,而在该位置删除一个元素则需要将表中的所有元素前移一个位置,这两种操作所需的时间均为O(N)。最好的情况,自然是在index=n-1的位置插入、删除元素,无需对表中的其他元素进行移动。
在一些情形下,对表的插入操作均在高端进行,其后只进行对数组的访问操作,这种情形下,数组是表的一种恰当的实现。当需要对表进行一些插入、删除操作,特别是在表的前端进行,这时,使用链表将会是一种更好的选择。
当表单中的元素不连续存储时,我们就可以有效的避免插入和删除的线性开销。如图所示:
抽象数据类型可以对应到Java中的类,抽象数据类型的数据对象与数据之间的关系可以通过类的成员变量来存储和表示,抽象数据类型的操作则使用类的方法来实现。
先不考虑如何完成数据对象以及数据之间关系的存储和表示,我们考虑如何将抽象数据类型所提供的操作使用Java语言给出明确的定义。事实上对抽象数据类型提供的操作使用高级语言进行定义,就是给出其应用程序接口,在Java中我们可以使用一个接口来进行定义。如此在使用类来完成抽象数据类型的具体体现时,我们只要实现相应接口就实现了对抽象数据类型定义的操作的实现。
List接口:
public interface List{
//返回线性表的大小,即数据元素的个数;
public int getSize();
//如果线性表为空返回true,否则返回false;
public boolean isEmpty();
//判断线性表是否包含数据元素e;
public boolean contains(Object e);
//返回数据元素e在线性表中i号位置;
public int indexOf(Object e);
//将数据元素e插入到线性表中i号位置;
public void insert(int i,Object e) throws OutOfBoundaryException;
//将数据元素e插入到obj之前;
public boolean insertBefore(Object obj,Object e);
//将数据元素e插入到obj之后;
public boolean insertAfter(Object obj,Object e);
//删除线性表中序号为i的元素,并返回之;
public Object remove(int i) throws OutOfBoundaryException;
//删除线性表示第一个与e相同的元素;
public boolean remove(Object e);
//替换线性表中序号为i的数据元素为e,返回原数据元素;
public Object replace(int i,Object e)throws OutOfBoundaryException;
//返回线性表中序号为i的数据元素
public Object get(int i) throws OutOfBoundaryException;
}
OutOfBoundaryException异常:
//线性表中出现序号越界是抛出该异常
public class OutOfBoundaryException extends RuntimeException{
public OutOfBoundaryException(String err){
super(err);
}
}
public interface Strategy{
//判断两个数据元素是否相等
public boolean equal(Object obj1,Object obj2);
/**
*比较两个数据元素的大小
*如果obj1 < obj2 返回-1
*如果obj1 = obj2 返回0
*如果obj1 > obj2 返回1
*/
public int compare(Object obj1,Object obj2);
}