本文内容,参考自《大话数据结构》(程杰著) ,一部分自己修改,如:把C语言换成了Java语言。写作目的,意在加强记忆。
本文写作工具,使用 Typora。
你们在用电脑时有没有经历过,机器有时会处于疑似死机的状态,点击什么都没用,双击任何快捷方式都不动弹。就当你失去耐心,打算重启时。突然它像酒醒了一样,把你刚才的所有操作都按顺序执行了一次。
操作系统,就是应用了一种数据结构来实现刚才提到的先进先出的排队功能,这就是队列。
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出 (Frist In Frist Out) 的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
循环队列
线性表有顺序存储和链式存储,栈是线性表,所以有这两种存储方式。同样,队列作为一种特殊的线性表,也同样存在这两种存储方式。
我们假设一个队列有n个元素,则顺序存储的队列需建立一个大于n的数组,并把队列的所有元素存储在数组的前n个单元,数组下标为0的一端即为队头。所谓的入队列操作,其实就是在队尾追加一个元素,不需要移动任何元素,因此时间复杂度为O(1)。
与栈不同的是,队列元素的出队列是在队头,即下标为0的位置,那就意味着,队列中的所有元素都得向前移动,以保证队列的队头,也就是下标为0的位置不为空,因此时间复杂度为O(n)。
为了解决这问题,我们需要循环队列。
循环队列是指头尾相接的顺序存储结构。
循环队列,有几个重要概念:
front:指向队头元素位置
rear:指向队尾元素的下一个位置
QueueSize:队列最大长度
空队列:当 front == rear 时,为空
队列满:(rear+1) % QueueSize = front
队列长度:(rear - front + QueueSize) % QueueSize
循环队列代码
public class Queue { int MAXSIZE; int front; int rear; int[] data; public Queue() { MAXSIZE = 5; front = 0; rear = 0; data = new int[MAXSIZE]; } //队列长度 public int length() { return (rear-front+MAXSIZE) % MAXSIZE; } //插入元素 public boolean insert(int e) { //判断队列是否已经满 if((rear+1)%MAXSIZE == front) { return false; } data[rear] = e;//将元素e赋值给队尾 rear = (rear+1) % MAXSIZE;//rear向后移动一个位置,若到最后则转到数组头部 return true; } //删除元素 public boolean delete() { //判断队列是否为空 if(front == rear) { return false; } front = (front+1) % MAXSIZE;//front向后移动一个位置,若到最后则转到数组头部 return true; } }复制代码
队列的链式存储结构及实现
**队列的链式结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。**为了操作上的方便,我们将队头指向链队列的头结点,而队尾指向终端结点。空队列时,front 和 rear 都指向头结点。
链队列代码
public class QueueNode { int data; QueueNode next; }public class LinkedQueue { QueueNode front; QueueNode rear; //插入元素 public void insert(int e) { QueueNode n = new QueueNode(); n.data = e; n.next = null; rear.next = n; rear = n; } //删除元素 public boolean delete() { if(front == rear) { return false; } QueueNode n = front.next; front.next = n.next; if(rear == n) { rear = front; } n = null; return true; } }复制代码
对于循环队列与链队列的比较,可以从两方面来考虑,从时间上,其实它们的基本操作都是常数时间,即都为O(1),不过循环队列是事先申请好空间,使用期间不释放,而对于链队列,每次申请和释放结点也会存在一些时间开销,如果入队出队频繁,则两者还是有差异的。对于空间上来说,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链队不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链队列更加灵活。
总的来说,在可以确定队列长度最大值的情况下,建议用循环队列,如果你无法估计队列的长度时,则用链队列。