一、简介
众所周知,线性表是数据结构的基础,通常有两种实现方式:数组和链表。栈和队列是最常用的数据结构,它们基于线性表实现。
二、栈
定义:栈是限定仅在表尾进行插入和删除操作的线性表,即FILO。
栈被经常类比于弹夹,即先被压如弹夹的子弹最后被打出。根据线性表的实现方式得知,栈的实现方式有两种:数组实现和链表实现。
栈的数组实现:
package basic.data_structure.cha01; /** * 栈:先进后出(FILO),只允许在栈顶操作元素 * 栈的基本操作: * 初始化栈、弹栈、压栈、获取栈顶元素、判断是否为空 * * * 用数组实现栈。 * */ public class ArrayStack<T> { private T[] data; //数据容器 private int maxSize; //栈容量 private int top = -1; //栈顶指针 public ArrayStack(){ this(10); } //初始化栈 public ArrayStack(int capacity){ if(capacity > 0){ data = (T[])new Object[capacity]; maxSize = capacity; top = -1; }else{ throw new RuntimeException("栈的初始化大小不能小于或等于0"); } } public boolean isEmpty(){ return top == -1; } //压栈 public boolean push(T elem){ if(top == maxSize - 1){ throw new RuntimeException("栈满"); } data[++top] = elem; return true; } //获取栈顶元素(不移除) public T peek(){ if(this.isEmpty()){ throw new RuntimeException("空栈"); } return data[top]; } //弹栈 public T pop(){ if(this.isEmpty()){ throw new RuntimeException("空栈"); } return data[--top]; } }
很容易理解,都是实际上都是线性表的基本操作。
栈的链表实现:
package basic.data_structure.cha01; /** * 栈:先进后出 FILO,只允许在栈顶操作元素 * 栈的基本操作: * 初始化栈、压栈、弹栈、获取栈顶元素、当前栈大小 * * 栈的链表实现 * */ public class LinkedStack<T> { private int currentSize;//当前栈大小(链表栈没有大小限制) private Node<T> top; //头节点 //初始化栈 public LinkedStack(){ top = null; //头指针为空 } public boolean isEmpty(){ return top == null; } public int length(){ return currentSize; } //压栈 public boolean push(T elem){ Node<T> node = new Node<T>(elem, top); top = node; currentSize ++; return true; } //获取栈顶元素 public T peek(){ if(this.isEmpty()){ throw new RuntimeException("空栈"); } return top.data; } //弹栈 public T pop(){ if(this.isEmpty()) { throw new RuntimeException("空栈"); } Node<T> node = top; top = top.next; node.next = null; currentSize --; return node.data; } //节点(内部类) private static class Node<T>{ T data; //数据域 Node<T> next; //链域 public Node(){} public Node(T data, Node<T> next){ this.data = data; this.next = next; } } }
总结:
由于线性表的实现方式有“数组”和“链表”两种,所以 栈 的实现方式也分两种,这两种实现方式都实现的栈的主要功能点:压栈、弹栈、获取栈顶元素,但也有所区别。基于数组实现的栈在初始化时需要指定具体的大小,基于链表的实现没有大小限制。
两种实现的本质区别在于:基于数组的实现用操作数组下标,基于链表的实现操作链表节点的指针。
需要注意的是,在基于链表的实现中,弹栈时,要对引用做清理工作,否则会导致无用对象无法被GC回收。