什么是栈?
栈结构是从数据的运算来分类的,也就是说栈结构具有特殊的运算规则。而从数据的逻辑结构来看,栈结构其实就是一种线性结构。说到线性结构,我们很容易就想到联想到线性结构中的顺序表和链表。而栈也确实鱼这两种结构存在有着不一般的关系,从存储结构来对栈进行划分,栈可以分为两类:
- 顺序栈结构:用一组地址连续的内存单元依次保存栈中的数据。
- 链式栈结构:用链表的形式保存栈中的数据。
但无论用何种存储结构的形式来表示栈,我们应当遵循一个原则:后进先出(Last In First Out, LIFO)原则。
如上图所示,该栈结构只能在一端进行操作,操作的一端称为栈顶,另一端则称为栈底。一般的栈结构的基本操作有两个:
- 入栈(Push):将数据保存到栈顶。
- 出栈(Pop):将栈顶的数据弹出(读取后删除)。
这里先用顺序存储结构来实现栈。首先是栈长度不变的顺序栈结构:
代码如下:
package ds.stack;
/**
* 简单不可变长栈
*
* @author Abyss_CMG
*
* @param <E>
*/
public class Stack<E> {
private static final int MAX_LENGTH = 50;// 栈的默认最大长度
private Object[] objData = null; // 存放栈的数据的数组
private int nTop;// 用于标记栈顶
private int nLen;// 用于标记栈的最大长度
/**
* 若实例化栈时没有参数,则使用默认长度50初始化栈
*/
Stack() {
this(MAX_LENGTH);
}
/**
* 实例化栈结构
*
* @param nInitSize
* 栈的最大长度
*/
Stack(int nInitSize) {
if (nInitSize > 0) {
objData = new Object[nInitSize];
nTop = -1;
nLen = nInitSize;
} else {
// throw new IllegalArgumentException("栈的初始化长度不能小于等于0:" +
// nInitSize);
System.out.println("栈的初始化长度不能小于等于0:" + nInitSize);
}
}
/**
* 获取栈中已存放数据的长度
*
* @param stack
* 栈的对象引用
* @return 获取栈中已存放数据的长度
*/
int getSize(Stack<E> stack) {
return stack.nTop + 1;
}
/**
* 获取栈的最大长度
*
* @param stack
* 栈的对象引用
* @return 栈的最大长度(初始化时的长度)
*/
int getMaxSize(Stack<E> stack) {
return stack.nLen;
}
/**
* 判断是否满栈
*
* @param stack
* 实例化的对象引用
* @return 若满栈则返回true,否则返回false
*/
boolean isFull(Stack<E> stack) {
return (getSize(stack) == stack.nLen) ? true : false;
}
/**
* 判断是否空栈
*
* @param stack
* 栈的对象引用
* @return 若空栈则返回true,否则返回false
*/
boolean isEmpty(Stack<E> stack) {
return (stack.nTop == -1) ? true : false;
}
/**
* 清空栈,但不释放内存
*
* @param stack
* 栈的对象引用
*/
void clear(Stack<E> stack) {
if (stack.nTop != -1) {
stack.nTop = -1;
}
}
/**
* 释放栈的内存空间
*
* @param stack
* 栈的对象引用
*/
void free(Stack<E> stack) {
stack.clear(stack);
if (stack != null) {
stack = null;
}
}
/**
* 入栈操作
*
* @param stack
* 栈的对象引用
* @param eData
* 要压入栈的数据
*/
void push(Stack<E> stack, E eData) {
if (isFull(stack)) {
// throw new IndexOutOfBoundsException("栈已满,无法入栈");
System.out.println("栈已满,无法入栈");
return;
}
stack.objData[++stack.nTop] = eData;
System.out.println(stack.objData[stack.nTop] + "已入栈");
}
/**
* 将栈顶弹出
*
* @param stack
* 栈的对象引用
* @return 弹出栈顶数据
*/
@SuppressWarnings("unchecked")
E pop(Stack<E> stack) {
if (isEmpty(stack)) {
// throw new IndexOutOfBoundsException("栈已空,无法出栈");
System.out.println("栈已空,无法出栈");
return null;
}
System.out.println(stack.objData[stack.nTop] + "已出栈");
return (E) stack.objData[stack.nTop--];
}
/**
* 读取栈顶数据(不弹出)
*
* @param stack
* 栈的对象引用
* @return 返回栈顶元素
*/
@SuppressWarnings("unchecked")
E peek(Stack<E> stack) {
if (isEmpty(stack)) {
// throw new IndexOutOfBoundsException("栈已空,无法读取栈顶元素");
System.out.println("栈已空,无法读取栈顶元素");
return null;
}
System.out.println(stack.objData[stack.nTop] + "已读取");
return (E) stack.objData[stack.nTop];
}
}
测试代码:
package ds.stack;
public class Simple {
public static void main(String[] args) {
System.out.println("----新建空栈----");
Stack<String> stack = new Stack<String>(5);
System.out.println("栈的最大长度:" + stack.getMaxSize(stack));
System.out.println("栈的现有长度:" + stack.getSize(stack));
System.out.println("----测试入栈操作----");
System.out.println("将test1压入栈");
stack.push(stack, "test1");
System.out.println("将test2压入栈");
stack.push(stack, "test2");
System.out.println("将test3压入栈");
stack.push(stack, "test3");
System.out.println("将test4压入栈");
stack.push(stack, "test4");
System.out.println("将test5压入栈");
stack.push(stack, "test5");
System.out.println("将test6压入栈");
stack.push(stack, "test6");
System.out.println("栈的现有长度:" + stack.getSize(stack));
System.out.println("----测试读取栈顶操作----");
stack.peek(stack);
stack.peek(stack);
System.out.println("----测试出栈操作----");
System.out.println("栈的现有长度:" + stack.getSize(stack));
stack.pop(stack);
System.out.println("栈的现有长度:" + stack.getSize(stack));
stack.pop(stack);
System.out.println("栈的现有长度:" + stack.getSize(stack));
stack.pop(stack);
System.out.println("栈的现有长度:" + stack.getSize(stack));
stack.pop(stack);
System.out.println("栈的现有长度:" + stack.getSize(stack));
stack.pop(stack);
System.out.println("栈的现有长度:" + stack.getSize(stack));
stack.pop(stack);
}
}
测试结果:
----新建空栈----
栈的最大长度:5
栈的现有长度:0
----测试入栈操作----
将test1压入栈
test1已入栈
将test2压入栈
test2已入栈
将test3压入栈
test3已入栈
将test4压入栈
test4已入栈
将test5压入栈
test5已入栈
将test6压入栈
栈已满,无法入栈
栈的现有长度:5
----测试读取栈顶操作----
test5已读取
test5已读取
----测试出栈操作----
栈的现有长度:5
test5已出栈
栈的现有长度:4
test4已出栈
栈的现有长度:3
test3已出栈
栈的现有长度:2
test2已出栈
栈的现有长度:1
test1已出栈
栈的现有长度:0
栈已空,无法出栈