[例子和习题出自数据结构(严蔚敏版), 本人使用java进行实现. 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正. ]
栈的实现
栈是一种先进后出的数据结构, 首先定义了栈需要实现的接口:
public interface MyStack{/*** 判断栈是否为空*/
booleanisEmpty();/*** 清空栈*/
voidclear();/*** 栈的长度*/
intlength();/*** 数据入栈*/
booleanpush(T data);/*** 数据出栈*/T pop();
}
栈的数组实现, 底层使用数组:
public class MyArrayStack implements MyStack{private Object[] objs = new Object[16];private int size = 0;
@Overridepublic booleanisEmpty() {return size == 0;
}
@Overridepublic voidclear() {//将数组中的数据置为null, 方便GC进行回收
for (int i = 0; i < size; i++) {
objs[size]= null;
}
size= 0;
}
@Overridepublic intlength() {returnsize;
}
@Overridepublic booleanpush(T data) {//判断是否需要进行数组扩容
if (size >=objs.length) {
resize();
}
objs[size++] =data;return true;
}/*** 数组扩容*/
private voidresize() {
Object[] temp= new Object[objs.length * 3 / 2 + 1];for (int i = 0; i < size; i++) {
temp[i]=objs[i];
objs[i]= null;
}
objs=temp;
}
@SuppressWarnings("unchecked")
@OverridepublicT pop() {if (size == 0) {return null;
}return (T) objs[--size];
}
@OverridepublicString toString() {
StringBuilder sb= newStringBuilder();
sb.append("MyArrayStack: [");for (int i = 0; i < size; i++) {
sb.append(objs[i].toString());if (i != size - 1) {
sb.append(", ");
}
}
sb.append("]");returnsb.toString();
}
}
栈的链表实现, 底层使用链表:
public class MyLinkedStack implements MyStack{/*** 栈顶指针*/
privateNode top;/*** 栈的长度*/
private intsize;publicMyLinkedStack() {
top= null;
size= 0;
}
@Overridepublic booleanisEmpty() {return size == 0;
}
@Overridepublic voidclear() {
top= null;
size= 0;
}
@Overridepublic intlength() {returnsize;
}
@Overridepublic booleanpush(T data) {
Node node= newNode();
node.data=data;
node.pre=top;//改变栈顶指针
top =node;
size++;return true;
}
@OverridepublicT pop() {if (top != null) {
Node node=top;//改变栈顶指针
top =top.pre;
size--;returnnode.data;
}return null;
}/*** 将数据封装成结点*/
private final classNode {privateNode pre;privateT data;
}
}
两种实现的比较, 主要比较数据入栈和出栈的速度:
@Testpublic voidtestSpeed() {
MyStack stack = new MyArrayStack();int num = 10000000;long start =System.currentTimeMillis();for (int i = 0; i < num; i++) {
stack.push(new Person("xing", 25));
}long temp =System.currentTimeMillis();
System.out.println("push time: " + (temp -start));while (stack.pop() != null)
;
System.out.println("pop time: " + (System.currentTimeMillis() -temp));
}
MyArrayStack中入栈和出栈10,000,000条数据的时间:
push time: 936
pop time: 47
将MyArrayStack改为MyLinkedStack后入栈和出栈的时间:
push time: 936
pop time: 126
可见两者的入栈速度差不多, 出栈速度MyArrayStack则有明显的优势.
为什么测试结果是这样的? 可能有些朋友的想法是数组实现的栈应该具有更快的遍历速度, 但增删速度应该比不上链表实现的栈才对. 但是栈中数据的增删具有特殊性: 只在栈顶入栈和出栈. 也就是说数组实现的栈在增加和删除元素时并不需要移动大量的元素, 只是在数组扩容时需要进行复制. 而链表实现的栈入栈和出栈时都需要将数据包装成Node或者从Node中取出数据, 还需要维护栈顶指针和前驱指针.
栈的应用举例
1. 将10进制正整数num转换为n进制
private String conversion(int num, intn) {
MyStack myStack = new MyArrayStack();
Integer result=num;while (true) {//将余数入栈
myStack.push(result %n);
result= result /n;if (result == 0) {break;
}
}
StringBuilder sb= newStringBuilder();//按出栈的顺序倒序排列即可
while ((result = myStack.pop()) != null) {
sb.append(result);
}returnsb.toString();
}
2. 检验符号是否匹配. '['和']', '('和')'成对出现时字符串合法. 例如"[][]()", "[[([]([])()[])]]"是合法的; "([(])", "[())"是不合法的.
遍历字符串的每一个char, 将char与栈顶元素比较. 如果char和栈顶元素配对, 则char不入栈, 否则将char入栈. 当遍历完成时栈为空说明字符串是合法的.
public booleanisMatch(String str) {
MyStack myStack = new MyArrayStack();char[] arr =str.toCharArray();for (charc : arr) {
Character temp=myStack.pop();//栈为空时只将c入栈
if (temp == null) {
myStack.push(c);
}//配对时c不入栈
else if (temp == '[' && c == ']') {
}//配对时c不入栈
else if (temp == '(' && c == ')') {
}//不配对时c入栈
else{
myStack.push(temp);
myStack.push(c);
}
}returnmyStack.isEmpty();
}
3. 行编辑: 输入行中字符'#'表示退格, '@'表示之前的输入全都无效.
使用栈保存输入的字符, 如果遇到'#'就将栈顶出栈, 如果遇到@就清空栈. 输入完成时将栈中所有字符出栈后反转就是输入的结果:
privateString lineEdit(String input) {
MyStack myStack = new MyArrayStack();char[] arr =input.toCharArray();for (charc : arr) {if (c == '#') {
myStack.pop();
}else if (c == '@') {
myStack.clear();
}else{
myStack.push(c);
}
}
StringBuilder sb= newStringBuilder();
Character temp= null;while ((temp = myStack.pop()) != null) {
sb.append(temp);
}//反转字符串
sb.reverse();returnsb.toString();
}