目录
前言
1、栈
2、队列
2.1、实现队列
2.2、循环队列
前言
上一篇中我们介绍了数据结构基础中的《动态数组》,本篇我们继续来学习两种基本的数据结构——栈和队列。
1、栈
特点:栈也是一种线性结构,相比数组,栈对应的操作是数组的子集,只能从一端添加元素,也只能从同一端取出元素,这一端称为栈顶。栈是一种后进先出的数据结构,即Last In First Out(LIFO)。
上面说到栈对应的操作是数组的子集,因此我们就基于上一篇中实现的动态数组来快速的实现一个栈。
首先定义一个接口,定义相关功能方法:
然后让栈来实现接口中的具体功能:
import arr.Array;
public class ArrayStack<E> implements Stack<E> {
Array<E> array;
public ArrayStack(int capacity) {
array = new Array<>(capacity);
}
public ArrayStack() {
array = new Array<>();
}
public int getCapacity() {
return array.getCapacity();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public void push(E e) {
array.addLast(e);
}
@Override
public E pop() {
return array.removeLast();
}
@Override
public E peek() {
return array.getLast();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Stack").append("[");
for (int i = 0; i < array.getSize(); i++) {
res.append(array.get(i));
if (i != array.getSize() - 1) {
res.append(",");
}
}
res.append("] Top");
return res.toString();
}
}
写一个测试方法:
执行结果如下:
2、队列
2.1、实现队列
队列也是一种线性结构,相比数组,队列对应的操作也是数组的子集,队列只能从一端(队尾)添加元素,只能从另一端(队首)取出元素。队列是一种先进先出的数据结构(先到先得),即:First In First Out(FIFO)。
由于队列对应的操作同样是数组的子集,那么我们让然也是基于上一篇中实现的动态数组来快速的实现一个栈。
定义一个接口,定义相关功能方法:
然后让队列来实现接口中的具体功能:
import arr.Array;
public class ArrayQueue<E> implements Queue<E> {
private Array<E> array;
public ArrayQueue(int capacity) {
array = new Array<>(capacity);
}
public ArrayQueue(){
array = new Array<>();
}
public int getCapacity(){
return array.getCapacity();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public void enqueue(E e) {
array.addLast(e);
}
@Override
public E dequeue() {
return array.removeFirst();
}
@Override
public E getFront() {
return array.getFirst();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Queue:").append("Front [");
for (int i = 0; i < array.getSize(); i++) {
res.append(array.get(i));
if (i != array.getSize() - 1) {
res.append(",");
}
}
res.append("] tail");
return res.toString();
}
}
同样的写一个测试类:
执行结果如下:
2.2、循环队列
初始时front和tail都是指向下标为0的位置,当有元素入队时,tail指向该元素的下一个位置((tail+1)%capacity),元素出队时,front向后移动一个位置,因此,循环队列有元素出队时,无需让所有的元素都移动一个位置,只需让front的指向移动一次即可,示意图如下:
下面我们来通过代码看一下循环队列该怎么实现:
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front, tail;
private int size;
public LoopQueue(int capacity) {
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
public int getCapacity() {
return data.length - 1;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public void enqueue(E e) {
if ((tail + 1) % data.length == front) {
resize(getCapacity() * 2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size++;
}
private void resize(int newCapacity) {
E[] newdata = (E[]) new Object[newCapacity + 1];
for (int i = 0; i < size; i++) {
newdata[i] = data[(i + front) % data.length];
}
data = newdata;
front = 0;
tail = size;
}
@Override
public E dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("队列为空");
}
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size--;
if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
resize(getCapacity() / 2);
}
return null;
}
@Override
public E getFront() {
if (isEmpty()) {
throw new IllegalArgumentException("队列为空");
}
return data[front];
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(String.format("Queue: size=%d,capacity=%d\n", size, getCapacity()))
.append("front [");
for (int i = front; i != tail; i = (i + 1) % data.length) {
res.append(data[i]);
if ((i+1)%data.length != tail) {
res.append(",");
}
}
res.append("] tail");
return res.toString();
}
}
同样的测试程序:
执行结果如下:
好了,关于栈和队列的内容就说这么多吧,咱们下期再会!
祝:工作顺利!