当前位置:   article > 正文

队列(Queue)的基本结构_queue物理结构

queue物理结构


定义: 队列(Queue) 是一种只在表的一端进行插入,而在另一端进行删除的数据结构。

队头(front) 允许删除的一端,永远指向第一个元素前一个位置。
队尾(rear) 允许插入的一端,永远是指向队列最后一个元素
先进先出(First In First Out)的线性表,简称FIFO
空队列 当队列中没有元素


上溢和下溢
当队列满时再入队会产生空间溢出,简称“上溢”;当队列空时再出队也会产生溢出,简称“下溢”。上溢是一种出错状态,应该避免;下溢则是正常现象。
假上溢:

因为在入队和出队的操作中,头尾指针只增加不减小,致使被删除元素的空间永远无法重新利用。因此,尽管队列中实际的元素个数远远小于向量空间的规模,但也可能由于尾指针巳超出向量空间的上界而不能做入队操作。该现象称为假上溢。

队尾指针为5,队头指针为1,此时尾指针已经指向了顺序队列最后一个元素,但可以看到0,1两个
并没有被使用,此时就为假上溢。
顺序队列
定义: 队列的顺序存储结构简称顺序队列。

完整代码
基于 JS 数组实现,由于 JS 数组能够自动扩容,所以没有上溢问题。并且原生实现了队列方法。
 

  1. class ArrayQueue {
  2. constructor() {
  3. this.queue = [];
  4. }
  5. // 入队
  6. enqueue(value) {
  7. return this.queue.push(value);
  8. }
  9. // 出队
  10. dequeue() {
  11. return this.queue.shift();
  12. }
  13. // 取队头元素
  14. peek() {
  15. return this.queue[0];
  16. }
  17. // 判断队列是否为空
  18. isEmpty() {
  19. return this.queue.length === 0;
  20. }
  21. // 取队列有多少个元素
  22. size() {
  23. return this.queue.length;
  24. }
  25. // 清空队列
  26. clear() {
  27. this.queue = [];
  28. }
  29. }

基于 JS 对象实现。不存在上溢和假上溢问题。

  1. class ObjectQueue {
  2. constructor() {
  3. this.queue = {};
  4. this.end = -1;
  5. this.front = -1;
  6. }
  7. // 入队
  8. enqueue(value) {
  9. if (this.front === -1) {
  10. this.front++;
  11. }
  12. this.queue[++this.end] = value;
  13. }
  14. // 出队
  15. dequeue() {
  16. if (!this.isEmpty()) {
  17. const res = this.queue[this.front];
  18. delete this.queue[this.front++];
  19. return res;
  20. }
  21. return null;
  22. }
  23. // 取队头元素
  24. peek() {
  25. if (!this.isEmpty()) {
  26. return this.queue[this.front];
  27. }
  28. return null;
  29. }
  30. // 判断队列是否为空
  31. isEmpty() {
  32. return this.front > this.end;
  33. }
  34. // 取队列有多少个元素
  35. size() {
  36. return this.end - this.front + 1;
  37. }
  38. // 清空队列
  39. clear() {
  40. this.queue = {};
  41. this.front = -1;
  42. this.end = -1;
  43. }
  44. }

为了解决假上溢问题,引入循环队列,即把向量空间想象为一个首尾相接的圆环,在循环队列中进行出队、入队操作时,头尾指针仍要加1,朝前移动。只不过当头尾指针指向向量上界(MAXNUM-1)时,其加1操作的结果是指向向量的下界0。

但是引进的循环队列又出现了新的问题,看图:

判空和判满两种方法
即队空和队满的时都满足q->front==q->rear,我们无法利用这个条件判断队空还是队满!!!该怎么解决这个问题呢?有两种方法:

少用一个数据元素空间,以队尾指示器加 l等于队头指示器判断队满,即队满条件为:
(q-> rear+l)%MAXNUM==q->front
使用一个计数器记录队列中元素的总数(实际上是队列长度)。
第一种方法实现
 

  1. class LoopArrayQueue{
  2. constructor() {
  3. this.MAXNUM = 5;
  4. this.queue = new Array(this.MAXNUM);
  5. this.front = 0;
  6. this.rear = 0;
  7. }
  8. // 入队
  9. enqueue(value) {
  10. if ((this.rear + 1) % this.MAXNUM === this.front) {
  11. console.log('队列已满,入队失败');
  12. return;
  13. }
  14. this.rear++;
  15. this.queue[this.rear % this.MAXNUM] = value;
  16. }
  17. // 出队
  18. dequeue() {
  19. if(!this.isEmpty()) {
  20. this.front++;
  21. const res = this.queue[this.front % this.MAXNUM];
  22. this.queue[this.front % this.MAXNUM] = undefined;
  23. return res;
  24. }
  25. return undefined;
  26. }
  27. // 取队头元素
  28. peek() {
  29. return this.queue[(this.front + 1) % this.MAXNUM];
  30. }
  31. // 判断队列是否为空
  32. isEmpty() {
  33. return this.front === this.rear;
  34. }
  35. // 取队列有多少个元素
  36. size() {
  37. return this.rear - this.front;
  38. }
  39. // 清空队列
  40. clear() {
  41. this.queue = new Array(this.MAXNUM);
  42. this.front = 0;
  43. this.rear = 0;
  44. }
  45. }

