总目录

文章目录

  • 1.说明
  • 2.基本操作
  • 2.1 结构体构造
  • 2.2 初始化
  • 2.3 判空
  • 2.4 入队
  • 2.5 出队
  • 2.6 获取队头元素的值
  • 2.7 输出队列中所有元素
  • 3.完整代码
  • 4.运行结果


1.说明

此处选择了略过一般队列而直接实现循环队列。一般队列如果以队尾指针rear==MaxSize作为队满条件,可能会产生一个问题:“假溢出”。队列是一种操作受限的线性表,入队是在队尾入队,出队是在队头出队,在不出队的情况下,如果出现rear==Maxsize,即表示已经队满,但是如果在此时进行出队,由于出队是在队头操作,即使队尾满了,进行出队操作之后,队头前会空置出大量的空间,直至最后一个元素,此时队头指针、队尾指针都指向一个位置,这也是判空的条件,队尾指针等于MaxSize,表示队已经满了。但事实上,队列还有一个元素未出队,且线性表中还有大量的空间未曾使用,此种溢出并不是真正的溢出,而是“假溢出”。

对于此种情况,可使用循环队列来解决,当队尾指针指向线性表的最后时,不会令rear==MaxSize,可以通过取模运算,使rear重新指向队头,当然,此前需要进行判定。此种循环队列一般有以下三种思路:

  • 第一种就是将队满条件设为(rear+1)%MaxSize==front,此种方法的优点是rear==front此条件仍然是表示队空的条件,且不需要额外的存储空间;缺点就是会浪费一个单元的存储空间,因为rear是指向下一个应该插入的位置,队满时其实rear指向的存储单元中并没有数据。
  • 第二种方法就是额外增加一个数据成员size,用于记录队中元素个数,这样即使是rear==front也能够轻易判断,但会占据额外的空间。
  • 第三种方法同样是增加一个数据成员tag,类似于一个标签,可以设置其等于0时rear==front表示队空,其等于1时rear==front表示队满。

此处实现的是第一种,默认队头指针front指向第一个元素,队尾指针rear指向下一个应该插入的位置。

2.基本操作

2.1 结构体构造

typedef struct {
    int data[MaxSize];  // 用静态数组存放队列元素
    // 队头指针与队尾指针
    // 队头指针指向队头元素,队尾指针指向队尾元素的后一个位置
    // 即队尾指针应指向下一个应该插入的位置
    int front, rear;
} SeqQueue;

2.2 初始化

// 初始化
void InitQueue(SeqQueue &Q) {
    // 初始时,队头队尾指针指向0
    // 队头指针指向第一个,队尾指针指向下一个应该插入的位置,也即是0
    Q.rear = Q.front = 0;
}

2.3 判空

// 判断队列是否为空
bool QueueEmpty(SeqQueue Q) {
    // 队空条件
    // 即队头指针和队尾指针都指向第一个位置
    if(Q.rear == Q.front) {
        return true;
    } else {
        return false;
    }
}

2.4 入队

// 入队操作
// 此循环队列队列元素计算方法:(rear + MaxSize - front) % MaxSize
bool EnQueue(SeqQueue &Q, int x) {
    // 判断队列是否已经满了
    // 注意:即使rear==MaxSize也不一定能代表队列已经满了
    // 因为出队是从队头出队,可能队头有元素出队,导致线性表前部分空置下来
    // 虽然以下操作可能会导致浪费一个存储单元,但是如果Q.rear==Q.front,就表示队列空置,难以判定
    // 所以不能让Q.rear==Q.front
    if((Q.rear + 1) % MaxSize == Q.front) {
        return false;
    }
    // 由于队尾指针指向下一个要插入的位置
    // 所以直接把要插入的数据插入队尾指针位置即可,即队尾
    Q.data[Q.rear] = x;
    // 队尾指针后移
    // 此处使用了取模运算,将无限的整数域映射到了有限的整数集合上,即{0, 1, 2, ..., MaxSize - 1}
    // 此运算将存储空间从逻辑上变为了环状
    // 即在rear指向队尾后,即rear == MaxSize时,会取模使其再次指向0
    Q.rear = (Q.rear + 1) % MaxSize;
    return true;
}

2.5 出队

// 出队操作
// 由于队列的操作受限性,所以删除只能在队头进行
bool DeQueue(SeqQueue &Q, int &x) {
    // 判断队列是否为空
    if(Q.rear == Q.front) {
        return false;
    }
    // 将队头元素的值赋给x
    x = Q.data[Q.front];
    // 队头元素后移
    // 类似于之前队尾元素后移,形成了环状循环
    Q.front = (Q.front + 1) % MaxSize;
    return true;
}

