一、栈结构
栈(stack)是限制插入和删除只能在一个位置上的表,该位置是 表的末端,叫做栈的顶(Top)。对栈的基本操作有push(进栈),pop(出栈),peak(栈顶元素),size(栈容量)等。
栈的核心思想:“先进后出”。
二、案例一:数组实现“栈”
1 package com.xfwl.algorithmAnalysis.stack; 2 3 import java.util.Arrays; 4 5 /** 6 * 自定义栈结构(基于数组的形式) 7 * 栈的核心思想:先进后出 8 * @function 日常学习测试 9 * @author 小风微凉 10 * @time 2018-5-19 上午10:19:07 11 * @param <T> 12 */ 13 public class MyStackDefin3<T> { 14 /** 15 * 定义一个默认扩展容量 16 */ 17 private static final int DEFAULT_CAPACITY=10; 18 /** 19 * 栈-容器数组 20 */ 21 private T[] stackArr; 22 /** 23 * 计数器 24 */ 25 private int nodeCount=0; 26 /** 27 * 设置容器大小 28 * @param newCapacity 新容量大小 29 * 说明:这里参考ArrayList源码中的一套扩展规则">>" 30 */ 31 public void ensureCapacity(int newCapacity){ 32 //大小范围检查 33 if(newCapacity<=this.size()){//不超过了当前容器的容量大小 34 return;//则不需要扩展容器容量 35 } 36 //数组初始值判断 37 if(this.stackArr==null){ 38 stackArr=(T[]) new Object[newCapacity]; 39 return;//第一次初始化进来 40 } 41 //需要扩展容器容量 42 T[] newItems=(T[]) Arrays.copyOf(this.stackArr, newCapacity,this.stackArr.getClass()); 43 this.stackArr=newItems; 44 } 45 /** 46 * 栈构造器 47 */ 48 public MyStackDefin3(){ 49 //重置栈结构 50 this.ensureCapacity(DEFAULT_CAPACITY); 51 nodeCount=0; 52 } 53 /** 54 * 数据入栈 55 * @param data 56 */ 57 public void push(T data){ 58 if(this.size()==this.stackArr.length){//数组容量达到上限 59 int newCapacity=this.size()+this.size()>>1;//扩展量:取当前容量的一半,且向下取整 60 this.ensureCapacity(newCapacity); 61 } 62 this.stackArr[this.size()]=data; 63 this.nodeCount++; 64 } 65 /** 66 * 数据出栈 67 */ 68 public void pop(){ 69 this.stackArr[this.size()-1]=null; 70 this.nodeCount--; 71 } 72 /** 73 * 返回当前栈中的数据条数 74 * @return 75 */ 76 public int size(){ 77 return this.nodeCount; 78 } 79 80 /** 81 * 返回栈的最顶端的元素 82 * @return 83 */ 84 public T peak(){ 85 return this.stackArr[(this.size()-1)<0?0:this.size()-1]; 86 } 87 /** 88 * 打印当前栈信息 89 */ 90 public void print(){ 91 System.out.println("----------开始打印----------------------"); 92 if(this.size()==0){ 93 System.out.println("空栈,无检索数据!"); 94 }else{ 95 for(int i=0;i<this.size();i++){ 96 System.out.println("栈结点数据:"+this.stackArr[i]); 97 } 98 } 99 System.out.println("----------打印结束----------------------"); 100 } 101 }
测试类:
1 package com.xfwl.algorithmAnalysis.stack; 2 /** 3 * 测试类 4 * @function 5 * @author 小风微凉 6 * @time 2018-5-19 上午9:43:05 7 */ 8 public class Test3 { 9 public static void main(String[] args) { 10 //创建一个空栈 11 MyStackDefin3<Object> stack=new MyStackDefin3<>(); 12 stack.print(); 13 System.out.println("当前栈顶数据:"+stack.peak()); 14 //数据入栈 15 stack.push("第1个数据"); 16 stack.push("第2个数据"); 17 stack.push("第3个数据"); 18 stack.push("第4个数据"); 19 stack.push("第5个数据"); 20 //打印 21 stack.print(); 22 System.out.println("当前栈顶数据:"+stack.peak()); 23 //数据出栈 24 stack.pop(); 25 //打印 26 stack.print(); 27 System.out.println("当前栈顶数据:"+stack.peak()); 28 } 29 }
运行结果:
----------开始打印---------------------- 空栈,无检索数据! ----------打印结束---------------------- 当前栈顶数据:null ----------开始打印---------------------- 栈结点数据:第1个数据 栈结点数据:第2个数据 栈结点数据:第3个数据 栈结点数据:第4个数据 栈结点数据:第5个数据 ----------打印结束---------------------- 当前栈顶数据:第5个数据 ----------开始打印---------------------- 栈结点数据:第1个数据 栈结点数据:第2个数据 栈结点数据:第3个数据 栈结点数据:第4个数据 ----------打印结束---------------------- 当前栈顶数据:第4个数据
三、案例二:单链表实现“栈”
1 package com.xfwl.algorithmAnalysis.stack; 2 /** 3 * 自定义栈结构(基于单链表的形式) 4 * 栈的核心思想:先进后出 5 * @function 日常学习测试 6 * @author 小风微凉 7 * @time 2018-5-18 下午1:49:31 8 */ 9 public class MyStackDefin<T> { 10 /** 11 * 头结点 12 */ 13 private Node<T> head; 14 /** 15 * 计数器 16 */ 17 private int nodeCount=0; 18 /** 19 * 栈构造器 20 */ 21 public MyStackDefin(){ 22 //重置栈结构 23 head=new Node(null,null); 24 nodeCount=0; 25 } 26 /** 27 * 内置一个结点类 28 */ 29 private class Node<T>{ 30 /** 31 * 结点数据域 32 */ 33 private T data; 34 /** 35 * 结点指针域 36 */ 37 private Node<T> next; 38 /** 39 * 结点构造函数 40 */ 41 public Node(T data,Node<T> node){ 42 this.data=data; 43 this.next=node; 44 } 45 } 46 /** 47 * 数据入栈 48 * @param data 49 */ 50 public void push(T data){ 51 //创建一个结点 52 Node<T> node=new Node(data,null); 53 //入栈 54 this.peakNode().next=node; 55 this.nodeCount++; 56 } 57 /** 58 * 数据出栈 59 */ 60 public void pop(){ 61 //找到最后一个结点 62 Node tmp=this.head; 63 //判断是否只有一个头结点 64 if(this.size()==0){ 65 System.out.println("当前栈中无数据,请先让数据入栈!"); 66 return ; 67 }else{ 68 int count=0; 69 while(tmp.next!=null){ 70 if(count==(this.size()-1)){ 71 break; 72 } 73 tmp=tmp.next; 74 count++; 75 } 76 //出栈操作 77 tmp.next=null; 78 this.nodeCount--; 79 } 80 } 81 /** 82 * 返回当前栈中的数据条数 83 * @return 84 */ 85 public int size(){ 86 return this.nodeCount; 87 } 88 /** 89 * 返回栈的最顶端的元素结点 90 * @return 91 */ 92 public Node<T> peakNode(){ 93 Node<T> tmp=this.head; 94 while(tmp.next!=null){ 95 tmp=tmp.next; 96 } 97 return tmp; 98 } 99 /** 100 * 返回栈的最顶端的元素 101 * @return 102 */ 103 public T peak(){ 104 Node<T> tmp=this.head; 105 while(tmp.next!=null){ 106 tmp=tmp.next; 107 } 108 return tmp.data; 109 } 110 /** 111 * 打印当前栈信息 112 */ 113 public void print(){ 114 System.out.println("----------开始打印----------------------"); 115 if(this.size()==0){ 116 System.out.println("空栈,无检索数据!"); 117 }else{ 118 Node tmp=this.head.next; 119 for(int i=0;i<this.size();i++){ 120 System.out.println("栈结点数据:"+tmp.data); 121 tmp=tmp.next; 122 } 123 } 124 System.out.println("----------打印结束----------------------"); 125 } 126 }
测试类:
package com.xfwl.algorithmAnalysis.stack; /** * 测试类 * @function * @author 小风微凉 * @time 2018-5-19 上午9:43:05 */ public class Test { public static void main(String[] args) { //创建一个空栈 MyStackDefin<Object> stack=new MyStackDefin<>(); stack.print(); System.out.println("当前栈顶数据:"+stack.peak()); //数据入栈 stack.push("第1个数据"); stack.push("第2个数据"); stack.push("第3个数据"); stack.push("第4个数据"); stack.push("第5个数据"); //打印 stack.print(); System.out.println("当前栈顶数据:"+stack.peak()); //数据出栈 stack.pop(); //打印 stack.print(); System.out.println("当前栈顶数据:"+stack.peak()); } }
运行结果:
----------开始打印---------------------- 空栈,无检索数据! ----------打印结束---------------------- 当前栈顶数据:null ----------开始打印---------------------- 栈结点数据:第1个数据 栈结点数据:第2个数据 栈结点数据:第3个数据 栈结点数据:第4个数据 栈结点数据:第5个数据 ----------打印结束---------------------- 当前栈顶数据:第5个数据 ----------开始打印---------------------- 栈结点数据:第1个数据 栈结点数据:第2个数据 栈结点数据:第3个数据 栈结点数据:第4个数据 ----------打印结束---------------------- 当前栈顶数据:第4个数据
四、案例三:双链表实现“栈”
1 package com.xfwl.algorithmAnalysis.stack; 2 /** 3 * 自定义栈结构(基于双链表的形式) 4 * 栈的核心思想:先进后出 5 * @function 日常学习测试 6 * @author 小风微凉 7 * @time 2018-5-19 上午10:00:07 8 * @param <T> 9 */ 10 public class MyStackDefin2<T> { 11 /** 12 * 头结点 13 */ 14 private Node<T> head; 15 /** 16 * 计数器 17 */ 18 private int nodeCount=0; 19 /** 20 * 栈构造器 21 */ 22 public MyStackDefin2(){ 23 //重置栈结构 24 head=new Node(null,null,null); 25 nodeCount=0; 26 } 27 /** 28 * 内置一个结点类 29 */ 30 private class Node<T>{ 31 /** 32 * 结点数据域 33 */ 34 private T data; 35 /** 36 * 结点前驱指针域 37 */ 38 private Node<T> prev; 39 /** 40 * 结点后驱指针域 41 */ 42 private Node<T> next; 43 /** 44 * 结点构造函数 45 */ 46 public Node(T data,Node<T> prev,Node<T> next){ 47 this.data=data; 48 this.prev=prev; 49 this.next=next; 50 } 51 } 52 /** 53 * 数据入栈 54 * @param data 55 */ 56 public void push(T data){ 57 //创建一个结点 58 Node<T> node=new Node(data,this.peakNode(),null); 59 //入栈 60 this.peakNode().next=node; 61 this.nodeCount++; 62 } 63 /** 64 * 数据出栈 65 */ 66 public void pop(){ 67 //找到最后一个结点 68 Node tmp=this.head; 69 //判断是否只有一个头结点 70 if(this.size()==0){ 71 System.out.println("当前栈中无数据,请先让数据入栈!"); 72 return ; 73 }else{ 74 int count=0; 75 while(tmp.next!=null){ 76 if(count==(this.size()-1)){ 77 break; 78 } 79 tmp=tmp.next; 80 count++; 81 } 82 //出栈操作 83 tmp.next=null; 84 this.nodeCount--; 85 } 86 } 87 /** 88 * 返回当前栈中的数据条数 89 * @return 90 */ 91 public int size(){ 92 return this.nodeCount; 93 } 94 /** 95 * 返回栈的最顶端的元素结点 96 * @return 97 */ 98 public Node<T> peakNode(){ 99 Node<T> tmp=this.head; 100 while(tmp.next!=null){ 101 tmp=tmp.next; 102 } 103 return tmp; 104 } 105 /** 106 * 返回栈的最顶端的元素 107 * @return 108 */ 109 public T peak(){ 110 Node<T> tmp=this.head; 111 while(tmp.next!=null){ 112 tmp=tmp.next; 113 } 114 return tmp.data; 115 } 116 /** 117 * 打印当前栈信息 118 */ 119 public void print(){ 120 System.out.println("----------开始打印----------------------"); 121 if(this.size()==0){ 122 System.out.println("空栈,无检索数据!"); 123 }else{ 124 Node tmp=this.head.next; 125 for(int i=0;i<this.size();i++){ 126 System.out.println("栈结点数据:"+tmp.data); 127 tmp=tmp.next; 128 } 129 } 130 System.out.println("----------打印结束----------------------"); 131 } 132 }
测试类:
package com.xfwl.algorithmAnalysis.stack; /** * 测试类 * @function * @author 小风微凉 * @time 2018-5-19 上午9:43:05 */ public class Test2 { public static void main(String[] args) { //创建一个空栈 MyStackDefin2<Object> stack=new MyStackDefin2<>(); stack.print(); System.out.println("当前栈顶数据:"+stack.peak()); //数据入栈 stack.push("第1个数据"); stack.push("第2个数据"); stack.push("第3个数据"); stack.push("第4个数据"); stack.push("第5个数据"); //打印 stack.print(); System.out.println("当前栈顶数据:"+stack.peak()); //数据出栈 stack.pop(); //打印 stack.print(); System.out.println("当前栈顶数据:"+stack.peak()); } }
运行结果:
----------开始打印---------------------- 空栈,无检索数据! ----------打印结束---------------------- 当前栈顶数据:null ----------开始打印---------------------- 栈结点数据:第1个数据 栈结点数据:第2个数据 栈结点数据:第3个数据 栈结点数据:第4个数据 栈结点数据:第5个数据 ----------打印结束---------------------- 当前栈顶数据:第5个数据 ----------开始打印---------------------- 栈结点数据:第1个数据 栈结点数据:第2个数据 栈结点数据:第3个数据 栈结点数据:第4个数据 ----------打印结束---------------------- 当前栈顶数据:第4个数据
五、总结
栈本身是一种结构思想的具现,实现的方式不固定,我们可以使用最简单的方式,或者复杂(结构层次复杂)的方式去实现,这都没问题,关键点是理解“栈”的核心思想:“先进后出”,在实际的运用场景中,我们就可以根据实际情况创造+改良来产生一套最佳的“栈”结构。