Java基础 - 栈的顺序存储结构及实现

栈和队列:

前面已经介绍完了线性表的顺序和链式两中实现,但如果再对线性表增加一些额外的限制和约束,例如,去除普通线性表中通过索引访问数据元素的功能,去除普通线性表中查询某个元素在表中的位置的功能。去除普通线性表中可以在任意位置随意增加、删除元素的功能,而是改为只允许在线性表的某端添加、删除元素,这时候普通线性表就会变成另外两种特殊的线性表:栈和队列。

从逻辑上看,栈和队列其实是由普通的线性表发展而来的,为普通线性表增加一些特殊的限制就可以得到栈和队列了。从功能上来看,栈和队列比普通线性表的功能相对弱一些,但在一些特殊的场合下,使用栈和队列会更有利,例如,编译器实现函数调用时需要使用栈来存储断点,实现递归算法时也需要使用栈来存储。当然我这里最主要是因为考虑到后面会有树的深度优先算法和广度优先算法打下基础。

栈是一种数据结构,他代表只能在某一端进行插入、删除操作的特殊线性表,通常就是在线性表的尾端进行插入、删除操作。
对于栈而言,允许进行插入、删除操作的一端被称为栈顶(top),另一端则被称为栈底(bottom)。
如果一个栈不包含任何元素,那么这个栈就被称为空栈。
从栈顶插入一个元素被称为进栈或者入栈或者压栈,对应的英文说法为 push。
从栈顶删除一个元素被称为出栈或者退栈,对应的英文说法为 pop。
如下图为栈的操作示意图:

Java基础 - 栈的顺序存储结构及实现_第1张图片

对于a0,a1,a2,……,an-1的栈,假设栈中元素按a0,a1,a2,……,an-1的次序进栈,那么a0为栈的栈底元素,an-1为栈顶元素。出栈时
第一个弹出的元素为栈顶元素,也就是an-1、也就是说栈中的元素的修改是按后进先出(LIFO)的原则进行的。

归纳起来,可以再对栈下一个定义:栈是一种后进先出(LIFO)的线性表。

栈的常用操作:
栈是一种被限制的线性表,通常不应该提供线性表中的如下操作:
(1)、获取指定索引处的元素。
(2)、按值查找数据元素的位置。
(3)、向指定索引位置插入元素。
(4)、删除指定索引位置处的元素。
从上面这些方法可以看出,栈不应提供从中间任意位置访问元素的方法。也就是说,栈只允许从栈顶插入、删除元素。

栈的常用操作如下:
(1)、初始化:通常是一个构造器,用于创建一个空栈。
(2)、返回栈的长度:该方法用于返回栈中数据元素的长度。
(3)、入栈:向栈的栈顶插入一个数据元素,栈的长度 +1。
(4)、出栈:向栈的栈顶删除一个数据元素,栈的长度 -1。返回被删除的元素
(5)、访问栈顶元素:返回栈顶的数据元素,但不删除。
(6)、判断栈是否为空栈:该方法判断栈是否为空,如果为空则返回ture,否则则返回false。
(7)、清空栈:将栈清空。
对于栈而言,上面的加粗的部分就是他的标志性方法。

顺序存储结构的栈简称为顺序栈,它利用一组地址连续的存储单元依次存放从栈底到栈顶的数据元素。栈底位置固定不变,他的栈顶元素可以直接通过顺序栈底层数组的数组元素arr[size - 1]来访问。顺序栈的存储结构示意图如下:

Java基础 - 栈的顺序存储结构及实现_第2张图片

1、进栈
对于顺序栈的进栈操作而言,只需将新的元素存入栈内,然后让记录栈的元素个数的size + 1。程序即可再次通过arr[size - 1]重新访问新的栈顶元素。进栈操作如下示意图:

Java基础 - 栈的顺序存储结构及实现_第3张图片

由于顺序栈底层通常采用数组来保存数据元素,因此可能出现的情况是:当程序试图让一个数据元素进栈,底层数组已满,那么就必须扩充底层数组的长度来容纳

新的进栈的数据元素。


