栈的定义
后进先出,先进后出,是一种操作受限的线性表,只允许在一端插入和删除数据。大多数使用栈的场景都可以使用数组或者链表进行代替,但是数组和链表暴露了太多操作的接口,看上去很灵活,但是对应的风险增大了,更加地不可控。
定容栈
即在创建的时候就指定一个栈的大小
创建一个定容栈
未使用范型
package com.pjh.Stack;
public class FixedCapacityStack {
/*定义一个数组*/
private String[] a;
/*定义一个大小*/
private int N;
/*有参构造方法*/
public FixedCapacityStack(int n) {
a=new String[n];
}
/*判断数组是否为空*/
public boolean isEmpty(){
return N==0;
}
/*返回数组的大小*/
private int size(){
return N;
}
/*出栈*/
public String pop(){
return a[--N];
}
/*入栈*/
public void push(String item){
a[N++]=item;
}
}
使用范型
注意:由于某些历史与技术的原因创建范型数组是不被允许的
实现代码
package com.pjh.Stack;
public class FixedCapacityStack2<Item> {
/*定义一个数组*/
private Item a[];
/*定义一个大小*/
private int N;
/*有参构造方法*/
public FixedCapacityStack2(int n) {
a=(Item[]) new Object[n];
}
/*判断数组是否为空*/
public boolean isEmpty(){
return N==0;
}
/*返回数组的大小*/
private int size(){
return N;
}
/*出栈*/
public Item pop(){
return a[--N];
}
/*入栈*/
public void push(Item item){
a[N++]=item;
}
}
代码优化
问题1
问题概述
选择数组表示栈的内容必须预先估计栈的最大容量,在java中,数组一但创建其大小是无法改变的,因此栈使用的空间只能是这个最大容量的一部分。选择大容量的用例在栈为空或者几乎为空的时候会浪费大量的内存,例如,一个交易系统可能会涉及数十亿笔交易和数千个交易的集合。即使这种系统一般都会限制每笔交易只能出现在一个集合中,但用例必须保证所有的集合都有能力保存所有的交易,另一方面,如果集合变的比数组大,那么用例可能会溢出。因此我们需要一个方法来检测栈是不是已经满了,还有一个方法来对栈进行扩容,这个其实就是动态数组的思想
解决方案
isFull()方法判断栈是不是已经满了
/*判满方法,只供内部方法调用*/
private void isFull(){
/*如果满了则调用数组扩容方法,扩容的倍数可以根据实际的需要调节*/
if (N==a.length){
resize();
}
}
resize()扩容方法
private void resize(){
int newCapacity= (int) (a.length * 2);
int lastCapacity=a.length;
Item[] newArray = (Item[]) new Object[newCapacity];
for (int i = 0; i < lastCapacity; i++)
newArray[i]=a[i];
/*将a数组的首地址指向newArray数组*/
a=newArray;
}
问题2
问题概述2
如果我们在使用过程中将大量的数据进行出栈处理了,但是之前的数组空间很大,所以我们也需要动态的进行缩小栈的容量,以到达内存使用率的最大化
解决方案
在调用pop()出栈函数的时候,我们首先删除栈顶的元素,然后如果数组太大我们就将数组的长度减半
栈的使用场景
就拿我们平时使用的浏览器来举例,我们在同一个页面点击多个网页的时候,常常使用浏览器的退后与前进按钮,以回到前一个打开的页面和后一个打开的页面
比如我们在统一页面中先后打开了
taobao.com
jd.com
tx.com
当我们点击左箭头的的时候回到jd.com
当我们再点击右箭头的时候又回到taobao.com
这个操作就可以使用栈来实现
**
图解
当我们点击左箭头的的时候回到jd.com
当我们再点击右箭头的时候又回到taobao.com
类似的应用场景还有软件的撤销恢复功能