栈
什么是栈:
栈是一种有序数据项构成的数据结构,数据项的插入和删除只能在一端(栈顶)进行。LIFO(last in first out)。
这里分别用数组和链表简单实现栈的功能。
1.用数组实现栈,元素array[0]就表示了栈底元素,array.length表示了栈的长度,array[array.length-1]表示了栈顶元素。每次有元素入栈的时候,array.length++
public ArrayStack<E> implements Cloneable{
private E[] data; //栈中的数据项
private int items; //栈中数据项的个数
//初始化一个大小为INITIAL_LENGTH的栈
public ArrayStack(){
final int INITIAL_LENGTH = 10;
items = 0;
data = (E[]) new Object[INITIAL_LENGTH];
}
//手动输入栈的大小
public ArrayStack(int initialLength){
if(initialLength < 0)
throw new IllegalArgumentException("输入的栈的大小有误!")
items = 0;
data = (E[]) new Object[initialLength];
}
//获取当前栈的大小
public int getLength(){
return data.length;
}
//获取当前栈内元素的个数
public int itemsSize(){
return items;
}
//判断当前栈是否为空
public boolean isEmpty(){
return (items == 0);
}
//修改当前栈的长度
public void changeLength(int newLength){
E[] newStack;
if(data.length < newLength){
newStack = (E[]) new Object[newLength];
System.arraycopy(data, 0, newStack, 0, items);
data = newStack;
}
}
//实现clone方法生成栈的一个副本
public ArrayStack<E> clone(){
ArrayStack<E> stackCopy;
try{
stackCopy = (ArrayStack<E>)super.clone();
}
catch(CloneNotSupportedException e){
throw new RuntimeException("复制失败!这个数据类型没有实现clone方法!")
}
stackCopy.data = data.clone();
return stackCopy;
}
}
下面就是栈最重要的入栈、出栈以及获取栈顶数据项的操作。
//获取当前栈顶项数据
public E peek(){
if(items == 0)
throw new EmptyStackException();
return data[items-1];
}
将栈顶数据项出栈,就是获取当前栈顶数据项,并将栈的长度减小,当栈为空时返回一个空值。
public E pop(){
E topItem;
if(items == 0)
throw new EmptyStackException();
topItem = data[--items];
data[items] = null;
return topItem;
}
将数据项入栈,要注意栈必须有足够的位置才能够push。
public void push(E newItem){
if(items == data.length)
changeLength(2*items + 1); //栈内如果没有空间了,增大栈的空间
data[items] = newItem;
items++;
}
这里如果2*items+1 > Integer.MAX_VALUE会产生算数溢出,造成入栈失败。
上面将栈的长度变长了,所以应该定义一个方法恢复到正确的长度,让它调用。
public void trimToSize(){
E[] trimArray;
if(data.length != items){
trimArray = (E[]) new Object[items];
System.arraycopy(data, 0, trimArray, 0, items);
data = trimArray;
}
}
用链表实现栈的本质跟数组实现还是差不多的,用一个个链表的结点来表示栈中的数据项。栈顶存储在链表的头部引用的位置,栈底存储在链表的尾部引用的位置,然后让变量head引用链表的头部位置。
public class LinkedStack<E> implements Cloneable{
private Node<E> head;
//初始化链表实现的栈
public LinkedStack(){
head == null;
}
public boolean isEmpty(){
return (head == null);
}
//实现复制栈的方法
public LinkedStack<E> clone(){
LinkedStack<E> linkCopy;
try{
linkCopy = (LinkedStack)super.clone();
}
catch(CloneNotSupportedException e){
throw new RuntimeException("复制失败!这个数据类型没有实现clone方法!");
}
linkCopy.head = Node.listCopy(head);
return linkCopy;
}
//获取当前栈的长度
public int size(){
return Node.listLength(head);
}
}
用链表实现的栈的入栈、出栈和获取栈顶数据的方法跟用数组实现是类似的。
//获取栈顶数据项
public E peek(){
if(head == null)
throw new EmptyStackException();
return head.getData();
}
//将栈顶数据项出栈
public E pop(){
E top;
if(head == null)
throw new EmptyStackException();
top = top.getLink();
return top;
}
//将数据项入栈
public E push(E newItem){
head = new Node<E>(newItem, head);
}