数据结构第 3 章:栈和队列之栈

栈的定义及基本运算

栈的定义

  1. 是限定仅在表尾进行插入和删除操作的线性表。表尾端称为栈顶,表头端称为栈底。在这里插入图片描述

  2. 允许插入、删除的一端称为栈顶(top),另一端称为栈底(bottom)

  3. 不含任何元素的栈称为空栈

  4. 栈的特点:后进先出(LIFO,last in first out)。

  5. 判断入栈、出栈序列是否合法

举个栗子:
¥插入题目图片¥

判断要点: 若进栈顺序为 i、j、k,则出栈顺序不能为 k、j、i。

在这里插入图片描述  

栈的基本操作

在这里插入图片描述

顺序栈

  1. 顺序栈: 用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,数组中下标为 0 的一端为栈底。
  2. 定义变量 top为指针指示栈顶元素在顺序栈中的位置,top 为整型。
    注意: top 的初始值为 -1,指向栈底,同时也可作为栈底的标志。
    在这里插入图片描述
  3. 关于 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. 栈的链接存储结构称为链栈。
  2. 链栈为限定在链表首部进行插入、删除操作的不带头结点的单链表。
  3. 链式栈无栈满问题,空间可扩充。

(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. 堆栈
    在这里插入图片描述
    在这里插入图片描述
  2. 递归思想
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

小结

(1)栈和链栈的区别:

  • 栈的操作位置(栈顶)是在表尾
  • 链栈的操作位置(栈顶)是在表首
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爪喵喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值