栈
栈的定义及基本运算
栈的定义
-
栈是限定仅在表尾进行插入和删除操作的线性表。表尾端称为栈顶,表头端称为栈底。
-
允许插入、删除的一端称为栈顶(top),另一端称为栈底(bottom)。
-
不含任何元素的栈称为空栈。
-
栈的特点:后进先出(LIFO,last in first out)。
-
判断入栈、出栈序列是否合法
举个栗子:
判断要点: 若进栈顺序为 i、j、k,则出栈顺序不能为 k、j、i。
栈的基本操作
顺序栈
- 顺序栈: 用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,数组中下标为 0 的一端为栈底。
- 定义变量 top为指针指示栈顶元素在顺序栈中的位置,top 为整型。
注意: top 的初始值为 -1,指向栈底,同时也可作为栈底的标志。
- 关于 top 初始值的问题:
顺序栈的基本操作
注意:
- 操作的具体实现基于存储的物理结构,因此需要先确定选择哪种存储结构,然后再完善操作的具体步骤。
- 只要是用到顺序结构存储在操作时都需要用到数组。
(1)顺序栈的泛型类定义:
public class SequeneStack<T>{
T[] StackArray; // 定义成员变量 1:数组
int top; // 定义成员变量 2:栈顶指针
final int defaultSize; // 定义成员变量 3:默认容量
}
(2)构造方法:
- 有参构造方法的过程:
1> 根据传进来的参数大小(参数为数组的长度)判断异常错误
2> 初始化,即给每个成员变量赋值 - 无参构造方法的过程:
1> 构造默认大小(即 defaultSize)的空栈
2> 调用有参构造方法,将 defaultSize 传进去
public SequenceStack(int n) { // 有参构造
// if(n <= 0) {
// System.out.println("数组长度有误");
// System.exit(1); // 异常结束
// }
// 判断异常,建议使用Java的异常报错语句,注释内容为书上写的报错语句
if(n <= 0) {
throw new IllegalArgumentException("数组长度有误");
}
this.top = -1;
StackArray = (T[])new Object[n];
}
public SequenceStack() { // 无参构造
// 在一个构造方法里调用同一个类的另一个构造方法使用 this
// 这是因为 Java 的多态、方法重载
this(defaultSize);
}
(3)进栈
- 进栈两步走:
1> 判断栈空间是否已满,栈满异常处理(只报错)/ 追加栈空间(推荐这种处理方法)
2> 栈空间未满,先将 top++ 指向下一个空间,再赋值
public void push(T t) {
// 判断栈是否已满
if(top == StackArray.length-1) { // 栈满异常处理
T[] p = (T[]) new Object[StackArray.length*2]; // 追加栈空间
for(int i = 0; i < StackArray.length; i++) {
p[i] = StackArray[i]; // 将值一一转进去
}
StackArray = p; // p 只是一个临时变量用来转存值扩充栈空间的,之后还是要赋给 StackArray
}
// 如果栈未满,就元素入栈:先将 top++,再将值赋给top
StackArray[++top] = t;
}
(4)出栈
- 出栈两步走:
1> 判断栈是否为空
2> 若栈为空就返回null,若栈不为空就删除栈顶元素并返回其值
// 出栈
public T pop() {
// 判断栈是否为空,若栈为空就返回null,若栈不为空就删除栈顶元素并返回其值
if(top == -1) { // 空栈的标志:top == -1
return null;
}
T t = StackArray[top--]; // 用 t 接收并返回栈顶元素的值
// ++ 或 -- 如果放在操作数之前,操作数先进行加 1 或减 1 运算,然后将结果用于表达式的操作
// ++ 或 -- 如果放在操作数之后,操作数先参加其他的运算,然后再进行加 1 或减 1 运算
return t;
}
(5)获取栈顶元素
- 获取栈顶元素两步走:
1> 判断栈是否为空
2> 若栈为空就返回null,若栈不为空就返回栈顶元素的值
注意: 这里只需要获取栈顶元素的值!因此不需要和出栈一样删除栈顶元素!
// 获取栈顶元素
public T getPop() {
// 判断栈是否为空,若栈为空就返回null,若栈不为空就返回栈顶元素的值
if(top == -1) { // 空栈的标志:top == -1
return null;
}
return StackArray[top]; // 注意:这里不删除栈顶元素,只返回值
}
(6)求栈的长度、判断栈是否为空、清空栈、遍历栈
-
求栈的长度:
因为 top 是从 0 开始指示栈顶元素,因此 top+1 就可表示栈的长度。 -
判断栈是否为空、清空栈、遍历栈:
关键点: 栈空的标志:top = -1
// 求栈的长度
public int length() {
return top+1;
}
// 判断栈是否为空
public Boolean isEmpty() {
// 栈空的标志:top=-1,因此只需要判断top是否等于-1即可
return top==-1;
}
// 清空栈
public void clear() {
// 栈空的标志:top=-1,因此只需要将top等于-1即可
top = -1;
}
// 遍历栈
public void nextOrder() {
// 判断栈是否为空
if(top == -1) { // 空栈的标志:top == -1
System.out.println("栈为空");
}
for(int i = top; i >= 0; i--) {
System.out.println(StackArray[i]);
}
}
小结:
完整代码
class SequenceStack<T>{
T[] StackArray;
int top;
static int defaultSize = 10; // static
// 有参构造
public SequenceStack(int n) {
// if(n <= 0) {
// System.out.println("数组长度有误");
// System.exit(1);
// }
if(n <= 0) {
throw new IllegalArgumentException("数组长度有误");
}
this.top = -1;
StackArray = (T[])new Object[n];
}
// 无参构造
public SequenceStack() {
// 在一个构造方法里调用同一个类的另一个构造方法使用 this,这是因为 Java 的多态、方法重载
this(defaultSize);
}
// 进栈
public void push(T t) {
if(top == StackArray.length-1) { // 栈满异常处理
T[] p = (T[]) new Object[StackArray.length*2]; // 追加栈空间
for(int i = 0; i < StackArray.length; i++) {
p[i] = StackArray[i]; // 将值一一转进去
}
StackArray = p; // p 只是一个临时变量用来转存值扩充栈空间的,之后还是要赋给 StackArray
}
StackArray[++top] = t; // 如果栈未满,就元素入栈:先将 top++,再将值赋给top
}
// 出栈
public T pop() {
// 判断栈是否为空,若栈为空就返回null,若栈不为空就删除栈顶元素并返回其值
if(top == -1) { // 空栈的标志:top == -1
return null;
}
T t = StackArray[top--]; // 用 t 接收并返回栈顶元素的值
// ++ 或 -- 如果放在操作数之前,操作数先进行加 1 或减 1 运算,然后将结果用于表达式的操作
// ++ 或 -- 如果放在操作数之后,操作数先参加其他的运算,然后再进行加 1 或减 1 运算
return t;
}
// 获取栈顶元素
public T getPop() {
// 判断栈是否为空,若栈为空就返回null,若栈不为空就返回栈顶元素的值
if(top == -1) { // 空栈的标志:top == -1
return null;
}
return StackArray[top]; // 注意:这里不删除栈顶元素,只返回值
}
// 求栈的长度
public int length() {
return top+1;
}
// 判断栈是否为空
public Boolean isEmpty() {
// 栈空的标志:top=-1,因此只需要判断top是否等于-1即可
return top==-1;
}
// 清空栈
public void clear() {
// 栈空的标志:top=-1,因此只需要将top等于-1即可
top = -1;
}
// 遍历栈·········
public void nextOrder() {
// 判断栈是否为空
if(top == -1) { // 空栈的标志:top == -1
System.out.println("栈为空");
}
for(int i = top; i >= 0; i--) {
System.out.println(StackArray[i]);
}
}
}
链栈
- 栈的链接存储结构称为链栈。
- 链栈为限定在链表首部进行插入、删除操作的不带头结点的单链表。
- 链式栈无栈满问题,空间可扩充。
(1)链栈的结点类定义
class Node<T>{
T data; // 定义数据域
Node<T> next; // 定义指针域
public Node(T data,Node<T> next) { // 有参构造
this.data = data;
this.next = next;
}
public Node(T data) {
this(data,null);
}
public Node() { // 无参构造
this(null,null); // 调用同一个类的构造方法使用this
}
public String toString() { // 重写toString()
return this.data.toString();
}
}
(2)链栈的定义
class LinkStack<T>{
Node<T> top; // 成员变量头指针
public LinkStack() { // 初始化栈
top = null; // 创建空栈
}
}
(3)入栈、出栈
- 入栈:链栈采取的是链式存储结构,可从top开始遍历整个栈,值是从头结点开始,即将值和数据域赋给top
- 出栈两步走:
1> 判断链栈是否为空
2> 若链栈为空就返回null,若链栈不为空就删除栈顶元素并返回其值
// 入栈
public void push(T t) {
top = new Node<T>(t,top); // 创建新结点
}
// 出栈
public T pop() {
if(top == null) { // 判断栈是否为空
throw new IllegalArgumentException("栈为空");
}
T t = top.data; // 变量 t 存储当前top的值
top = top.next; // top 出栈并删除,头指针后移
return t; // 返回出栈元素的值
}
(3)获取栈顶元素
注意: 这里和出栈不同,获取栈顶元素只取值,不删除元素。
// 取栈顶元素的值
public T getTop() {
if(top == null) { // 判断栈是否为空
throw new IllegalArgumentException("栈为空");
}
return top.data; // 注意:这里只取值,不删除元素
}
(4)求栈的长度、判断栈是否为空、遍历栈、清空栈
// 求栈长
public int length() {
Node<T> p = top;
int len = 0;
while(p != null) {
len++;
p = p.next;
}
return len;
}
// 判断栈是否为空
public boolean isEmpty() {
return top==null;
}
// 遍历链栈
public void nextOrder() {
if(top == null) { // 判断栈是否为空
System.out.println("null");
}
Node<T> p = top;
while(p != null) {
System.out.println(p.data);
p = p.next;
}
}
// 清空栈
public void clear() {
top = null;
}
完整代码:
class Node<T>{
T data; // 定义数据域
Node<T> next; // 定义指针域
public Node(T data,Node<T> next) { // 有参构造
this.data = data;
this.next = next;
}
public Node(T data) {
this(data,null);
}
public Node() { // 无参构造
this(null,null); // 调用同一个类的构造方法使用this
}
public String toString() { // 重写toString()
return this.data.toString();
}
}
class LinkStack<T>{
Node<T> top; // 成员变量头指针
public LinkStack() { // 初始化栈
top = null; // 创建空栈
}
// 入栈
public void push(T t) {
top = new Node<T>(t,top); // 创建新结点
}
// 出栈
public T pop() {
if(top == null) { // 判断栈是否为空
throw new IllegalArgumentException("栈为空");
}
T t = top.data; // 变量 t 存储当前top的值
top = top.next; // top 出栈并删除,头指针后移
return t; // 返回出栈元素的值
}
// 取栈顶元素的值
public T getTop() {
if(top == null) { // 判断栈是否为空
throw new IllegalArgumentException("栈为空");
}
return top.data; // 注意:这里只取值,不删除元素
}
// 求栈长
public int length() {
Node<T> p = top;
int len = 0;
while(p != null) {
len++;
p = p.next;
}
return len;
}
// 判断栈是否为空
public boolean isEmpty() {
return top==null;
}
// 遍历链栈
public void nextOrder() {
if(top == null) { // 判断栈是否为空
System.out.println("null");
}
Node<T> p = top;
while(p != null) {
System.out.println(p.data);
p = p.next;
}
}
// 清空栈
public void clear() {
top = null;
}
}
栈的应用
- 堆栈
- 递归思想
小结
(1)栈和链栈的区别:
- 栈的操作位置(栈顶)是在表尾
- 链栈的操作位置(栈顶)是在表首