Java数据结构与算法之栈(Stack)

一、栈的概述

栈是一种特殊线性表,与数组和链表最大的区别就是,栈只允许在栈顶操作,不允许在栈底操作,也就是出栈和入栈都在发生在栈顶。这就有点类似叠落在一起的盘子,每次放盘子都是从下到上一个一个的放,取盘子也是从上到下一个一个的取。如下图:

从栈的操作上我们可以看出,栈是一种“受限制”的线性表,只允许从一端插入或者删除。人栈或出栈基本的操作原则是“先进后出或后进先出”,也就是第一个进入栈的元素,只能最后一个才能出栈;最后一个进入栈的元素,总是第一个出栈,然后依次操作。

二、栈的基本操作

栈的主要操作包括入栈和出栈,当然也还有清空栈、栈顶取值、栈的容量大小。栈可以有两种实现方式,数组实现的是顺序栈,链表实现的是链式栈,下面我们用数组来演示顺序栈的实现方式:

首实现接口,包含基本栈的基本操作:

package cn.com.stack;
/**
 * 栈的顶级接口
 * @author Administrator
 * @param <T>
 */
 interface Stack<T> {
	 /**
	  * 栈是否为空
	  * @return
	  */
	 boolean isEmpty();
	 /**
	  * 入栈
	  * @param data
	  */
	void push(T data);
	/**
	 * 出栈-删除栈顶的元素
	 * @param data
	 */
	T pop();
	/**
	 * 获取栈顶元素
	 * @return
	 */
	T peek();
	/**
	 * 清空栈
	 */
	void clear();
        /**
         *栈的大小
         */
        void size();
}

栈的具体实现:首先实现栈的顶级接口Stack<T>,声明了栈的大小、存放数据的临时数组、栈的初始化大小,同时新建了栈的两个构造方法。

package cn.com.stack;

import java.util.EmptyStackException;

/**
 * 顺序栈的实现
 * @author Administrator
 */
public class MyStack<T> implements Stack<T>{

	private int size;//栈的容量大小
	private static final int capacity=10;//默认容量为10
	private T []  elementData;//存放元素的数组
	
	/**
	 * 指定栈的大小
	 * @param capacity
	 */
	public MyStack(int capacity){
		elementData = (T[]) new Object[capacity];
	}
	/**
	 * 无参构造方法,默认为10
	 */
	public MyStack(){
		elementData = (T[]) new Object[this.capacity];
	}

从获取栈顶元素:最后一个入栈的元素,由于栈的结构特点,栈的大小也就是栈顶元素在数组中的下标位置。

	/**
	 * 获取栈顶元素
	 */
	@Override
	public T peek() {
		//如果栈为空直接抛错
		if(isEmpty()){
			throw new EmptyStackException();
		};
		return temp[size];
	}

入栈操作:当入栈时先判断是否需要扩容,将原来的存储数据的元素扩大到原来的两倍。

        /**
	 * 入栈,栈已满时进行扩容
	 */
	@Override
	public void push(T data) {
		//当栈以满时,进行扩容
		ensureCapacity(size*2);
		elementData[size++]=data;
		
	}
	/**
	 * 判断数据是否需要扩容
	 * @param index
	 */
	public void ensureCapacity(int capacity){
		if (size == elementData.length) {
			//将当前数组赋值给临时数组
			Object[] elementDataArray = this.elementData;
			this.elementData = (T[]) new Object[capacity];
			// 把原来的数组赋值复制到新的数组中
			for (int i = 0; i < size; i++) {
				this.elementData[i] = (T) elementDataArray[i];
			}
		}
	}

出栈操作:栈的大小就是栈顶元素在数组中的下标位置,在删除栈顶元素后,同时将数组大小减少至栈的大小。

        /**
	 * 出栈操作,返回出栈的元素
	 */
	@Override
	public T pop() {
		//如果栈为空,直接抛异常
		if(isEmpty()){
			throw new EmptyStackException();
		}
		T oldData =this.elementData[size-1];
		size--;//栈的大小减少1
		//将存储数据的数组缩小为栈的大小
		T []  elementTemp =  (T[]) new Object[size];
		for (int i = size-1; 0 <= i; i--){
			elementTemp[i]=elementData[i];
		}
		elementData=elementTemp;
		
		return oldData;
	}

栈的其他操作:

	/**
	 * 栈的大小
	 * @return
	 */
	public int size(){
		return this.size;
	}
    	/**
	 * 判断栈是否为空
	 */
	@Override
	public boolean isEmpty() {
		return this.size==0;
	}
	/**
	 * 清除栈元素
	 */
	@Override
	public void clear() {
		this.size=0;
		
	}

栈的基本操作已经分析完了,普通栈从入栈和出栈的操作可以看出只涉及栈顶元素的操作,所以时间复杂度时O(1),不过在动态扩容的栈中,在需要扩容时,需要重新申请内存和搬移数据,而在出栈时,将数组进行了缩容,所以这里的动态扩容的栈的时间复杂度为O(n)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值