数据结构实验4.1:链队列的基本操作


一,问题描述

编程实现有关链队列的下列基本操作。
(1)初始化队列:创建一个空的链队列,为后续操作做好准备,确保队列的头指针和尾指针都指向合适的初始状态(通常为空)。
(2)进队操作:将新的元素添加到链队列的尾部,更新尾指针以指向新的队尾元素,保证队列的逻辑结构正确。
(3)出队操作:从链队列的头部移除并返回一个元素,同时更新头指针,若队列为空则需要进行适当的处理(如返回错误信息)。
(4)调用进队函数建立一个队列:通过多次调用进队函数,将多个元素依次添加到队列中,构建出一个完整的链队列。
(5)输出队列中的所有元素:遍历链队列,按顺序输出队列中存储的所有元素,以便直观地查看队列的内容。

二,基本要求

(1)设计链队列的存储结构:使用结构体来定义链队列的节点,节点包含数据域和指向下一个节点的指针域。同时,定义一个结构体来表示整个链队列,包含头指针和尾指针。
(2)设计基于链队列的几种基本操作的算法:分别为初始化队列、进队、出队、建立队列和输出队列元素设计详细的算法步骤,确保操作的正确性和高效性。
(3)在参考程序中的下划线处填写适当的语句,完成参考程序:仔细分析参考程序的逻辑,根据设计的存储结构和算法,在相应位置补充缺失的代码,使程序完整且能正确运行。
(4)设计测试用例,上机调试、测试参考程序,记录测试结果,对结果进行分析:设计多种测试用例,包括空队列的操作、正常队列的进队和出队、边界情况(如队列满、队列空时的非法操作)等。上机运行程序,记录每个测试用例的输入和输出,分析程序是否按照预期工作,是否存在逻辑错误或异常情况。
(5)要求每完成一个步骤就必须及时输出队列中元素以便观察操作结果:在初始化队列、进队、出队、建立队列等操作完成后,立即调用输出队列元素的函数,显示当前队列的内容,方便直观地了解操作对队列的影响。

三,算法分析

链队列的存储结构设计

使用两个结构体来实现链队列:

  1. 节点结构体
typedef struct Node {
    
    
    int data;  // 数据域,存储节点的值
    struct Node *next;  // 指针域,指向下一个节点
} Node;
  1. 链队列结构体
typedef struct Queue {
    
    
    Node *front;  // 头指针,指向队列的头部
    Node *rear;  // 尾指针,指向队列的尾部
} Queue;

基本操作的算法分析

  1. 初始化队列
    • 时间复杂度:将头指针和尾指针都设置为 NULL,时间复杂度为 O ( 1 ) O(1) O(1)
    • 空间复杂度:只需要存储头指针和尾指针,空间复杂度为 O ( 1 ) O(1) O(1)
void initQueue(Queue *q) {
    
    
    q->front = q->rear = NULL;
}
  1. 进队操作
    • 时间复杂度:创建一个新节点并将其添加到队列尾部,更新尾指针,时间复杂度为 O ( 1 ) O(1) O(1)
    • 空间复杂度:需要为新节点分配内存,空间复杂度为 O ( 1 ) O(1) O(1)
void enqueue(Queue *q, int value) {
    
    
    Node *newNode = (Node *)malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = NULL;
    if (q->rear == NULL) {
    
    
        q->front = q->rear = newNode;
    } else {
    
    
        q->rear->next = newNode;
        q->rear = newNode;
    }
}
  1. 出队操作
    • 时间复杂度:从队列头部移除一个节点,更新头指针,若队列为空则直接返回,时间复杂度为 O ( 1 ) O(1) O(1)
    • 空间复杂度:释放被移除节点的内存,空间复杂度为 O ( 1 ) O(1) O(1)
int dequeue(Queue *q) {
    
    
    if (q->front == NULL) {
    
    
        return -1;  // 队列为空,返回错误标志
    }
    Node *temp = q->front;
    int value = temp->data;
    q->front = q->front->next;
    if (q->front == NULL) {
    
    
        q->rear = NULL;
    }
    free(temp);
    return value;
}
  1. 建立队列
    • 时间复杂度:通过多次调用进队函数来添加元素,假设添加 n n n 个元素,时间复杂度为 O ( n ) O(n) O(n)
    • 空间复杂度:需要为 n n n 个元素分配内存,空间复杂度为 O ( n ) O(n) O(n)
