栈
什么是栈
栈相对于数组结构做了一些限制。在使用数组结构的时候,我们可以任意的取出数组内的任何元素,也可以随意在指定位置插入元素,而栈却对元素的获取与插入做了一些限制。
栈只允许访问一个数据项:即最后插入的数据。移除这个数据项后才能访问倒数第二个插入的数据项。它是一种后进先出的数据结构。就好像打手枪,你的子弹夹,先装进弹夹的子弹,最后被打出来(左轮另说)。
代码实现
只对栈最基本的操作:出栈、入栈等操作进行了简单实现,没有对可能的异常进行处理。
public class Stack<E> {
private int size; //栈的大小
private int top; //栈顶元素下标
private E[] element; //栈容器
private int elementNum; //栈内元素
public Stack() {
this(10); //默认大小为10
}
public Stack(int size) {
this.element = (E[])new Object[size];
top = -1;
this.size = size;
}
//入栈并返回入栈元素,栈顶下标加1,栈内元素加1
public E push(E element) {
this.element[++top] = element;
elementNum++;
return element;
}
//出栈并返回栈顶元素,栈顶下标减1,栈内元素减1
public E pop() {
elementNum--;
return this.element[top--];
}
//返回栈顶元素
public E peek() {
return this.element[top];
}
//判断栈是否为空
public boolean isEmpty() {
return top == -1;
}
//返回栈内元素个数
public int size() {
return this.elementNum;
}
//toString方法
@Override
public String toString() {
if (isEmpty()) { //如果栈为空,返回 []
return "[]";
}
StringBuilder sb = new StringBuilder();
sb.append('['); //当栈内不为空时,拼接
for (int i = 0; i < elementNum; i++) {
sb.append(element[i]);
if ((i + 1) == elementNum) {
return sb.append(']').toString();
}
sb.append(',');
}
return null;
}
}
实战应用
实战一:十进制转换二进制
我们都知道,十进制转二进制是将一个数每次都除以2,一直到无法再除以2以后,倒着将每次除以2后获得的余数连接起来,就是正确的二进制数,如下图
所以,我们可以看到,最先获得的余数是最后才被取走的,这是一个很典型的栈结构的例子。每次除得的余数,就对其进行入栈操作,最后再进行多次出栈操作就可以实现十进制转二进制的功能了。
代码实现
public static String d2b(Integer number) {
Stack stack = new Stack();
String result = "";
while (number > 0) {
Object push = stack.push(number % 2);
System.out.println(push);
number = number / 2;
}
while (!stack.empty()) {
result += Integer.toString((Integer)stack.pop());
}
return result;
}
实战二:分隔符匹配
如果我们写的代码中如果多了一个“{”,后者少了一个“}”,或者括号的顺序错乱,都会报错。接下来我们就用栈来模拟这种分隔符匹配。
思路:当读取一段程序时,如果发现它是左分隔符({、[、(),将它压入栈中。当读到一个右分隔符时()、]、}),弹出栈顶元素,并且查看它是否和该右分隔符匹配。如果它们不匹配,则程序报错。如果到最后一直存在着没有被匹配的分隔符,程序也报错。
我们来看下面这个正确的字符串:
a{b(c[d]e)f}
,在栈中的变化过程:
所读字符 | 栈中内容 |
---|---|
a | 空 |
{ | { |
b | { |
( | {( |
c | {( |
[ | {([ |
d | {([ |
] | {( |
e | {( |
) | { |
f | { |
} | 空 |
代码实现
public class BrecketChecker {
private String input;
public BrecketChecker(String input) {
this.input = input;
}
//检查分隔符匹配的方法
public String check() {
if (input.isEmpty()) { //如果是空字符串直接返回
return "代码正常";
}
Stack stack = new Stack();
char[] chars = input.toCharArray();
for (int i=0;i<chars.length;i++) { //遍历数组
char aChar = chars[i];
switch (aChar) {
//如果是左符号则进入栈
case '(':
case '[':
case '{':
stack.push(aChar);
break;
//如果是右符号,进行判断
case ')':
case ']':
case '}':
if (!stack.isEmpty()) { //栈不为空,三种正确情况
char pop = (char) stack.pop();
if ((pop == '(' && aChar == ')') ||
(pop == '[' && aChar == ']') ||
(pop == '{' && aChar == '}')
) {
continue; //进行下次循环
}
return "代码有误;位置:" + i + ";代码:" + aChar; //不为三种正确情况,出错
} else { //栈如果为空,又有右符号,那么是错误的,必须先有左符号
return "代码有误;位置:" + i + ";代码:"+aChar;
}
default:
break;
}
}
if (stack.isEmpty()) { //遍历结束,如果栈为空那么是正确的;如果不为空,那么有括号没有匹配
return "代码正常";
}
return "代码有误;有括号没有关闭";
}
}