1.定容栈
public class StringArrayOfStack { private String[] stack; private int size; public StringArrayOfStack(int cap){ stack=new String[cap]; size=0; } public boolean isEmpty(){ return size==0; } public int size(){ return size; } public void push(String item){ stack[size++]=item; } public String pop(){ return stack[--size]; } }
2.泛型定容栈
public class FixedCapacityStackOfStrings<Item> { private Item[] s; private int N = 0; @SuppressWarnings("unchecked") public FixedCapacityStackOfStrings(int capacity) { s = (Item[]) new Object[capacity]; } public boolean isEmpty() { return N == 0; } public void push(Item item) { if (N == s.length) { resize(2 * s.length); } s[N++] = item; } public Item pop() { Item item = s[--N]; s[N] = null; if (N == s.length / 4 && N > 0) { resize(s.length / 2); } return item; } public int size() { return N; }
在这里有一个细节非常重要:我们希望用以上代码的构造函数的实现中创建一个泛型的数组:
a=new Item[cap];
由于某些历史和技术原因,创建泛型数组在Java中是不允许的。我们需要类型转换:
a=(Item []) new Object[cap];
3.调整数组大小
选择用数组表示栈内容意味着用例必须预先估计栈的最大容量。在Java中,数组一旦创建,其大小是无法改变的,因此栈使用的空间只能是这个最大容量的一部分。选择大容量的用例在栈为空或几乎为空时会浪费大量的内存。另一方面,如果集合变得比数组更大那么用例有可能溢出。
动态调整数组s[]的大小,使得它既足以保存所有元素,又不至于浪费过多的空间。
首先,实现一个方法讲栈移动到另一个大小不同的数组中。
public void resize(int Max) { Item[] item = (Item[]) new Object[Max]; for (int i = 0; i < N; i++) { item[i] = s[i]; } s = item; }
现在,在push()中,检查数组是否太小。具体来说,我们会通过检查栈大小N和数组大小a.length是否想等来检查数组是否能够容纳新的元素。如果没有多余的空间,我们会将数组的长度加倍。然后就可以和从前一样使用s[N++]=item插入新元素了。
public void push(Item item) { if (N == s.length) { resize(2 * s.length); } s[N++] = item; }
类似,在pop()中,首先删除栈顶的元素,然后如果数组太大我们就将它的长度减半。只要稍加思考,你就明白正确的检测条件是栈大小是否小于数组的四分之一。在数组长度被减半之后,它的状态约为半满,在下次需要改变数组大小之前仍然能够进行多次push()和pop()操作。
public Item pop() { Item item = s[--N]; s[N] = null; if (N == s.length / 4 && N > 0) { resize(s.length / 2); } return item; }
在这个实现中,栈永远不会溢出,使用率也永远不会低于四分之一(除非栈为空,那种情况下数组的大小为1)。
4.迭代
在任意可迭代的集合数据类型中我们都需要实现的东西:
1)集合数据类型必须实现一个iterator()方法并返回一个Iterator对象。
2)Iterator类必须包含两个方法:hasNext()(返回一个布尔值)和next()(返回集合中的一个泛型元素)。
在Java中,我们使用接口机制来指定一个类所必须实现的方法。对于可迭代的集合数据类型,Java已经为我们定义了所需的接口。
要使一个类可迭代,第一步就是在它的声明中加入implements Iterable<Item>,对应的接口为:
public interface Iterable<Item>{ Iterator<Item> iterator(); }
然后在类中添加一个方法iterator()并返回一个迭代器Iterator<Item>。迭代器都是泛型的,因此我们可以使用参数类型Item来帮助用例遍历它们指定的任意类型的对象。对于一直使用的数组表示法,我们需要逆序迭代遍历这个数组,因此我们将迭代器命名为ReverseArrayIterator,并添加方法:
public Iterator<Item> iterator(){ return new ReverseArrayIterator(); }
迭代器是什么?它是一个实现了hasNext()和next()方法的类的对象,由以下接口定义:
public interface Iterator<Item>{ public boolean hasNext(){return i>0;} public Item next(){return a[--i];} public void remove(){ } }
尽管接口指定了一个remove()方法,但在本书中remove()方法总为空,因为我们希望避免在迭代中穿插能够修改数据结构的操作。对于ReverseArrayIterator,这些方法只需要一行代码,它们实现在栈类的一个嵌套类中:
private class ReverseArrayIterator implements Iterator<Item> { private int i = N; @Override public boolean hasNext() { return i > 0; } @Override public Item next() { return s[--i]; } @Override public void remove() { } }
请注意,嵌套类可以访问包含它的类的实例变量,在这里就是s[]和N(这也是我们使用嵌套类实现迭代器的主要原因)。从技术角度来说,为了和Iterator的结构保持一致,我们应该在两种情况下抛出异常:如果用例调用了remove()则抛出UnsupportedOperationException,如果用例在调用next()时i为0则抛出NoSuchElementException。因为我们只会在foreach语法中使用迭代器,这些情况都不会出现,所以我们省略了这部分代码。
5.链表
使用一个嵌套类来定义结点的抽象数据类型:
private class Node{ Item item; Node next; }
链表的使用能达到三个设计目标:
1)它可以处理任意类型的数据。
2)所需的空间总是和集合的大小成正比。
3)操作所需的时间总是和集合的大小无关。
下压堆栈(链表实现)
public class LinkedlistOfStack<Item> implements Iterable<Item> { private int size; private Node first; private class Node { Item item; Node next; } public boolean isEmpty() { return first == null; } public int size() { return size; } public void push(Item item) { Node old = first; first = new Node(); first.item = item; first.next = old; size++; } public Item pop() { Item item = first.item; first = first.next; size--; return item; } public Iterator<Item> iterator() { return new ReverseStack(); } private class ReverseStack implements Iterator<Item> { private Node pointer = first; public boolean hasNext() { return pointer != null; } public Item next() { Item item = pointer.item; pointer = pointer.next; return item; } public void remove() { } } }
队列的实现
public class LinkedQueueOfStrings<Item> implements Iterable<Item> { private Node first; private Node last; private int N; private class Node { Item item; Node next; } public boolean isEmpty() { return first == null; } public int size() { return N; } public void enqueue(Item item) { Node oldlast = new Node(); last = new Node(); last.item = item; if (isEmpty()) { first = last; } else { oldlast.next = last; } N++; } public Item dequeue() { Item item = first.item; first = first.next; if (isEmpty()) { last = null; } N--; return item; } public Iterator<Item> iterator() { return new ListIterator(); } private class ListIterator implements Iterator<Item> { private Node current = first; public boolean hasNext() { return current != null; } public void remove() { } public Item next() { Item item = current.item; current = current.next; return item; } } }
背包的实现
public class Bag<Item> implements Iterable<Item> { private Node first; private class Node { Item item; Node next; } public void add(Item item) { Node oldfirst = first; first = new Node(); first.item = item; first.next = oldfirst; } public Iterator<Item> iterator() { return new ListIterator(); } private class ListIterator implements Iterator<Item> { private Node current = first; public boolean hasNext() { return current != null; } public void remove() { } public Item next() { Item item = current.item; current = current.next; return item; } } }
6.union-find算法