第二种方法实现

  1. class LoopArrayQueue{
  2. constructor() {
  3. this.MAXNUM = 5;
  4. this.queue = new Array(this.MAXNUM);
  5. this.front = -1;
  6. this.rear = -1;
  7. this.count = 0;
  8. }
  9. // 入队
  10. enqueue(value) {
  11. if (this.count === this.MAXNUM) {
  12. console.log('队列已满,入队失败');
  13. return;
  14. }
  15. this.count++;
  16. this.rear++;
  17. this.queue[this.rear % this.MAXNUM] = value;
  18. }
  19. // 出队
  20. dequeue() {
  21. if (this.count > 0){
  22. this.front++;
  23. this.count--;
  24. const res = this.queue[this.front % this.MAXNUM];
  25. this.queue[this.front % this.MAXNUM] = undefined;
  26. return res;
  27. }
  28. return undefined;
  29. }
  30. // 取队头元素
  31. peek() {
  32. return this.queue[(this.front + 1) % this.MAXNUM];
  33. }
  34. // 判断队列是否为空
  35. isEmpty() {
  36. return this.count === 0;
  37. }
  38. // 取队列有多少个元素
  39. size() {
  40. return this.count;
  41. }
  42. // 清空队列
  43. clear() {
  44. this.count = 0;
  45. this.front = -1;
  46. this.rear = -1;
  47. this.queue = new Array(this.MAXNUM);
  48. }
  49. }

链队列
定义: 队列的链式存储结构简称为链队列,它是限制仅在表头删除和表尾插入的单链表。 显然仅有单链表的头指针不便于在表尾做插入操作,为此再增加一个尾指针,指向链表的最后一个结点。于是,一个链队列由头指针和尾指针唯一确定。


注意:虽然 JS 并没有指针的概念,但是对于对象这样的类型,对象的名字本质上就相当于指针。

虽然 JS 中数组实现了在队列的方法,但是它本质上还是数组操作,所以若从数组头部删除一个元素,那么在 JS 引擎内部还是需要移动后边的数组元素,很耗费性能。但是链式存储结构删除和添加元素复杂度为 O1。

节点存储结构:
 

  1. class Node {
  2. constructor(value) {
  3. this.value = value;
  4. this.next = null;
  5. }
  6. }

判断队空
注意:有 JS 本身并无指针概念,所以再实现“链队”时必须要定义一个变量 count 记录队列中元素个数,由 count == 0 作为判空条件。

而其他语言(例如 c 语言)链队队空条件是pointer->front->next == NULL && pointer->rear->next == NULL而不是pointer->front == pointer->rear这是因为当队空的时候pointer->front和pointer->rear的值不一样。

原因: 如上图所示,当创建空链队时,队列头指针、尾指针都指向上图中黑色节点(这个黑色节点相当于顺序队列的-1,是一个初始节点),当链队列添加元素后尾指针会改变它指向的节点,此时pointer->rear的值是它指向的尾结点的地址。而当队列删除元素时,改变的是黑色节点的 next 指针(这样做是为了使头指针满足它的特点,具体看上边队列特点),这样pointer->front的值永远是黑色节点的地址。所以pointer->front和pointer->rear的值不一样,不能作为判断队空的条件。

出队列:

当使用其它语言(例如 c 语言)时此功能需要注意的就是当队列只剩一个元素时,即pointer->front->next == pointer->rear,而你要删除此元素,就需要先改变尾指针指向的节点,再删除,释放最后一个元素节点的内存。因为如果你不改变的话,则尾指针是指向最后一个元素节点的,而你删除释放最后一个元素节点的内存后,此时pointer->rear->next是一个野指针(野指针的危害就不用我说了吧!!!)。
 

  1. class LinkQueue {
  2. constructor() {
  3. const node = new Node();
  4. this.count = 0;
  5. this.head = node;
  6. this.rear = node;
  7. }
  8. // 入队
  9. enqueue(value) {
  10. const node = new Node(value);
  11. this.rear.next = node;
  12. this.rear = node;
  13. this.count++;
  14. }
  15. // 出队
  16. dequeue() {
  17. if (!this.isEmpty()) {
  18. const res = this.head.next.value;
  19. this.head.next = this.head.next.next;
  20. this.count--;
  21. if (this.isEmpty()) {
  22. this.rear = this.head;
  23. }
  24. return res;
  25. }
  26. return null;
  27. }
  28. // 取队头元素
  29. peek() {
  30. if (!this.isEmpty()) {
  31. return this.head.value;
  32. }
  33. return null;
  34. }
  35. // 判断是否为空
  36. isEmpty() {
  37. return this.count === 0;
  38. }
  39. // 返回队列元素个数
  40. size() {
  41. return this.count;
  42. }
  43. // 清空队列
  44. clear() {
  45. const node = new Node();
  46. this.count = 0;
  47. this.head = node;
  48. this.rear = node;
  49. }
  50. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/918140
推荐阅读
相关标签
  

闽ICP备14008679号