栈及其实现
来源:互联网 发布:python 内容管理系统 编辑:程序博客网 时间:2024/06/10 05:45
1.栈介绍
栈是一种比较重要的线性结构。从数据结构的角度看,栈也是线性表,只不过它是操作受限的线性表。其限制是仅允许在表的一端进行插入和删除操作,不允许在其他任何位置进行插入、查找、删除等操作。
在表中进行插入、删除操作的一端称为栈顶(top),栈顶保存的元素称为栈顶元素。相对的,栈的另一端称为栈底(bottom)。不含元素的空表称为空栈。
假设栈S=(a0,a1,…,an-1),则称a0为栈底元素,an-1为栈顶元素。栈中元素按照a0,a1,…,an-1的次序进栈,退栈的第一个元素应为栈顶元素。换句话说,栈的修改是按后进先出的原则进行的。因此,栈又称为后进先出(Last In First Out)的线性表(简称LIFO结构)。
下图展示了一个栈及数据元素插入和删除的过程:
在上图中,当ABCD均已入栈后,出栈时得到的序列为DCBA,这就是后进先出。
栈的基本操作除了进栈push()
,出栈pop()
之外,还有判空isEmpty()
、取栈顶元素peek()
等操作。
2.栈的顺序存储与实现
和线性表类似,栈也有两种存储结构:顺序存储和链式存储。
顺序栈是使用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放栈中的数据元素。由于栈是一种特殊的线性表,因此在线性表的顺序存储结构的基础上,选择线性表的一端作为栈顶即可。那么根据数组操作的特性,选择数组下标大的一端,即线性表顺序存储的表尾来作为栈顶,此时入栈、出栈操作可以O(1)时间完成。
由于栈的操作都是在栈顶完成,因此在顺序栈的实现中需要附设一个指针top来动态地指示栈顶元素在数组中的位置。通常top可以用栈顶元素所在的数组下标来表示,top=-1
时表示空栈。
栈在使用过程中所需的最大空间难以估计,所以,一般构造栈的时候不应设定最大容量。一种合理的做法和线性表类似,先为栈分配一个基本容量,然后在实际的使用过程中,当栈的空间不够用时再倍增存储空间。
下述代码给出了栈的顺序存储的实现:
public class ArrayStack<T> { private final int DEFAULT_LEN = 8;// 数组的默认大小 private T[] elements;// 数组 private int top; // 栈顶指针 @SuppressWarnings("unchecked") public ArrayStack() { top = -1; elements = (T[]) new Object[DEFAULT_LEN]; } /** * 获取栈的大小,即栈中数据元素的个数 * * @return */ public int size() { return top + 1; } /** * 获取堆栈是否为空,为空返回true,否则返回false * * @return */ public boolean isEmpty() { return top == -1; } /** * 入栈 * * @param e */ public void push(T e) { if (size() >= elements.length) { // 栈满 ensureCapacity(); } elements[++top] = e; } /** * 辅助方法,扩充容量 */ @SuppressWarnings("unchecked") private void ensureCapacity() { T[] a = (T[]) new Object[2 * elements.length + 1]; System.arraycopy(elements, 0, a, 0, size()); elements = a; } /** * 出栈 * * @return */ public T pop() { if (isEmpty()) { throw new EmptyStackException(); } T e = elements[top]; elements[top--] = null; return e; } /** * 获取栈顶元素 * * @return */ public T peek() { if (isEmpty()) { throw new EmptyStackException(); } return elements[top]; }}
以上基于数据实现的栈代码并不难理解。由于有top指针的存在,所以size()
、isEmpty()
方法均可在O(1)时间内完成。push()
、pop()
和peek()
方法,除了需要ensureCapacity()
外,都执行常数基本操作,因此它们的运行时间也是O(1)。
3.栈的链式存储与实现
栈的链式存储即采用链表实现栈。当采用单链表存储线性表后,根据单链表的操作特性选择单链表的头部作为栈顶,此时,入栈和出栈等操作可以在O(1)时间内完成。
由于栈的操作只在线性表的一端进行,在这里使用带头结点的单链表或不带头结点的单链表都可以。使用带头结点的单链表时,结点的插入和删除都在头结点之后进行;使用不带头结点的单链表时,结点的插入和删除都在链表的首结点上进行。
下面以不带头结点的单链表为例实现栈,如下示意图所示:
在上图中,top为栈顶结点的引用,始终指向当前栈顶元素所在的结点。若top为null,则表示空栈。入栈操作是在top所指结点之前插入新的结点,使新结点的next域指向top,top前移即可;出栈则直接让top后移即可。
如下给出了栈的链式存储实现的代码:
public class LinkedStack<T> { /** * 内部结点类 */ private class Node{ private T data; private Node next; public Node() { this(null,null); } public Node(T data, LinkedStack<T>.Node next) { super(); this.data = data; this.next = next; } public T getData() { return data; } public void setData(T data) { this.data = data; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } } private Node top; // 首结点,即栈顶 private int size;// 栈的大小 public LinkedStack() { top = null; size = 0; } public int size(){ return size; } public boolean isEmpty(){ return size == 0; } public void push(T e){ Node newNode = new Node(e, top); top = newNode; size ++; } public T pop(){ if(isEmpty()){ throw new EmptyStackException(); } T data = top.getData(); top = top.getNext(); size --; return data; } public T peek(){ if(isEmpty()){ throw new EmptyStackException(); } return top.getData(); }}
上述LinkedStack
类中有两个成员变量,其中top
表示首结点,也就是栈顶元素所在的结点;size
指示栈的大小,即栈中数据元素的个数。不难理解,所有的操作均可以在O(1)时间内完成。
- 栈及其实现
- 数据结构试验-栈的实现及其应用
- 基本数据结构-栈的实现及其运用
- 栈原理详解及其应用实现
- 栈的C++实现及其应用
- Controller 接口及其实现
- Cell体系结构及其实现
- 线程池及其实现
- RC4加密算法及其实现
- Trie树及其实现
- 数字翻译器及其实现
- 排序及其实现
- AVL树及其实现
- List及其实现类
- 内存池及其实现
- 快速傅里叶变换及其实现
- Gamma校正及其实现
- 最小二乘法及其c++实现
- N!的k进制位数 【打表】【斯特林公式变形】
- Centos7下安装apache过程总结
- C# udp小程序
- RecycleView简单实现滑动删除Item
- 特征值和特征向量(二)
- 栈及其实现
- 数据结构与算法
- scala(一)
- 【Unified Auditing】统一审计的存储(12.2)
- 欢迎使用CSDN-markdown编辑器
- LeetCode 98. Validate Binary Search Tree Add to List
- Android使用Retrofit请求WebService
- 二叉树遍历:已知前序中序输出后序/已知后序中序输出前序
- python基础学习(二):数据类型