对于数据结构中常用的栈结构,其实现方式通常分为三种:
一、基于简单数组的实现方式
二、基于动态数组的实现
三、基于链表的实现方式
1、简单数组实现方式
//最大空间必须事先声明
public class ArrayStack {
private int top;
private int capacity;
private int[] array;
public ArrayStack() {
capacity = 3;//申明栈的最大空间
array = new int [capacity];
top = -1;
}
public boolean isEmpyt() {
return top == -1;
}
public boolean isStackFull() {
return top == capacity - 1;
}
public void push(int data) {
if(isStackFull()) {
System.out.println("Stack Overflow");
}else {
array[++top] = data;
}
}
public int pop() {
if (isEmpyt()) {
System.out.println("Stack is empyt");
return 0;
}else {
return array[ top--];
}
}
public void deleteStack() {
top = -1;
}
public static void main(String[] args) {
ArrayStack ast= new ArrayStack();
ast.push(3);
ast.push(7);
ast.push(6);
System.out.println(ast.pop());
}
}
局限性:栈的最大空间必须预先声明且不能改变。试图对一个满栈执行入操作将产生一个栈满异常。
2、动态数组实现方法
为了解决栈满情况下的入栈溢出的异常,需要采用动态数组实现。
方案一、当栈满时,每次将数组的大小增加1,这样执行n次入栈操作总的时间开销为:
1+2+...+n = O(n^2),时间复杂度为平方级别。
方案二、重复倍增。这样执行n次入栈操作的时间开销为:
1+2+4+8+...+n/4+n/2+n = n(1+1/2 + 1/4 + 1/8 +.... + 4/n +2/n + 1/n) = 2n = O(n).所以采用倍增的方式n次push的时间开销更小,实际上Java中栈的默认实现方式也是采用该方式。
// 倍增太多可能导致内存溢出
public class DynArrayStack {
private int top;
private int capacity;
private int[] array;
public DynArrayStack() {
capacity = 1;
array = new int[capacity];
top = -1;
}
public boolean isEmpty() {
return top == -1;
}
public boolean isStackFull() {
return (top == capacity - 1);
}
public void push(int data) {
if(isStackFull()) {
doubleStack();
}
array[++ top] = data;
}
private void doubleStack() {
// TODO Auto-generated method stub
int[] newArray = new int[capacity * 2];
System.arraycopy(array, 0, newArray, 0, capacity);
capacity = capacity * 2;
array = newArray;
}
public int pop() {
if(isEmpty()) {
System.out.println("Stack is Empty!");
return 0;
}else {
return array[top --];
}
}
public void deleteStack(){
top = -1;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
DynArrayStack dysta = new DynArrayStack();
dysta.push(3);
dysta.push(7);
dysta.push(9);
System.out.println(dysta.pop());
System.out.println(dysta.isEmpty());
}
}
3、使用链表也可以实现栈。通过在链表的头部插入元素的方式实现push操作,删除链表的表头节点(栈顶节点)实现pop操作。
public class LLStack {
private LLNode headNode;
public LLStack() {
this.headNode = new LLNode();
}
public void push(int data) {
if(headNode == null) {
headNode = new LLNode(data);
}else {
LLNode llNode = new LLNode(data);
llNode.setNext(headNode);
headNode = llNode;
}
}
public int top() {
if (headNode == null) {
return 0;
}else {
return headNode.getData();
}
}
public int pop() {
if(headNode == null) {
System.out.println("stack Empty!");
return 0;
}else {
int data = headNode.getData();
headNode = headNode.getNext();
return data;
}
}
public boolean isEmpty() {
if(headNode == null) return true;
else return false;
}
public void deleteStack() {
headNode = null;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
LLStack llst = new LLStack();
llst.push(6);
llst.push(7);
System.out.println(llst.pop());
}
public class LLNode {
private int data;
private LLNode next;
public LLNode() {
}
public LLNode(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public LLNode getNext() {
return next;
}
public void setNext(LLNode next) {
this.next = next;
}
}
}
采用该方法唯一麻烦的地方在于删除链表时如果采用c++实现的链表,则删除链表需要循环链表删除所有元素,其时间复杂度为O(n),采用Java由于其垃圾回收机制可以直接将链表的头节点置为空即可。
以上就是栈的三种实现机制,Java中默认采用第二种方式,栈的初始大小为5000.