本文将介绍三种数据类型,分别是背包(Bag)、队列(Queue)和栈(Stack)。它们的不同之处在于删除或者访问对象的顺序不同。
其次是介绍【链式数据结构】的重要性,特别是经典数据结构【链表】,有了它们我们才能高效实现背包、队列和栈。
集合结构介绍及API
背包
背包是一种不支持从中删除元素的集合数据类型——它的目的就是帮助用例收集元素并迭代遍历所有收集到的元素(用例也可以检查背包是否为空或者获取背包中元素的数量)。迭代顺序不确定且与用例无关。
先进先出队列
先进先出队列(简称队列)是一种基于先进先出(FIFO)策略的集合类型。
下压栈
下压栈(或简称栈)是一种基于后进先出(LIFO)策略的集合类型。
栈的数组实现
在Java中,数组一旦被创建,其大小是无法改变的,因此栈使用的空间只能是这个最大容量的一部分。使用数组实现栈的基本思路是首先创建默认长度的数组用来存储元素(可以是任意长度),使用一个计数器N(初值为0)存储栈中元素的个数。
向栈中加入元素时,首先判断栈中元素是否已经达到数组长度,如果达到,就创建一个更大的数组,将旧数组中的元素依次复制过去,然后使用新数组替代旧数组,将新加入的元素存储在数组中,更新计数器N的值。
在栈中移除最上层的元素时,首先将elements[N - 1](elements为成员变量,即存储元素使用的数组)赋值为空,这是为了防止对象游离(根据java内存回收机制,如果一个对象仍然有引用指向它,就不会被回收),然后将N减一。完成移除操作后,如果栈中元素小于等于数组长度的四分之一(整型的除法向下取整,所以elements.length / 4的含义是整除4),就将数组大约缩减一半(即创建一个长度为旧数组一半的新数组取代它)。
public class ResizeArrayStack<Item> implements Iterable<Item>{
private int N;
private Item[] elements;
private static int DEFAULT_CAPACITY = 2;
//......
/**
* 往堆里增加元素
* @param item 元素
*/
public void push(Item item){
if(N == elements.length){
resize(N * 2);
}
elements[N++] = item;
}
/**
* 弹出堆最上层的元素
* @return 堆最上层的元素
*/
public Item pop(){
if(isEmpty()) throw new NoSuchElementException("stack underflow");
Item result = elements[--N];
if(N > 0 && N == elements.length / 4){
resize(elements.length / 2);
}
return result;
}
private void resize(int l){
elements = Arrays.copyOf(elements, l);
}
public boolean isEmpty(){
return N == 0;
}
//......
}
这里有两个辅助函数isEmpty()和resize(int l)。isEmpty非常简单,如果计数器N的值为0即代表栈为空,反之,栈不为空。resize方法调用了Arrays.copyOf(Object[] src, int newlength)方法复制数组,第一个参数为源数组(即将被复制的数组),第二个参数为新数组的长度(如果小于源数组,则相当于截取源数组的一部分,如果大于源数组,多出的部分使用默认值填充,如Int型数组默认值是0,对象数组默认值是null),函数返回对应参数要求的新数组,将其赋值给成员变量elements。
队列的数组实现
队列的数组实现相对于栈的数组实现比较复杂。它需要维护first和last两个指针,以记录队列中元素存储在数组中的下标范围。具体代码如下:
package QueueStackAndBag;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class ResizeArrayQueue<Item> implements Iterable<Item>{
private int N;
private Item[] elements;
private static int DEFAULT_CAPACITY = 2;
private int first;
private int last;
/**
* 没有指定stack初始容量,则调用默认的容量
*/
ResizeArrayQueue(){
this(DEFAULT_CAPACITY);
}
/**
* 初始化
* @param capacity 初始容量
*/
ResizeArrayQueue(int capacity){
elements = (Item[]) new Object[capacity];
}
/**
*
* @return 队列是否为空