栈(stack)是什么???——》是一种线性存储结构,也是存储数据的简单数据结构(简称为“数据的存储结构”);
那有什么特色之处???——》
特点:
- 栈中数据是按照"后进先出(LIFO, Last In First Out)或先进先出(FILO)"方式进出栈的;
- 向栈中添加/删除数据时,只能从栈顶进行操作;
那实现栈的定义需要什么操作???——》栈通常包括的三种操作:push、peek、pop。
push ——》向栈中添加元素(入栈);
peek——》返回栈顶元素;
pop ——》返回并删除栈顶元素的操作(出栈);
当然栈的基本操作不止这么少!!!
——》还有:top(栈顶)、size(元素个数)、isEmpty(判断是否为空)、isStackFull(是否存满元素);
要是不符合了栈,那会有什么异常???——》
溢出:试图对一个满栈执行入栈操作;
下溢:试图对一个空栈执行出栈操作;
如何实现栈的操作的图文解释???——》
1、栈的示意图
栈中的数据依次是 30 --> 20 --> 10
2、 出栈
出栈前:栈顶元素是30。此时,栈中的元素依次是 30 --> 20 --> 10
出栈后:30出栈之后,栈顶元素变成20。此时,栈中的元素依次是 20 --> 10
3、入栈
入栈前:栈顶元素是20。此时,栈中的元素依次是 20 --> 10
入栈后:40入栈之后,栈顶元素变成40。此时,栈中的元素依次是 40 --> 20 --> 10
一般考试都是从栈的入栈顺序来考的;入栈顺序和出栈顺序的规律也就是其特点:后进先出或先进先出;
既然我们讲解了栈是什么,栈的特点以及栈的具有哪些操作等;那么,现在我们就来实现栈吧!
栈的实现
和C++一样,JDK包中也提供了"栈"的实现,它就是集合框架中的Stack类;
关于Stack类的原理,这里有详细的解释:"Java 集合系列07之 Stack详细介绍(源码解析)和使用示例"中。这里给出2种实现方式:
一、数组实现的栈,能存储任意类型的数据;
二、Java的 Collection集合 中自带的"栈"(stack)的示例;
1、数组实现的栈,能存储任意类型的数据方式的代码实现
/**
* Java : 数组实现的栈,能存储任意类型的数据
*
* @author skywang
* @date 2013/11/07
*/
import java.lang.reflect.Array;
public class GeneralArrayStack<T> {
private static final int DEFAULT_SIZE = 12;
private T[] mArray;
private int count;
public GeneralArrayStack(Class<T> type) {
this(type, DEFAULT_SIZE);
}
public GeneralArrayStack(Class<T> type, int size) {
// 不能直接使用mArray = new T[DEFAULT_SIZE];
mArray = (T[]) Array.newInstance(type, size);
count = 0;
}
// 将val添加到栈中
public void push(T val) {
mArray[count++] = val;
}
// 返回“栈顶元素值”
public T peek() {
return mArray[count-1];
}
// 返回“栈顶元素值”,并删除“栈顶元素”
public T pop() {
T ret = mArray[count-1];
count--;
return ret;
}
// 返回“栈”的大小
public int size() {
return count;
}
// 返回“栈”是否为空
public boolean isEmpty() {
return size()==0;
}
// 打印“栈”
public void PrintArrayStack() {
if (isEmpty()) {
System.out.printf("stack is Empty\n");
}
System.out.printf("stack size()=%d\n", size());
int i=size()-1;
while (i>=0) {
System.out.println(mArray[i]);
i--;
}
}
public static void main(String[] args) {
String tmp;
GeneralArrayStack<String> astack = new GeneralArrayStack<String>(String.class);
// 将10, 20, 30 依次推入栈中
astack.push("10");
astack.push("20");
astack.push("30");
// 将“栈顶元素”赋值给tmp,并删除“栈顶元素”
tmp = astack.pop();
System.out.println("tmp="+tmp);
// 只将“栈顶”赋值给tmp,不删除该元素.
tmp = astack.peek();
System.out.println("tmp="+tmp);
astack.push("40");
astack.PrintArrayStack(); // 打印栈
}
}
测试代码
运行结果:
1 tmp=30
2 tmp=20
3 stack size()=3
4 40
5 20
6 10
结果说明:GeneralArrayStack是通过数组实现的栈,而且GeneralArrayStack中使用到了泛型。
2、Java的 Collection集合 中自带的"栈"(stack)的代码实现
import java.util.Stack;
/**
* Java : java集合包中的Stack的演示程序
*
* @author skywang
* @date 2013/11/07
*/
public class StackTest {
public static void main(String[] args) {
int tmp=0;
Stack<Integer> astack = new Stack<Integer>();
// 将10, 20, 30 依次推入栈中
astack.push(10);
astack.push(20);
astack.push(30);
// 将“栈顶元素”赋值给tmp,并删除“栈顶元素”
tmp = astack.pop();
//System.out.printf("tmp=%d\n", tmp);
// 只将“栈顶”赋值给tmp,不删除该元素.
tmp = (int)astack.peek();
//System.out.printf("tmp=%d\n", tmp);
astack.push(40);
while(!astack.empty()) {
tmp = (int)astack.pop();
System.out.printf("tmp=%d\n", tmp);
}
}
}
测试结果
tmp=40
tmp=20
tmp=10
既然我们对栈的代码实现了;那么,接下来就是对栈的算法分析了;
性能和局限性
1、简单数组
2、动态数组
动态数组的实现方式有两种:
1、复制原数组中所有元素到新数组中,在新数组末端添加新元素;新数组也就比原数组多一个空间;
其执行n次push操作后,总时间开销T(n)(复制操作的数量)为1+2+......+n≈O(n^2);
2、重复倍增:也就是新建一个比原数组空间大一倍的数组,然后复制原数组的元素;
执行n次push操作的时间开销为n;是不是惊到啦!怎么回事???竟然比上一种实现方式减少了这么多时间开销;
其计算过程:
T(n)为O(n),一次push操作的平摊时间也就是O(1);
性能
3、链表实现
栈的各种方法比较
1、递增策略和倍增策略比较
2、基于数组实现和基于链表实现的比较
基于数组实现的栈:
基于链表实现的栈:
通过上面的总结,相信大家已经对于栈这类算法的实现,以及思路、特点、题型有了一定的了解;
那么,接下来我们就要了解该算法用于何处了;
应用
直接应用
1、符号匹配、中缀表达式转换为后缀表达式、计算后缀表达式、实现函数调用(包括递归)、求范围误差(极差)
2、网页浏览器中已访问页面的历史记录(后退按钮)、文本编辑器中的撤销序列
3、HTML和XML文件中的标签匹配
间接应用
1、作为一个算法的辅助数据结构(例如,树遍历算法);
2、其他数据结构的组件(模拟队列)
也就到这里了,下次我会把这些应用等实践的问题一一展开!!!有什么不对请多多指教;