说到栈,我们浮现在脑海中的肯定是后进先出(LIFO),先进入的数据被压入栈底,最近的数据在栈顶,当需要读取数据的时候,从栈顶弹出数据。
插入一般称为进栈(PUSH),删除则称为退栈(POP)。以羽毛球筒为例,羽毛球筒就是一个栈,刚开始羽毛球筒是空的,也就是空栈,然后我们一个一个放入羽毛球,也就是一个一个push进栈,当我们需要使用羽毛球的时候,从筒里面拿,也就是pop出栈,但是第一个拿到的羽毛球是我们最后放进去的。
push图示
pop图示
下面是我用数组实现的一个栈模型,代码如下:
public class MyStack {
private final int[] arr;
private final int size;
private int top;
public MyStack(int size){
if(size<=0){
this.size = 0;
arr = null;
System.out.println("参数错误");
}else{
this.size = size;
arr = new int[this.size];
top = -1;
}
}
public void push(int data){
if(top < size-1){
arr[top+1] = data;
top++;
}else{
System.out.println("满了");
}
}
public int pop(){
if(top == 0){
System.out.println("栈中空的");
return -1;
}else{
top--;
return arr[top+1];
}
}
public int peek(){
if(top == 0){
System.out.println("栈中空的");
return -1;
}else{
return arr[top];
}
}
public boolean isEmpty(){
return top == 0;
}
public boolean isFull(){
return top == (size-1);
}
public static void main(String[] args) {
MyStack myStack = new MyStack(4);
myStack.push(1);
myStack.push(2);
myStack.push(3);
myStack.push(4);
myStack.push(5);
System.out.println(myStack.peek());
System.out.println(myStack.pop());
System.out.println(myStack.peek());
}
}
上面的代码内部使用了数组存放数据,size表示栈的最大容量,top用来指示栈顶的位置。通过构造方法规定了该栈的容量,push()方法是向栈中压入元素,指向栈顶的变量top加一,使它指向原顶端数据项上面的一个位置,并在这个位置上存储一个数据。pop()方法返回top变量指向的元素,然后将top变量减一,便移除了数据项。
但是上面的栈由于是固定大小,不支持扩容,而且里面只能放int,那么就需要进行一些功能的扩充,代码如下:
public class GrowableArrayStack {
private static final int DEFAULT_CAPACITY = 5;
private Object[] elementData;
private int top;
private int size;
private static int factor = 2;
public GrowableArrayStack(){
elementData = new Object[DEFAULT_CAPACITY];
top = -1;
size = DEFAULT_CAPACITY;
}
public GrowableArrayStack(int size) throws IllegalArgumentException{
if(size <= 0){
throw new IllegalArgumentException("容量应该大于0 : "+size);
}
elementData = new Object[size];
top = -1;
this.size = size;
}
public void push(Object obj){
isGrow(top+1);
elementData[++top] = obj;
}
public Object pop(){
Object object = peek();
remove(top);
return object;
}
public boolean isEmpty(){
return (top==-1);
}
public Object peek(){
if(isEmpty()){
throw new EmptyStackException();
}
return elementData[top];
}
public void remove(int top){
elementData[top] = null;
this.top--;
}
/*扩容*/
private void isGrow(int index){
if(index>size){
int newCapacity = 0;
if(size * factor > Integer.MAX_VALUE){
newCapacity = Integer.MAX_VALUE;
}else{
newCapacity = size*factor;
}
elementData = Arrays.copyOf(elementData,newCapacity);
}
}
}
下面我们需要测试一下,当然不是简单的测试,我们可以用我们上面的代码完成一些简单的功能。
字符串的reverse
private static void reverseString(){
String string = "abcdef";
char[] chars = string.toCharArray();
GrowableArrayStack growableArrayStack = new GrowableArrayStack(chars.length);
for(char a : chars){
growableArrayStack.push(a);
}
String reverse = "";
for (int i = 0; i < chars.length; i++) {
reverse+=growableArrayStack.pop();
}
System.out.println(reverse);
}
输出 fedcba
字符合法性
写过xml标签或者html标签的,我们都知道<必须和最近的>进行匹配,[ 也必须和最近的 ] 进行匹配。比如:<abc[123]abc>这是符号相匹配的,如果是 <abc[123>abc] 那就是不匹配的。对于 12<a[b{c}]>,我们分析在栈中的数据:遇到匹配正确的就消除
private static void testValid(){
String str = "12<ha[bb{f}]>{";
char[] chars = str.toCharArray();
GrowableArrayStack growableArrayStack = new GrowableArrayStack(6);
for(char a : chars){
switch (a){
case '<':
case '[':
case '{':
growableArrayStack.push(a);
break;
case '>':
if('<' == (char)(growableArrayStack.peek())){
growableArrayStack.pop();
}else {
growableArrayStack.push(a);
}
break;
case ']':
if('[' == (char)(growableArrayStack.peek())){
growableArrayStack.pop();
}else {
growableArrayStack.push(a);
}
break;
case '}':
if('{' == (char)(growableArrayStack.peek())){
growableArrayStack.pop();
}else {
growableArrayStack.push(a);
}
break;
default:
break;
}
}
if(growableArrayStack.isEmpty()){
System.out.println("合法");
}else{
System.out.println("非法");
}
}
输出 合法