void buildQueue(Queue *q, int values[], int n) {
    
    
    for (int i = 0; i < n; i++) {
    
    
        enqueue(q, values[i]);
    }
}
  1. 输出队列元素
    • 时间复杂度:遍历整个队列,时间复杂度为 O ( n ) O(n) O(n),其中 n n n 是队列中元素的个数。
    • 空间复杂度:只需要一些临时变量来遍历队列,空间复杂度为 O ( 1 ) O(1) O(1)
void printQueue(Queue *q) {
    
    
    Node *temp = q->front;
    while (temp != NULL) {
    
    
        printf("%d ", temp->data);
        temp = temp->next;
    }
    printf("\n");
}

四,示例代码

#define OK 1
#define ERROR 0
#include<stdio.h>
#include<malloc.h>
typedef int Status;
typedef int QElemType;

typedef struct QNode {
    
      	//链表结点类型
    QElemType data;
    struct QNode* next;
}QNode, * QueuePtr;

typedef struct {
    
         		//队列类型
    QueuePtr   front;  	//队头指针
    QueuePtr   rear;  	//队尾指针
}LinkQueue;

Status InitQueue(LinkQueue& Q) {
    
    
    //构造一个带附加表头结点的空链队列Q
    Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
    if (!Q.front)  return ERROR;
    Q.front->next = NULL;
    return  OK;
}

Status EnQueue(LinkQueue& Q, QElemType e) {
    
    
    //将元素e插入到带头结点的链队列Q中
    QueuePtr p;
    p = (QueuePtr)malloc(sizeof(QNode));
    if (!p)  return ERROR;
    p->data = e;
    p->next = NULL;
    Q.rear->next = p;  // 1. 将新节点连接到队尾节点之后
    Q.rear = p;  // 2. 更新队尾指针指向新节点
    return  OK;
}

Status DeQueue(LinkQueue& Q, QElemType& e) {
    
    
    //若队列不空,则队首元素出队列,用e返回其值,返回OK,否则返回ERROR
    QueuePtr p;
    if (Q.rear == Q.front)
        return ERROR;
    p = Q.front->next;
    e = p->data;
    Q.front->next = p->next;  // 3. 将队头指针的下一个节点指向出队节点的下一个节点
    if (Q.rear == p)
        Q.rear = Q.front;  // 4. 若出队节点是队尾节点,更新队尾指针指向头节点(此时队列为空)
    free(p);
    return  OK;
}

void OutputQueue(LinkQueue Q)
// 输出队列中的所有元素
{
    
    
    QueuePtr p;
    p = Q.front->next;
    while (p != NULL) {
    
    
        printf("%d  ", p->data);
        p = p->next;  // 5. 移动指针到下一个节点
    }
    printf("\n");
    return;
}

void main()
{
    
    
    LinkQueue Q;
    InitQueue(Q);
    int op, x;
    while (1) {
    
    
        printf("请选择操作:1.进队  2.出队  0.退出==>");
        scanf("%d", &op);
        switch (op) {
    
    
        case 0:            // 退出
            return;
        case 1:            // 进队
            printf("请输入进队元素:"); 	// 整数
            scanf("%d", &x);
            EnQueue(Q, x);  // 6. 调用进队函数
            printf("队内元素为:\n");
            OutputQueue(Q);
            break;
        case 2:           // 出队
            if (DeQueue(Q, x)) {
    
         // 7. 若出队成功
                printf("出队元素为:[%d],队内元素为:\n", x);
                OutputQueue(Q);
            }
            else
                printf("  队已空!\n");
            break;
        }
    }
}

五,实验操作

1.双击程序图标,启动程序。
在这里插入图片描述

2.新建项目。
在这里插入图片描述
3.选择”空项目“——输入项目名称——单击”确认“按钮。
在这里插入图片描述
4.右击”源文件“——”添加“——选择”新建项“。
在这里插入图片描述

5.选择”C++文件“——输入文件名——单击”添加“按钮。
在这里插入图片描述
6.编写代码。
在这里插入图片描述

7.编译代码。
在这里插入图片描述

8.查看编译结果。
在这里插入图片描述

9.单击绿色小三角,运行项目。
在这里插入图片描述

六,运行效果

1.实验要求的效果。
在这里插入图片描述

2.编写程序运行后的效果。
在这里插入图片描述