2、出栈
对于顺序栈的出栈操作而言,需要将栈顶的元素弹出,程序要做两件事:
(1)让记录栈内元素个数的变量size-1。
(2)释放数组对栈顶元素的引用。
出栈操作示意图如下:
Java基础 - 栈的顺序存储结构及实现_第4张图片
对于删除操作而言,只要让记录栈内元素个数的size - 1,程序即可通过arr[size - 1]访问到新的栈的栈顶元素。但不要忘记释放原来栈顶元素的数组索引。
否则会引起内存泄漏。


下面是代码实现:

import java.util.Arrays;

public class SequenceStack {
	private final int DEFAULT_SIZE = 10;
	//栈的容量
	private int capacity;
	//底层存放数据元素的数组
	private Object[] elementData;
	//当前栈所存放的元素个数
	private int size = 0;
	
	//默认数组扩容向左移一位
	private int capacityIncrementInt = 1;
	//初始化1
	public SequenceStack(){
		capacity = DEFAULT_SIZE;
		elementData = new Object[capacity];
	}
	//初始化2
	public SequenceStack(T element){
		/*capacity = DEFAULT_SIZE;
		elementData = new Object[capacity];*/
		this();
		elementData[0] = element;
		size ++;
	}
	//初始化3
	public SequenceStack(T element, int initCapacity){
		capacity = initCapacity;
		elementData = new Object[capacity];
		elementData[0] = element;
		size ++;
	}
	//初始化4
	public SequenceStack(T element, int initCapacity, int initCapacityIncrementInt){
		this(element, initCapacity);
		capacityIncrementInt = initCapacityIncrementInt;
	}
	//返回元素个数
	public int length(){
		return size;
	}
	//是否为空栈
	public boolean isEmpty(){
		return size == 0;
	}
	
	//位运算扩容,很麻烦,而且性能差
	private void expendCapacity(int minCapacity){
		while(capacity < minCapacity){
			capacity <<= capacityIncrementInt;
		}
		elementData = Arrays.copyOf(elementData, capacity);
	}
	
	//入栈
	public void push(T element){
		expendCapacity(size + 1);
		elementData[size] = element;
		size ++;
	}
	
	//出栈
	@SuppressWarnings("unchecked")
	public T pop(){
		T t = (T) elementData[size - 1];
		elementData[size - 1] = null;
		size --;
		return t;
	}
	
	//返回栈顶元素,但不删除
	@SuppressWarnings("unchecked")
	public T peek(){
		return (T) elementData[size - 1];
	}
	
	//清空栈
	public void clear(){
		if(size > 0){
			/*for(int i = 0; i < size; i ++){
				elementData[i] = null;
			}*/
			Arrays.fill(elementData, null);
			size = 0;
		}
	}
	
	//toString方法
	public String toString(){
		if(size == 0){
			return "[]";
		}else{
			StringBuffer sb = new StringBuffer("[");
			for(int i = 0; i < size; i ++){
				sb.append(elementData[i].toString() + ",");
			}
			return sb.toString().substring(0, sb.length() - 1) + "]";
		}
		
	}
}

从上面的程序可以看出,当采用基于数组的方式来实现顺序栈时,程序比普通线性表更简单。这符合前面的介绍:从功能上来看,栈比普通线性表的功能更弱;栈是一种被限制过得线性表,只能从栈顶插入、删除元素。



测试类如下:

import com.yc.list.SequenceStack;

public class SequenceStackTest {
	public static void main(String[] args) {
		SequenceStack stack = new SequenceStack();
		stack.push(new Student("WB", "湖工", 20));
		stack.push(new Student("LS", "湖工", 22));
		stack.push(new Student("QYB", "湖工", 21));
		System.out.println( "PUSh后的栈为:  " + stack.toString());
		
		Student speek = stack.peek();
		System.out.println("peek的数据元素:  "+speek.toString());
		System.out.println("peek后的栈为:  "+stack.toString());
		
		Student spop1 = stack.pop();
		System.out.println( "pop的数据元素:   "+spop1);
		System.out.println( "pop后的栈为:   "+stack.toString());
		
		Student spop2 = stack.pop();
		System.out.println( "pop的数据元素:   "+spop2);
		System.out.println( "pop后的栈为:   "+stack.toString());
	}
}


输出结果: Java基础 - 栈的顺序存储结构及实现_第5张图片

你可能感兴趣的:(Java基础)