数据结构:3.2 队列及C/C++实现循环队列、链式队列基本操作

113 阅读4分钟

1. 队列

    // 3.2 队列

    // 3.2.1 队列的基本概念
    //    1. 队列的定义
    //      定义:一种操作受限的线性表,只允许在表的一端(队尾Rear)进行插入,而在表的另一端(队头Front)进行删除。
    //      操作特性:先进先出(First In First Out)
    //    2. 队列常见的基本操作
    //      InitQueue(&Q)
    //      QueueEmpty(Q)
    //      EnQueue(&Q, x)
    //      DeQueue(&Q, &x)
    //      GetHead(Q, &x)

    // 3.2.2 队列的顺序存储结构
    //    1. 队列的顺序存储
    //      顺序存储类型:
    //        #define MaxSize 50
    //        typedef struct {
    //          ElemType data[MaxSize];
    //          int front, rear;
    //        }SqQueue;
    //      初始状态(队空条件):Q.front == Q.rear == 0
    //      进队:队不满时,先送值到队尾元素,再将队尾指针加1
    //      出队:队不空时,先取队头元素值,再将队头指针加1
    //    2. 循环队列
    //      定义:将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列
    //      初始时,Q.front = Q.rear = 0;
    //      队首指针进1: Q.front = (Q.front + 1) % MaxSize
    //      队尾指针进1: Q.rear = (Q.rear + 1) % MaxSize
    //      队列长度: (Q.rear + MaxSize - Q.front) % MaxSize
    //      出队入队时:指针都按顺时针方向进1
    //      队满条件:牺牲一个单元,队头指针在队尾指针的下一位置作为队满的标志(Q.rear + 1)% MaxSize == Q.front
    //    3. 循环队列的操作
    //      InitQueue(SqQueue &Q)
    //      IsEmpty(SqQueue Q)
    //      EnQueue(SqQueue &Q, ElemType x)
    //      DeQueue(SqQueue &Q, ElemType &x)
    //    4. 为什么引入循环队列
    //      顺序队列中的溢出现象:
    //      1)下溢,队空时做出队运算产生的溢出现象
    //      2)真上溢,队满时做出队运算产生的溢出现象
    //      3)假上溢,由于入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用。当队列中实际的元素个数远远小于向量空间的规模时,
    //         也可能由于尾指针已超过向量空间的上限而不能做入队操作。
    //      于是,为了充分利用向量空间,克服“假上溢”现象,引入了循环队列

    // 3.2.3 队列的链式存储结构
    //    1. 队列的链式存储
    //      链队列:一个同时带有队头指针和队尾指针的单链表
    //      队列的链式存储类型:
    //        结点
    //        typedef struct{
    //          ElemType data;
    //          struct LinkNode *next;
    //        }LinkNode;
    //        链式队列
    //        typedef struct{
    //          LinkNode *front, *rear;
    //        }LinkQueue;     
    //    2. 链式队列的基本操作
    //      InitQueue()
    //      IsEmpty(SqQueue Q)
    //      EnQueue(SqQueue &Q, ElemType x)
    //      DeQueue(SqQueue &Q, ElemType &x)
    //    3. 链式队列适用场景
    //      1)适合数据元素变动比较大的情形,且不存在因队列满而产生溢出的问题。
    //      2)程序中需要使用多个队列时,使用链式队列就不会出现存储分配不合理和溢出问题。

    // 3.2.4 双端队列
    //    双端队列:允许两端都可以进行入队和出队操作的队列
    //    输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许插入的双端队列
    //    输入受限的双端队列:允许在一端进行插入和删除,但在另一端只允许删除的双端队列

2. 循环队列基本操作

    #include<stdio.h>

    #define MaxSize 10
    #define ElemType int

    typedef struct {
      ElemType data[MaxSize];
      int front, rear;
    }SqQueue;

    // 初始化
    void InitQueue(SqQueue &Q) {
      Q.front = Q.rear = 0;
    }

    // 判空
    bool IsEmpty(SqQueue Q) {
      return Q.rear == Q.front == 0;
    }

    // 队满
    bool IsFull(SqQueue Q) {
      return (Q.rear + 1) % MaxSize == Q.front;
    }

    // 进队
    void EnQueue(SqQueue &Q, ElemType x) {
      if(IsFull(Q)) {
        printf("队满,无法进队");
        return;
      }

      Q.data[Q.rear] = x;
      Q.rear = (Q.rear + 1) % MaxSize;
    }

    // 出队
    void DeQueue(SqQueue &Q, ElemType &x) {
      if(IsEmpty(Q)) {
        printf("队空,无法出队");
        return;
      }

      x = Q.data[Q.front];
      Q.front = (Q.front + 1) % MaxSize;
    }

    int main() {
      return 0;
    }

3. 链式队列基本操作

    #include<stdio.h>
    #include<stdlib.h>

    #define ElemType int

    typedef struct LinkNode{
      ElemType data;
      struct LinkNode *next;
    }LinkNode;

    typedef struct{
      LinkNode *front, *rear;
    }LinkQueue;

    // 初始化
    void InitQueue(LinkQueue &Q) {
      Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
      Q.front->next = NULL;
    }

    // 判空
    bool IsEmpty(LinkQueue Q) {
      return Q.front == Q.rear;
    }

    // 入队
    void EnQueue(LinkQueue &Q, ElemType x) {
      LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));
      s->data = x;
      s->next = NULL;
      Q.rear->next = s;
      Q.rear = s;
    }

    // 出队
    void DeQueue(LinkQueue &Q, ElemType &x) {
      if(IsEmpty(Q)) {
        printf("队空,无法出队");
        return;
      }
      LinkNode *p = Q.front->next;
      x = p->data;
      Q.front->next = p->next;
      // if(p == Q.rear) {
      //   int test1 = Q.front->next == NULL ? 1 : 0;
      //   int test2 = Q.rear->next == NULL ? 1 : 0;
      //   printf("%d %d %d %d \n", test1, test2, Q.front->data, Q.rear->data);
      //   Q.front = Q.rear;
      //   printf("%d %d \n", Q.front->data, Q.rear->data);
      // }
      if(Q.rear == p) {
        // int test1 = Q.front->next == NULL ? 1 : 0;
        // int test2 = Q.rear->next == NULL ? 1 : 0;
        // printf("%d %d %d %d \n", test1, test2, Q.front->data, Q.rear->data);
        Q.rear = Q.front;
        // printf("%d %d \n", Q.front->data, Q.rear->data);
      }
      free(p);
    }

    // 输出队列
    void printQueue(LinkQueue Q) {
      if (IsEmpty(Q)) {
        printf("队空");
        return;
      }
      LinkNode *p = Q.front->next;
      while(p) {
        printf("%d ", p->data);
        p = p->next;
      }
    }

    int main() {
      LinkQueue Q;
      ElemType x;

      printf("----------初始化----------\n");
      InitQueue(Q);
      printf("----------入队----------\n");
      printf("请输入入队元素值:");
      scanf("%d", &x);
      EnQueue(Q, x);
      printf("----------当前队列----------\n");
      printQueue(Q);
      printf("\n");
      printf("----------出队----------\n");
      DeQueue(Q, x);
      printf("----------当前队列----------\n");
      printQueue(Q);
      printf("\n");

      return 0;
    }