一、栈介绍
1.1 简单介绍
栈(stack) 是一个有序线性表,只能在表的一端(称为栈顶(top))执行插入和删除操作。最后插入的元素最后一个被删除,所以,栈也称为后进先出(LIFO)或先进后出(FILO)线性表。
入栈(push):表示在栈顶插入一个元素。
出栈(pop):表示从栈顶删除一个元素。
试图对一个空栈执行出栈操作称为下溢(underflow)
对一个满栈进行入栈操作称为溢出(overflow)。
通常情况下下溢和溢出均认为异常。
1.2 操作
简单起见,我们使用int类型。
void push(int data) 将data插入栈
int pop() 删除并返回最后一个插入的元素。
int top() 返回一个最后插入的元素。
int size() 返回存储在栈中的元素的个数。
boolean isEmpty() 判断栈中是否有元素
boolean isStackFull() 判断栈中是否存满元素。
1.3 栈的常见应用
- 符号匹配
- 中辍表达式转为后辍表达式
- 计算后辍表达式
- 实现函数调用(包括递归)
- 文本编辑器的撤销操作。
- 浏览器后退按钮的实现。
二、栈的实现
2.1 抽象栈
1
public abstract class Stack {
2
/**
3
* 栈顶插入元素
4
* @param data
5
*/
6
public abstract void push(int data);
7
8
/**
9
* 删除并返回栈顶元素
10
* @return
11
*/
12
public abstract int pop();
13
14
/**
15
* 返回栈顶元素
16
* @return
17
*/
18
public abstract int top();
19
20
/**
21
* 返回栈中元素个数
22
* @return
23
*/
24
public abstract int size();
25
26
/**
27
* 判断栈是否为空
28
* @return
29
*/
30
public abstract boolean isEmpty();
31
32
/**
33
* 判断栈是否满了
34
* @return
35
*/
36
public abstract boolean isStackFull();
37
}
38
我们使用三种方式实现栈,一种是简单数组,一种是动态数组,最后是单向链表
2.2 简单数组实现栈
1
import java.util.Arrays;
2
3
/**
4
* Created by Administrator on 2017/6/24 0024.
5
*/
6
public class SimpleDynamicArrayStack extends Stack {
7
private int top;
8
private int capacity;
9
private int[] array;
10
11
public SimpleDynamicArrayStack(int capacity) {
12
this.capacity = capacity;
13
array = new int[capacity];
14
}
15
16
public void push(int data) {
17
if(isStackFull()){
18
expansion();
19
}
20
array[top] = data;
21
top++;
22
}
23
24
public int pop() {
25
if(isEmpty()){
26
throw new IllegalMonitorStateException("该栈为空栈,无法探出元素");
27
}
28
top--;
29
return array[top-1];
30
}
31
32
public int top() {
33
if(isEmpty()){
34
throw new IllegalMonitorStateException("该栈为空栈,没有元素");
35
}
36
top--;
37
int temp = array[top-1];
38
//如果栈中存储的不是基本数据类型,这里注意将array[top-1]置为null,否则可能出现内存泄漏
39
return temp;
40
}
41
42
public int size() {
43
return 0;
44
}
45
46
public boolean isEmpty() {
47
return top == 0;
48
}
49
50
public boolean isStackFull() {
51
return top == capacity;
52
}
53
54
/**
55
* 数组扩容操作
56
*/
57
private void expansion(){
58
capacity = 2*capacity;
59
array = Arrays.copyOf(array,capacity);
60
}
61
}
62
性能和局限性
性能:假设n为栈中元素个数,在基于简单数组的栈的实现中,各种栈操作的算法复杂度如下:
局限性:栈的最大容量在初始化的时候指定不能改变。
2.2 基于动态数组的实现
我们采取的方式是当栈满了,就能让栈的容量扩充为原来的两倍
1
import java.util.Arrays;
2
3
/**
4
* Created by Administrator on 2017/6/24 0024.
5
*/
6
public class SimpleDynamicArrayStack extends Stack {
7
private int top;
8
private int capacity;
9
private int[] array;
10
11
public SimpleDynamicArrayStack(int capacity) {
12
this.capacity = capacity;
13
array = new int[capacity];
14
}
15
16
public void push(int data) {
17
if(isStackFull()){
18
expansion();
19
}
20
array[top] = data;
21
top++;
22
}
23
24
public int pop() {
25
if(top == 0){
26
throw new IllegalMonitorStateException("该栈为空栈,无法探出元素");
27
}
28
top--;
29
return array[top-1];
30
}
31
32
public int top() {
33
if(isEmpty()){
34
throw new IllegalMonitorStateException("该栈为空栈,没有元素");
35
}
36
top--;
37
int temp = array[top-1];
38
//如果栈中存储的不是基本数据类型,这里注意将array[top-1]置为null,否则可能出现内存泄漏
39
return temp;
40
}
41
42
public int size() {
43
return 0;
44
}
45
46
public boolean isEmpty() {
47
return top == 0;
48
}
49
50
public boolean isStackFull() {
51
return top == capacity;
52
}
53
54
/**
55
* 数组扩容操作
56
*/
57
private void expansion(){
58
capacity = 2*capacity;
59
array = Arrays.copyOf(array,capacity);
60
}
61
}
62
性能:假设n为栈中元素个数。算法复杂度如下
1
/**
2
链表节点
3
*/
4
public class Node {
5
private int data;
6
private Node next;
7
8
public Node(int data) {
9
this.data = data;
10
11
}
12
13
public int getData() {
14
return data;
15
}
16
17
public void setData(int data) {
18
this.data = data;
19
}
20
21
public Node getNext() {
22
return next;
23
}
24
25
public void setNext(Node next) {
26
this.next = next;
27
}
28
}
29
30
31
32
import cn.lx.node.Node;
33
34
/**
35
* Created by Administrator on 2017/6/24 0024.
36
*/
37
public class SimpleNodeStack extends Stack{
38
private Node headNode;
39
private int size;
40
41
public void push(int data) {
42
Node node = new Node(data);
43
if(isEmpty()){
44
headNode = node;
45
return;
46
}
47
size++;
48
node.setNext(headNode);
49
headNode = node;
50
}
51
52
53
public int pop() {
54
if(isEmpty()){
55
throw new IllegalMonitorStateException("该栈为空栈,无法探出元素");
56
}
57
int temp = headNode.getData();
58
headNode = headNode.getNext();
59
size--;
60
return temp;
61
}
62
63
64
public int top() {
65
if(isEmpty()){
66
throw new IllegalMonitorStateException("该栈为空栈,无法探出元素");
67
}
68
69
int temp = headNode.getData();
70
return temp;
71
}
72
73
74
public int size() {
75
return 0;
76
}
77
78
79
public boolean isEmpty() {
80
return headNode == null;
81
}
82
83
84
public boolean isStackFull() {
85
return false;
86
}
87
}
88
性能:假设栈中元素个数为n,算法复杂度如下:
基于数组与基于链表的比较:
基于数组实现:
各个操作都是常数时间开销
每隔一端时间倍增操作的开销较大
基于链表实现
栈规模大的增加和减少都很简洁
每个操作都是常数时间开销
每个操作都都要使用额外的空间和时间来处理指针。