数据结构与算法(java描述)笔记整理——第三章:表,栈与队列

抽象数据类型(ADT)

  • 抽象数据类型是带有一组操作的一些对象的集合。
  • 集合,,与他们各自操作一起形成的这些对象都可以看作抽象数据类型
  • 和整数,实数,布尔数一样,有与之相关的操作。
  1. 表ADT(list)

处理形如
A 0 ,   A 1 ,   A 2   . . . , A N − 1 A_0,\space A_1,\space A_2\space ...,A_{N-1} A0, A1, A2 ...,AN1
的一般的表,我们说这个表的大小是N,我们将大小为0的特殊表称为空表。

对于一个非空的表:

  • 在后面的一项后继前面的那项
  • 在前面的一项前驱后面的那项

与定义相关的还有在表ADT上操作的集合(各种方法)。比如:

  • printList
  • makeEmpty
  • find
  • findKth
  • insert
  • remove

当然啦,功能怎样才算恰当应该由设计者确定。


  1. 表的简单数组实现:

显然,这些对表的操作可以用数组实现。但是需要解决一个问题

数组创建时是由固定容量的

但现代编程语言中表应该是不需要手动扩展的,以下是一个简单的扩展方法

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 => 常数时间
  • insertremove => 可能有较高花费:
    最坏情况下,在表的最前端插入/删除,则每个元素都要动一下;平均来说也要移动一半元素 ,时间复杂度是 O(N)
优化思考:
  1. 如果所有操作都在表的高端 => 添加和删除只花费 O(1) 时间
  2. 如果是高端进行插入操作,然后只发生对数组的访问(即只有findKth)=> 合适
  3. 如果有对表的插入和删除=> 太慢了

所以我们尝试另一种数据结构:链表


  1. 简单链表

应该满足的条件:

避免插入和删除的线性开销 => 保证表可以不连续存储(否则表的每个部分都会需要整体移动)

链表特点:
  1. 由一系列节点组成,这些节点不必在内存中相连
  2. 每一个节点均含有表元素和到包含该元素后继元节点的链(link),称为 next链 。最后一个单元的 next链 引用 null
相关方法实现&花费时间:
  • printList 或 find(x)
    从表的第一个节点开始然后用一些后继的 next链 遍历该表
    操作时间是线性的(O(N)),但常数会大于数组实现
  • findKth()
    要返回某个位置上的元素则要一个个顺着链找过去,所以找第i项花费 O(i) 时间
    当然,一次扫描也可以直接完成多个
  • remove()
    修改一个next引用实现
  • insert()
    用new操作符从系统取得一个新节点,再执行两次引用调整。
关于insert()remove() 的具体分析
  • 如果知道变动要发生的地方,那么向链表插入或从链表中删除一项的操作不需要移动很多项,只需要常数个节点链的改变
  • 在表的前端添加或删除第一项的特殊情形此时也属于常数时间的操作(如果假设链表前端的链是存在的)。
  • 只要拥有到链表最后节点的链(因此典型的链表有到该表两端的链)
    • 在链表末尾进行添加操作的特殊情形(让新的项成为最后一项)可以花费常数时间
    • 删除最后一项比较复杂,因为必须找出指向最后节点的项,把它的next改成null,再更新持有最后节点的链。

==> 所以我们最后的做法是让每一个节点再持有一个指向它在表中的前驱节点的链,称之为 双链表

Java Collection API中的表

类库里,Java语言包含着一些普通数据结构的实现,这一部分通常叫 Collections API

Collection接口

位于java.util包中
集合(collection)的概念在此接口中得到抽象,它存储一组类型相同的对象。

//部分Collection接口
public interface Collection<AnyType> extends Iterable<AnyType>{
    int size();
    boolean isEmpty();
    void clear();
    boolean contains( AnyType x );
    boolean add( AnyType x );
    boolean remove( AnyType x );
    java.util.Iterator<AnyType> iterator();

}

Collection接口扩展了Iterable接口,因此可以有增强的for循环

Iterator接口
Iterator是什么
  • 实现Iterable接口的集合必须提供一个称为iterator的方法,该方法返回一个Iterator类型的对象。
public interface Iterator<AnyType>{
    boolean hasNext();
    //给出集合的(尚未见到的)下一项
    AnyType next();
    void remove();
}

思路:通过iterator创建并返回一个实现Iterator接口的对象,并将当前位置的概念在对象内部存储下来。

当编译器见到一个正在用于Iterable的对象的增强的for循环的时候,它用对iterator方法的那些调用代替增强的for循环已得到一个Iterator对象,然后调用next和hasNext,即:

public static <AnyType> void print( Collection<AnyType> coll){
    for(AnyType item : coll)
        System.out.println( item );
}

print例程将由编译器重写成:

//通过编译器使用一个迭代器改写的Iterable类型上增强的for循环
public static <AnyType> void print( Collection<AnyType> coll){
    Iterator<AnyType> itr = coll.iterator();
    while( itr.hasNext() ){
        AnyType item = itr.next();
        System.out.println( item );
    }
}

Iterator接口中现有的方法有限,很难使用Iterator做简单遍历Collection以外的任何事

Iterator的remove问题

有两个我们更愿意用Iterator的remove()方法的原因

  1. 第一个优点
  • 基本法则:当直接使用Iterator(而不是通过一个增强的for循环间接使用)时,如果对正在被迭代的集合进行结构上的改变(使用集合的add ,remove , clear)方法,则迭代器就不再合法。

这意味着,只有立即需要使用迭代器的时候才应该获取迭代器。
如果迭代器调用的是他自己的remove方法,那么它就仍然是合法的。

  1. 第二个优点(主要)
    Collection的 remove 方法必须首先找出要被删除的项。如果知道了所要删除的项的准确位置,那么删除它的开销应该要小很多。
List接口 ArrayList类和LinkedList
public interface List<AnyType> extends Collection<AnyType>{
	AnyType get(int idx);
	AnyType set(int idx, AnyType newVal);
	void add(int idx, AnyType x);
	void remove(int idx);

	ListIterator<AnyType> listIterator(int pos);
}

^包 java.utilList 接口的子集。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值