所谓的栈,是一个含有至少两个基本操作的抽象数据类型:插入新的元素;删除最近时间插入的元素。遵循FILO(First in,last out,先进后出)的原则。
所谓的队列,也是一个含有至少两个基本操作的抽象数据类型:插入新的元素;删除最久时间插入的元素。遵循FIFO(First in,first out,先进先出)的原则。
关于栈和队列的具体实现,我们即可以借助于数组,也可以采用链表来实现。
1) 栈的数组实现方式
java代码
public class MyStack {
public int count;
public Object[] items;
public MyStack()
{
items = new Object[20];
count = 0;
}
public MyStack(int len)
{
items = new Object[len];
count = 0;
}
//判断栈是否为空
boolean isEmpty(){return count==0;}
//重整数组的大小
private void resize(int size)
{
Object[] newItems = new Object[size];
for(int i =0;i//进栈
public void push(E e)
{
//如果栈满,则重整数组大小。
if(count == items.length) resize(2*items.length);
items[count++] = e;//数组的起始索引是0,入栈后,数组的大小应增加1
}
//出栈
public E pop()
{
if(count==0) return null; //如果栈为空
E item = (E)items[count-1];
items[count-1] = null;
count--;
//重整数组大小,节省存储空间。
if(count>0&&count<=items.length/4) resize(items.length/2);
return item;
}
//返回栈中的最后一个元素
public E peek()
{
if(count==0) return null;
E item = (E)items[count-1];
return item;
}
}
2)栈的链式实现方式
java代码
public class MyStack {
Node head;//表示栈顶
private class Node
{
E item;
Node next;
}
public boolean isEmpty()
{
return head==null;
}
public void push(E e)
{
Node node = new Node();
node.item = e;
node.next = head; //栈的物理连接顺序是从上到下
head = node;
}
public E pop()
{
if(head==null) return null;
E e = (E)head.item;//栈顶元素先出
head=head.next;
return e;
}
public E peek()
{
if(head == null) return null;
else
return (E)head.item;
}
}
3)队列的数组实现方式
java代码
//用数组实现循环队列
public class ArrayQueue
{
private int front;
private int rear;
private int capacity;
private int count;
private int capacityIncrement;
private Object[] itemArray;
public ArrayQueue(){
front = 0;
rear = 0;
count = 0;
capacity = 10;
capacityIncrement = 5;//新增队列的长度。
itemArray = new Object[capacity];
}
boolean isEmpty() {return count==0;}
//1.入队
public void insert(E e)
{
//1.如果队列已满,就先扩充队列,后插队。
if(count==capacity) {
capacity+=capacityIncrement;
Object[] newArray = new Object[capacity];
/**
* 将原队列中的元素原样拷贝到新队列中。如果不是原样拷贝,则会破坏队列的FIFO特性。
*/
//如果队列中的元素位于itemArray[font.....rear-1]中
if(frontfor(int i = front;ielse{
//因队列已满,此时front==rear,应该将队列分成两个区间:[0:rear-1] 和[front:count-1],分别拷贝到新数组的两端。
//在原队列的中间插入一段新的存储空间。
//否则,如果在原队列的后面插入新的空间,那么再次进行入队操作时,原来的元素就会被覆盖掉。
for(int i =0;ifor(int i = front;i//然后,将front改为指向其新位置.
}
itemArray = newArray;
}
//2.如果队列未满,直接插队。
itemArray[rear]=e;
rear=(rear+1)%capacity;
count++;
}
//2.出队
public E remove()
{
if(count==0) return null;
else{
E item = (E)itemArray[front];
itemArray[front] = null;
front = (front+1)%capacity;
count--;
return item;
}
}
}
//另外一种数组实现循环队列的简单实现
import java.util.NoSuchElementException;
public class SimpleArrayQueue {
protected Object [] data;
protected int size,
head,
tail;
public SimpleArrayQueue(){
final int INITIAL_LENGTH=10;
data=new Object[INITIAL_LENGTH];
size=0;
head=0;
tail=-1;
}
//获取队列的大小和判断队列是否为空
public int theSize(){ return size;}
public boolean isEmpty(){ return size==0;}
//获取队头元素
public Object front(){
if(size==0) throw new NoSuchElementException();
return data[head];
}
//入队操作
public void enqueue(Object element){
if(size==data.length){
//在java中允许将一个数组变量拷贝给另外一个数组变量。这时,两个变量将引用同一个数组。
Object [] oldData=data;//保存原数组data
data=new Object[data.length*2]; //double the length of data
//将数组分成两个区间进行拷贝:区间1:[head:OldData.length-1]和区间2:[0:tail]
//当head==0时,原数组只有区间1这一段需要被拷贝,当head>0时,原数组有区间1和区间2这两段需要被拷贝。
//copy oldData[head:OldData.length-1] to data[0:OldData.length-1-head]
System.arraycopy(oldData, head,data,0,oldData.length-head);
/*
* 如果希望将一个数组的所有值拷贝到一个新的数组中去,就要使用Arrays类中的copyOf()方法:
* int[] copiedLckyNumbers = Arrays.copyOf(luckyNumbers,2*luckyNumbers.length);
* 第二个参数是新数组的长度,这个方法通常用来增加数组的大小。
* 如果数组的元素是数值型,那么多余的元素将被赋值为0;如果是boolean,多余的元素将会被赋值为false;
* 相反,如果长度小于原始数组的长度,则只会拷贝最前面的数据元素。
*/
if(head>0)//拷贝区间2
//copy oldData[0:tail] to data [oldData.length-head:oldlenght-1]
//System.arraycopy(oldData, 0, data, head+1, tail+1); //错误写法
System.arraycopy(oldData, 0, data,oldData.length-head, tail+1);//正确写法
head=0;
tail=oldData.length-1;
}
tail=(tail+1)%data.length;
data[tail]=element;
size++;
}
//出队操作
public Object dequeue(){
/* 注意 :
* if(size--==0){throw new NoSuchElementException();}//先执行if(size==0),后执行size--;
* 放置的顺序。
* 不能写成if(--size==0){ throw new NoSuchElementException(); }
* 因为当队列中就剩下一个元素时,--size;会使size为0,此时抛出异常,并没有对该异常进行处理。
* 程序中断,后面的语句无法执行。
*/
Object element=data[head];
head=(head+1)%data.length;//当队列满的时候size==data.length,即data.length就是当前size的值。
//如果size变小后,head就会跳过下一个元素,而不是顺移下一个,导致下一个元素被”遗忘“了。
if(size--==0){ throw new NoSuchElementException(); } //先执行if(size==0),后执行size--;
return element; //程序最后一句一定要是return语句。
}
}
4)队列的链式实现方式
java代码
public class ListQueue {
private int count;
private Node front,rear;
private class Node
{
E item;
Node link;
}
// //提供java类的空参数的构造函数。
// ListQueue()
// {
// count =0;
// front = null;
// rear = null;
// Node node = new Node();
// }
public boolean isEmpty(){ return count==0;}
//入队
public void insert(E e)
{
Node newNode = new Node();//初始化为空节点。
newNode.item = e;
//如果队为空
if(rear==null)
front = rear = newNode;
else{
rear.link = newNode;//新节点连接到队尾处
rear = newNode;//新节点变成队尾
}
count++;
}
//出队
public E remove()
{
//如果队为空
if(count==0) return null;
else{
E item = front.item;
front = front.link;
if(front==null) { rear = null; }//队列为空。
count--;
return item;
}
}
//克隆队列
public ListQueue clone()
{
ListQueueQ = new ListQueue();
for(Nodet=front;t!=null;t=t.link)//for循环的这种写法。等价于while循环。
Q.insert(t.item);
return Q;
}
}