一、栈的概述
和队列一样,栈是一种操作受限的线性表,和队列不同的是栈只允许从一端插入和删除数据。
因此,栈的特点是:
- 栈中的数据元素遵守“先进后出"(First In Last Out)的原则,简称FILO结构。
- 限定只能在栈顶进行插入和删除操作,栈最重要的特征
一些需要了解的概念:
栈顶与栈底:允许元素插入与删除的一端称为栈顶,另一端称为栈底。
压栈:栈的插入操作,叫做进栈,也称压栈、入栈。
弹栈:栈的删除操作,也叫做出栈。
就像生活中我们取放盘子,当一摞盘子在桌子上时候,我们总是会取最上边的盘子,同时,我们在放盘子时,也会放在最上面。其实,每个栈都有一个栈顶指针,它初始值为-1,且总是指向最后一个入栈的元素,栈有两种处理方式,即进栈(push)和出栈(pop),因为在进栈只需要移动一个变量存储空间,所以它的时间复杂度为O(1),但是对于出栈分两种情况,栈未满时,时间复杂度也为O(1),但是当栈满时,需要重新分配内存,并移动栈内所有数据,所以此时的时间复杂度为O(n)。
栈有两种存储方式,即线性存储和链接存储(链表)。
二、栈的常用操作和实现
栈的常用操作有:
- pop弹栈,向栈底添加一个元素
- pop 压栈,从栈顶取出一个元素
- size 判定栈中所含元素的个数
- isEmpty 判断栈是否为空
- peek 获取栈顶元素的值,复制栈顶元素返回,但不进行删除
1、线性存储
线性存储的方式和线性表基本一致,不同的是添加了后进先出的限制,并且是静态分配的,即使用前,它的内存就已经以数组的形式分配好了,所以在初始化时,需要指明栈的节点最大个数。基于数组的栈是以数组为底层数据结构的,通常以数组头为栈底,数组头到数组尾为栈顶的生长方向。
public class MyStack { //大小 private int size; //栈的最大容量 private int maxSize; //默认最大容量10 private static final int DEFAULT_MAX_SIZE =10;/数组(用来保存数据)String[] stack; //有参初始化,设置栈的最大容量 public MyStack(int maxSize) { this.maxSize=maxSize; stack =new String[maxSize]; } //无参初始化,设置栈的默认容量 public MyStack() { this(DEFAULT_MAX_SIZE); } //进行入栈操作 public void push(String s) { if(size >= maxSize) { throw new IndexOutOfBoundsException("栈已经满了!"); } stack[size] =s; size++; look(); } //出栈操作 public String pop() { String rs=stack[size-1]; //从后往前将做高位的值赋为null stack[size-1]=null; size--; look(); return rs; } //清栈操作 public void clear() { for(int i=0;i
2、链接存储
对于单链表类,只要做适当的修改,限制其插入、删除、修改和访问结点的行为,使其符合栈先进后出的规则即可,另外需要单独提供栈访问的接口函数,例如进栈、出栈、获取栈大小等。基于单链表的栈是以链表为底层的数据结构的,以链表头为栈顶,便于节点的插入与删除,压栈产生的新节点将一直出现在链表的头部
/ * 栈的实现 * */public class MyStackNode { //大小 private int size; //栈的最大容量 private int maxSize; //默认最大容量10 private static final int DEFAULT_MAX_SIZE =10; //栈顶的节点 private Node top; //有参初始化,设置栈的最大容量 public MyStackNode(int maxSize) { this.maxSize=maxSize; } //无参初始化,设置栈的默认容量 public MyStackNode() { this(DEFAULT_MAX_SIZE); } //进行入栈操作 public void push(String s) { if(size >= maxSize) { throw new IndexOutOfBoundsException("栈已经满了!"); } Node node=new Node(s,top); top=node; look(); size++; } //移除栈顶的元素 public String pop() { if(top == null) return null; Node oldTop=top; Node newTop=top.next; String rs=oldTop.s; oldTop.next=null; oldTop.s=null; oldTop=null; top=newTop; look(); size--; return rs; } //清栈操作 public void clear() { Node node=top; while(node !=null) { Node newTop=node.next; node.next=null; node.s=null; node =newTop; } look(); size =0; } //添加一个Node内部类 class Node{ public String s; public Node next; public Node(String s,Node next) { this.s=s; this.next=next; } public void setNext(Node next) { this.next=next; } } //获取栈的长度 public int getSize() { return size; } //动态显示栈中的数据 private void look() { System.out.println(""); Node node=top; while(node !=null) { System.out.print(node.s + " "); node=node.next; } } public static void main(String[] args) { MyStackNode stack=new MyStackNode(4); //MyStack stack=new MyStack(); stack.push("壹"); stack.push("贰"); stack.push("叁"); stack.push("肆"); stack.pop(); stack.pop(); stack.push("money"); stack.push("money"); System.out.println("当前栈的容量为:" + stack.getSize()); stack.clear(); }}测试:
三、栈的应用场景
- 数制转换
- 字符匹配
- 数据反转
- 括号匹配检验
- 平衡符号的判断
四、栈和队列的区别
栈的插入和删除操作都是在一端进行的,而队列的操作却是在两端进行的。
栈是先进后出,队列是先进先出。
栈只允许在表尾一端进行插入和删除,队列只允许在表尾一端进行插入,在表头一端进行删除。