数据结构 No.3 栈、队列

本文深入探讨了数据结构中的栈和队列概念,包括它们的基本原理、应用场景及具体实现方式。并通过实例展示了如何利用这两种数据结构解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

       昨天晚上看到一篇文章,写的是程序猿的个人生涯,其中包括GitHub上被star了多少代表代码能力,动手能力什么,自己的技术博客代表一个人的表达能力,总结能力等,所以决定以后的一些文章要尽量自己去写,可以参考一些文档,看到比较好的文章转载就一个字也不要变。尊重原创

      堆栈(英语:stack)又称为栈或堆叠,是计算机科学中一种特殊的串列形式的抽象资料型别,其特殊之处在于只能允许在链接串列或阵列的一端(称为堆叠顶端指标,英语:top)进行加入数据(英语:push)和输出数据(英语:pop)的运算。另外栈也可以用一维数组或连结串列的形式来完成。堆叠的另外一个相对的操作方式称为伫列。由于堆叠数据结构只允许在一端进行操作,因而按照后进先出(LIFO, Last In First Out)的原理运作。

应用场景

  • 用于符号匹配      在编译器的语法检查中,一个过程就是检查各种括号是否匹配,比如 ([]) ,这就是匹配的,而 {[}] 就不匹配了。可以用堆栈来实现括号匹配。
    具体算法如下:建立一个空的堆栈。
        while( 文件没有结束 ) {
            读取一个字符。
            if 遇到一个左括号,把它入栈。
            else if 遇到右括号 then 检查堆栈,{
                if 堆栈为空 then 报告错误,终止程序(括号不匹配)。
                else if 堆栈非空 then {
                    if 栈顶不是对应的左括号 then 报错,终止程序。
                    弹出栈顶。
                }
         }
        if 栈非空 then 报错。
  • 用于计算代数式。( 也可以用二叉树来解决 )    如果我们要计算 6 + 4 * 8,要考虑到优先级的问题,这时候就可以用到堆栈了。先要把代数式构造成 6 4 8 * + 。逐个读取数据,当读到数字时, 把数字入栈,读到运算符时,弹出栈中的两个元素(因为这里的是二元运算符,所以弹出两个,如果是sin等一元运算符就弹出一个),根据读取的运算符执行运算,把结果压入栈中,然后继续读取数据,读取结束后栈顶元素就是结果。比如读取6,4,8,由于是数字,所以依次入栈,读到 "*" 时,弹出 4 和 8,相乘得到 32,把32入栈。读到 "+" 时,弹出 6 和 32 ,执行运算得到 38,压入栈中,接着读取结束,栈顶的 38 就是结果。
  • 用于函数调用     因为CPU一次只能执行一个命令,而寄存器也是公用的,
    当前函数 current() 在运行时,数据储存在寄存器中,如果要调用另外一个函数 target(),而target() 也要求使用寄存器,为了防止数据丢失并且在执行完 target()能够返回到 current() 继续执行, 这时候就要把当前函数的重要数据储存起来,压入内存中的栈中( 包括变量的值和函数地址 )。这样target()函数就可以无所顾忌的使用寄存器了。target() 函数执行结束就取栈顶的返回地址继续执行 current()。如果target()中又调用另外一个函数,相应的操作也是一样的。这种机制和括号匹配有点相似,函数调用就像遇到了一个左括号,函数返回就像遇到一个右括号。这种机制就是递归的原理。递归返回地址就是自己。(这句话可能有问题,我就是这么理解的)。栈的空间有限,如果递归没有结束条件,就会不断的压栈,然后栈溢出,程序出错。
栈的应用场景写法参照:https://www.cnblogs.com/pengyingh/articles/2388882.html

具体实现

      栈一般有两种结构实现,一种是链表,一种是数组,下面的代码演示是用java数组实现的。构造方法有两个,一种是无参的构造方法,长度为10。另一种为指定长度。top为栈的游标,始终指向栈顶元素的位置。
  • 添加数据 就是把数据添加到数组,然后top指向最后一个元素,即栈顶元素。
  • 移除数据 移除数据游标向前移动一位,逻辑上移除
  • 查看数据 直接返回对象游标位置的数据即可
  • 判断是否为空 看油标的位置是否在-1处,即不存在栈顶元素
  • 判断是否满了 看栈顶元素的位置是不是已经处于数组长度减1的位置,因为位置从0开始
public class MyStack {
	//底层实现是一个数组
	private long[] arr;
	private int top;
	
	/**
	 * 默认的构造方法
	 */
	public MyStack() {
		arr = new long[10];
		top = -1;
	}
	
	/**
	 * 带参数构造方法,参数为数组初始化大小
	 */
	public MyStack(int maxsize) {
		arr = new long[maxsize];
		top = -1;
	}
	
	/**
	 * 添加数据
	 */
	public void push(int value) {
		arr[++top] = value;
	}
	
	/**
	 * 移除数据
	 */
	public long pop() {
		return arr[top--];
	}
	
	/**
	 * 查看数据
	 */
	public long peek() {
		return arr[top];
	}
	
	/**
	 * 判断是否为空
	 */
	public boolean isEmpty() {
		return top == -1;
	}
	
	/**
	 * 判断是否满了
	 */
	public boolean isFull() {
		return top == arr.length - 1;
	}
}

队列

队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加。

应用场景

  • 当多个任务分配给打印机时,为了防止冲突,创建一个队列,把任务入队,按先入先出的原则处理任务。
  • 当多个用户要访问远程服务端的文件时,也用到队列,满足先来先服务的原则。
  • 有一个数学的分支,叫队列理论(queuing theory )。用来计算 预测用户在队中的等待时间,队的长度等等问题。答案取决于用户到达队列的频率,用户的任务的处理时间。如果比较简单的情况,可以单单用分析解决。一个简单的例子就是,一部电话,一个接线员。当一个电话打进来,如果接线员很忙,就电话放到等待线路后。接线员从队头开始应答。如果有k个接线员,问题就变的复杂了。通常难以分析解决的问题就用模拟来解决。 可以用一个队列来模拟这个问题。如果k的值很大,我们也可能需要其他数据结构来模拟问题。通过模拟,可以找到最小的 k 值,使队列满足一个合理的等待时间。
  • 银行的排队叫号,火车站单窗口的买票等等等等

具体实现

本次演示的为循环队列,与普通的队列的差别在于头结点和尾结点是相连的。具体详解一会说。构造函数两个,一个默认数组长度为10,另一个长度为自定义。element是为目前队列中存在的有效值得个数,front为头结点游标,end为尾结点游标。
  • 添加数据 队列的属性就是从队尾添加数据,即尾结点的游标向下一个位置移动一下,有效数据数量加一。但是就会有一个新的问题。如果头结点和尾结点都指向了数组的最后一个有效位置,那前面的数据应该是无效的,这时候会添加失败出现数据越界异常 java.lang.ArrayIndexOutOfBoundsException。那岂不是白白浪费了空间。这时候添加一个判断,如果队尾游标已经添加到了数组的最后一个可用空间,那么把队尾游标重新调为-1,避免出现这种情况,也就是循环队列和普通队列的区别之处,就是所谓头结点和尾结点相连的代码实现。
  • 删除数据 有了前面的交代,这里就不再说明if判断,只是把头结点游标向后移动一位,逻辑上从队首已经删除掉了,然后把有效的数量减一。
  • 查看数据 由于队列的特性,所以直接返回队首游标指向位置的数据即可
  • 判断是否为空 看有效的数量是否为0,没什么好说的
  • 判断是否满了 看有效长度是否等于数组最大的可用容量

public class MyCycleQueue {
	//底层使用数组
	private long[] arr;
	//有效数据的大小
	private int elements;
	//队头
	private int front;
	//队尾
	private int end;
	
	/**
	 * 默认构造方法
	 */
	public MyCycleQueue() {
		arr = new long[10];
		elements = 0;
		front = 0;
		end = -1;
	}
	
	/**
	 * 带参数的构造方法,参数为数组的大小
	 */
	public MyCycleQueue(int maxsize) {
		arr = new long[maxsize];
		elements = 0;
		front = 0;
		end = -1;
	}
	
	/**
	 * 添加数据,从队尾插入
	 */
	public void insert(long value) {
		if(end == arr.length - 1) {
			end = -1;
		}
		arr[++end] = value;
		elements++;
	}
	
	/**
	 * 删除数据,从队头删除
	 */
	public long remove() {
		long value = arr[front++];
		if(front == arr.length) {
			front = 0;
		}
		elements--;
		return value;
	}
	
	/**
	 * 查看数据,从队头查看
	 */
	public long peek() {
		return arr[front];
	}
	
	/**
	 * 判断是否为空
	 */
	public boolean isEmpty() {
		return elements == 0;
	}
	
	/**
	 * 判断是否满了
	 */
	public boolean isFull() {
		return elements == arr.length;
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值