在Java编程中,我们经常会用到栈这种数据结构,通常我们都是直接调用Java中已经实现的栈,而我们要深入理解栈这种数据结构的原理,就要动手自己来实现它。
栈的定义
栈是一种只能在一端进行插入和删除操作的特殊线性表。对于一个栈来说,表尾端有着特殊的含义,称为栈顶,表头端称为栈底,不含元素的空表称之为空栈,栈又称为后进先出的线性表,简称 LIFO(Last In First Out)结构。也就是说后存放的先取,先存放的后取,这就类似于我们要在取放在箱子底部的东西(放进去比较早的物体),我们首先要移开压在它上面的物体(放进去比较晚的物体)。通常,我们将栈的插入操作称为入栈,删除操作称为出栈。入栈时元素总是放在栈底,而出栈的总是栈顶元素。因此,栈中元素采用的是“后进先出”的方式。栈的数据元素类型可以任意,只要是同一种类型即可。它的基本操作包括清空、判空、求元素个数、获取栈顶、入栈和出栈等。
栈的常用操作如下:
public interface Stack<E> {
//获取栈的容量
public int getSize();
//判断栈是否为空
public boolean isEmpty();
//入栈
public void push(E e);
//出栈
public E pop();
/**
* 获取当前栈顶元素
* @return
*/
public E peek();
//清空栈
public void clear();
}
栈已有两种存储表示方法,分别称之为顺序栈(数组实现)和链栈(链表实现)。
顺序栈的实现:
顺序栈是指利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。
优点:入栈和出栈速度快,缺点:长度有限(一般而言),不过我在这里实现的是基于动态数组的栈,可以在栈的容量需要改变时对其进行扩容或者缩容。详情请查看:https://blog.csdn.net/CDCSPR/article/details/100895758
所以,数据项入栈和出栈的时间复杂度都为O(1)
实现代码如下:
public class ArrayStack <E> implements Stack<E>{
//栈的实现完全可以借助于ArrayList
private ArrayList<E> list;
public ArrayStack(){
list=new ArrayList<E>();
}
/**
* 创建一个容量为capacity指定大小的栈(顺序表)
* @param capacity
*/
public ArrayStack(int capacity) {
list=new ArrayList<E>(capacity);
}
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public void push(E e) {
list.addLast(e);
}
@Override
public E pop() {
return list.removeLast();
}
@Override
public E peek() {
return list.getLast();
}
@Override
public void clear() {
list.clear();
}
@Override
public boolean equals(Object obj) {
if(obj==null) {
return false;
}
if(obj==this) {
return true;
}
if(obj instanceof ArrayStack) {
ArrayStack<E> stack=(ArrayStack<E>)obj;
return list.equals(stack.list);
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ArrayStack: size="+getSize()+",capacity="+list.getCapacity()+"\n");
if(isEmpty()) {
sb.append("[]");
}else {
sb.append('[');
for(int i=0;i<getSize();i++) {
sb.append(list.get(i));
if(i==getSize()-1) {
sb.append(']');
}
else {
sb.append(',');
}
}
}
return sb.toString();
}
}
链栈的实现
链栈是采用链式存储结构实现的栈,通常链栈采用单链表来实现,由于栈的主要操作是在栈顶进行插入和删除,所以以链表的头部作为栈顶最为方便。
注意:我实现的栈是所基于的单链表是在我的另一篇博客中所实现的单链表:详情请查看:https://blog.csdn.net/CDCSPR/article/details/100856471
没有长度限制,并且出栈和入栈速度都很快
public class LinkedStack<E> implements Stack<E> {
private LinkedList<E> list;
public LinkedStack() {
list=new LinkedList<E>();
}
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public void push(E e) {
list.addFirst(e);
}
@Override
public E pop() {
return list.removeFirst();
}
@Override
public E peek() {
return list.getFirst();
}
@Override
public void clear() {
list.clear();
}
@Override
public boolean equals(Object obj) {
if(obj==null) {
return false;
}
if(obj==this) {
return true;
}
if(obj instanceof LinkedStack) {
LinkedStack<E> stack=(LinkedStack<E>)obj;
return list.equals(stack.list);
}
return false;
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append("LinkedStack:size="+getSize()+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append("[");
for(int i=0;i<getSize();i++) {
sb.append(list.get(i));
if(i!=getSize()-1) {
sb.append(",");
}else {
sb.append("]");
}
}
}
return sb.toString();
}
}
通过对栈的两种不同存储方式的实现,我对于栈的原理和各种操作有了一个更加直观的理解!