一,受限的线性表
在数据结构中,栈和队列也是线性表,只不过是它们的操作受到一定的限制。
二, 栈
栈是一种线性表——受限的线性表,其插入和删除操作都在表的同一端进行。其中插入和删除元素的一端 称为栈顶,另一端称为栈底,不含任何元素的栈称为空栈。栈的特点是后进先出(last in first out,LIFO)。
三,栈的实现
栈的实现可以借助于顺序表或者链表来实现,用顺序表实现的栈称为顺序栈,用链表实现的栈称为链栈。这里我们采用顺序表来实现顺序栈。要用顺序表的方式实现栈,我们需要重用之前的代码,重用代码又分为两种方式,继承和聚合,这里分别采用继承和聚合的方式实现。
继承方式
继承方式实现的话会暴露父类的方法,比如栈只有push,pop,isEmpty,peek这些方法,继承MyArrayList的话则会暴露父类中诸如add,remove,set,indexOf等方法,使用者调用这些方法则会破坏栈的结构,个人喜欢采用聚合方式实现。
public class MyArrayStack2<T> extends MyArrayList<T> {
//调用父类的构造方法
public MyArrayStack2()
{
super();
}
public boolean push(T data)//调用父类的add方法,在栈底插入元素
{
return super.add(0, data);
}
public T pop()//调用父类的remove方法,删除栈底元素
{
return super.remove(0);
}
public T peek()//调用父类的get方法,查看栈底元素
{
return super.get(0);
}
@Override
public boolean isEmpty() { //和父类的方法一样,其实没必要重写
return super.isEmpty();
}
}
聚合方式
public class MyArrayStack<T> {
MyArrayList<T> stack; //聚合方式实现顺序栈,定义一个顺序表,用来存储数据
public MyArrayStack()
{
stack = new MyArrayList<T>();//初始化顺序表
}
public boolean push(T data)//调用顺序表的add方法,在栈底插入元素
{
stack.add(0, data);
return true;
}
public T pop()//调用顺序表的remove方法,删除栈底元素
{
return stack.remove(0);
}
public T peek()//调用顺序表的get方法,查看栈底元素
{
return stack.get(0);
}
public boolean isEmpty()//调用顺序表的isEmpty方法,判断栈是否为空
{
return stack.isEmpty();
}
}
四,测试
import static java.lang.System.*;
public class TestStack {
public static void main(String[] args) {
MyArrayStack<String> stack1 = new MyArrayStack<String>();
MyArrayStack2<String> stack2 = new MyArrayStack2<String>();
stack1.push("北京");
stack1.push("我");
stack1.push("爱");
stack1.push("你");
while(!stack1.isEmpty())
{
out.print(stack1.pop()+" ");
}
out.println();
stack2.push("数据结构");
stack2.push("操作系统");
stack2.push("软件工程");
stack2.push("计算机图形学");
stack2.push("数据库原理");
out.println("栈顶元素为:"+stack2.peek());
stack2.add(5,"我是栈底元素"); //这里调用了父类的add方法,在栈底插入了元素,
while(!stack2.isEmpty()) //已经破坏了栈后进先出的原则
{
out.print(stack2.pop()+" ");
}
out.println();
}
}
运行结果:
你 爱 我 北京
栈顶元素为:数据库原理
数据库原理 计算机图形学 软件工程 操作系统 数据结构 我是栈底元素
这样我们便借助之前的代码实现了栈。关于上面用到的类MyArrayList请参考以下文章: https://blog.csdn.net/YIXIANG0234/article/details/79901468
五,栈的数组实现
关于栈的实现方法除了上面说的采用顺序表和链表的方式外,还可以用数组来实现,不过数组实现有缺陷,就是必须事先知道栈的大小,否则创建出来的栈容量可能不合理,即过大(浪费空间)或过小(数据溢出),因此最好是使用顺序表或者链表的方式实现。
public class ArrayStack<T> {
private int top; //指向栈底元素
private int maxSize;//栈的容量
private Object[] data;//存放数据的Object数组
public ArrayStack(int size)//根据参数初始化栈
{
if(size>0)
maxSize = size;//初始化栈的大小
data = new Object[maxSize];
top = -1;//栈指针开始指向-1,即没有元素
}
public ArrayStack()//默认栈大小为10
{
this(10);
}
public boolean push(T myData)
{
if(isFull())
throw new RuntimeException("您的栈已满,无法再加入新元素");
top++; //栈指针向后移动
data[top] = myData;//数组对应位置插入数据
return true;
}
public boolean isFull()
{
return top >= maxSize-1?true:false;//top指针大于等于maxSize-1时即为栈满
}
public boolean isEmpty()
{
return top == -1;//栈指针等于-1是表示没有数据,即为栈空
}
@SuppressWarnings("unchecked")
public T pop()
{
if(isEmpty())
throw new RuntimeException("您的栈已空,无法获取栈顶元素");
return (T) data[top--];//弹出栈顶元素,并移动栈顶指针
}
@SuppressWarnings("unchecked")
public T peek()
{
if(isEmpty())
throw new RuntimeException("您的栈已空,无法查看栈顶元素");
return (T) data[top];
}
}
六,测试
import static java.lang.System.out;
public class TestArrayStack {
public static void main(String[] args) {
ArrayStack<String> ss = new ArrayStack<String>(5);
ss.push("Java");
ss.push("C");
ss.push("C++");
ss.push("Python");
ss.push("C#");
//ss.push("JavaScript");
while(!ss.isEmpty())
{
out.println(ss.pop());
}
out.println(ss.peek());
//out.println(ss.pop());
}
}
运行结果:
C#
Python
C++
C
Java
Exception in thread "main" java.lang.RuntimeException: 您的栈已空,无法查看栈顶元素
从测试程序可以看出使用数组也是可以很好模拟栈操作的,除了大小无法动态扩展这个缺点外。