2.6 获取队头元素的值

// 获取队头元素的值
// 类似于出队操作,不过队头指针位置不变
bool GetHead(SeqQueue Q, int &x) {
    if(Q.rear == Q.front) {
        return false;
    }
    x = Q.data[Q.front];
    return true;
}

2.7 输出队列中所有元素

// 输出队列中所有元素
// 由于队列只能在队头出队,所以只能将所有元素出队输出
void PrintQueue(SeqQueue Q) {
    if(QueueEmpty(Q)) {
        printf("the queue is empty!\n");
    }
    while(!QueueEmpty(Q)) {
        int x;
        DeQueue(Q, x);
        printf("dequeue:%d\n", x);
    }
}

3.完整代码

#include<stdio.h>

#define MaxSize 10  // 定义队列中元素最大个数

typedef struct {
    int data[MaxSize];  // 用静态数组存放队列元素
    // 队头指针与队尾指针
    // 队头指针指向队头元素,队尾指针指向队尾元素的后一个位置
    // 即队尾指针应指向下一个应该插入的位置
    int front, rear;
} SeqQueue;

// 初始化
void InitQueue(SeqQueue &Q) {
    // 初始时,队头队尾指针指向0
    // 队头指针指向第一个,队尾指针指向下一个应该插入的位置,也即是0
    Q.rear = Q.front = 0;
}

// 判断队列是否为空
bool QueueEmpty(SeqQueue Q) {
    // 队空条件
    // 即队头指针和队尾指针都指向第一个位置
    if(Q.rear == Q.front) {
        return true;
    } else {
        return false;
    }
}

// 入队操作
// 此循环队列队列元素计算方法:(rear + MaxSize - front) % MaxSize
bool EnQueue(SeqQueue &Q, int x) {
    // 判断队列是否已经满了
    // 注意:即使rear==MaxSize也不一定能代表队列已经满了
    // 因为出队是从队头出队,可能队头有元素出队,导致线性表前部分空置下来
    // 虽然以下操作可能会导致浪费一个存储单元,但是如果Q.rear==Q.front,就表示队列空置,难以判定
    // 所以不能让Q.rear==Q.front
    if((Q.rear + 1) % MaxSize == Q.front) {
        return false;
    }
    // 由于队尾指针指向下一个要插入的位置
    // 所以直接把要插入的数据插入队尾指针位置即可,即队尾
    Q.data[Q.rear] = x;
    // 队尾指针后移
    // 此处使用了取模运算,将无限的整数域映射到了有限的整数集合上,即{0, 1, 2, ..., MaxSize - 1}
    // 此运算将存储空间从逻辑上变为了环状
    // 即在rear指向队尾后,即rear == MaxSize时,会取模使其再次指向0
    Q.rear = (Q.rear + 1) % MaxSize;
    return true;
}

// 出队操作
// 由于队列的操作受限性,所以删除只能在队头进行
bool DeQueue(SeqQueue &Q, int &x) {
    // 判断队列是否为空
    if(Q.rear == Q.front) {
        return false;
    }
    // 将队头元素的值赋给x
    x = Q.data[Q.front];
    // 队头元素后移
    // 类似于之前队尾元素后移,形成了环状循环
    Q.front = (Q.front + 1) % MaxSize;
    return true;
}

// 获取队头元素的值
// 类似于出队操作,不过队头指针位置不变
bool GetHead(SeqQueue Q, int &x) {
    if(Q.rear == Q.front) {
        return false;
    }
    x = Q.data[Q.front];
    return true;
}

// 输出队列中所有元素
// 由于队列只能在队头出队,所以只能将所有元素出队输出
void PrintQueue(SeqQueue Q) {
    if(QueueEmpty(Q)) {
        printf("the queue is empty!\n");
    }
    while(!QueueEmpty(Q)) {
        int x;
        DeQueue(Q, x);
        printf("dequeue:%d\n", x);
    }
}

int main() {
    SeqQueue Q;
    InitQueue(Q);
    PrintQueue(Q);
    int x;
    EnQueue(Q, 3);
    GetHead(Q, x);
    printf("enqueue:%d\n", x);
    x = 5;
    EnQueue(Q, x);
    printf("enqueue:%d\n", x);
    x = 7;
    EnQueue(Q, x);
    printf("enqueue:%d\n", x);
    PrintQueue(Q);
}

4.运行结果

如何输出队列中的元素java 输出队列中的所有元素_算法