数据结构开篇(表)


要点
1. 抽象数据类型ADT
2. 有效执行表操作

1. 抽象数据类型
抽象数据类型ADT(Abstrat Data Type)是带有一组操作的一些对象的集合。它是数学的抽象,在ADT中不涉及这组操作的具体实现。

Java类也考虑ADT的实现,但适当隐藏了实现的细节。程序中需要对ADT实施操作的的任何其他部分可以通过调用适当的方法来进行。

2. 表ADT

定义:形如A0,A1,A2,…,A(N-1)的一般的表。大小为N,A(i)后继A(i-1),A(i-1)前驱A(i),表的第一个元素时A0,最后一个元素时A(N-1)。不定义A0的前驱元,也不定义A(N-1)的后继元。

表ADT相应操作的集合:printList和makeEmpty是常用的操作;find返回某一项首次出现的位置;insert和remove一般是从表中某个位置插入和删除某个元素findKth则返回(作为参数而被指定的)某个位置上的元素。还应当对一些特殊情况添加一些,如在34,12,33,44,46,89这个表中find(46)返回4,那么find(1)返回什么?还有比如可以添加next和previous取一个位置作为参数并分别返回其后继元和前驱元的位置。

2.1 表的简单数组实现

表的这些操作可以由数组实现,数组是由固定容量创建的,然而需要时可以用双倍容量创建一个不同的数组,这解决了使用数组而产生的最严重的问题——即历史上为使用数组而需要对表大小作估计。而这种估计在Java或任何现代编程语言都是不需要的。

数组arr扩展示例(初始长度为10)

int[] arr = new int[10];
...
//下面我们扩大arr
int[] newArr = new int[arr.lenngth * 2];
for(int i = 0; i<arr.length;i++)
    newArr[i] = arr[i];
arr = newArr;

findKth操作花费常数时间,printList以线性时间被执行。而插入和删除操作则潜藏着昂贵的时间开销,这要看插入和删除位置在哪里。最坏情况下,在位置0处插入(即在表前端插入)首先要将整个数组后移一个位置以空出空间来,删除第一个元素需要将表中所有元素迁移一个位置,两种操作的最坏情况是O(N)。最好情况,如果所有操作都发生在表的高端,就不需要移动元素,插入和删除操作是花费O(1)时间。平均来看,这两种操作需要移动表一半元素,需要线性时间。

2.2 简单链表

为减少插入和删除的线性开销,需保证表可以不连续存储,否则表的每个部分将需要整体移动。下图指出链表的一般想法。

这里写图片描述

链表由一系列节点组成,这些节点不必再内存中相连。每个节点均含有表元素和到包含该元素后继元的节点的链(link).我们称之为next链,最后一个单元的next链引用null。

执行printList或find(x),从表的第一个节点开始然后用一些后继的next链遍历该表即可。这种操作是线性时间,和数组实现一样,其中的常数可能比用数组实现要大。findKth操作不如数组实现时的效率高;findKth(i)花费O(i)的时间并以这种明显的方式遍历链表而成。实践中这个界是保守的,因为调用findKth常是以排序后的方式进行。例如findKth(2),findKth(3),findKth(4)以及findKth(6)可通过对表的一次扫描同时实现。

remove方法可以通过修改一个next引用实现,insert方法需要使用new操作符从系统取得一个新节点,此后执行两次引用的调整。在实践中若知道变动将要发生的地方,那么向链表中删除一项的操作不需要移动的很多项,而只涉及常数个节点链的改变。

这里写图片描述

删除最后一项会比较麻烦,因为必须找出指向最后节点的项,把它的next改为null,然后更新更新持有最后节点的链。经典链表中每个节点均存储器下一节点的链,而拥有指向最后节点的链并不提供最后节点的前驱节点的任何信息。

为此,提出了双链表,让每个节点持有一个指向它在标准的前驱节点的链。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值