文章首发于2020-10-15 知乎文章:数据结构(C语言)-链队列基本操作
作者:落雨湿红尘(也就是我o)
导语
队列是一种先进先出(first in first out,FIFO)的线性表,是一种常用的数据结构。
它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列结构
队列有很多种,按照存储结构划分,有链式队列,循环队列,单向队列,双端队列
实现队列的方式也有很多种,如基于链式存储结构的链接队列(又称链队列),基于顺序存储结构的队列
本文介绍链队列的实现和一些基本操作,并用代码的形式讲解
一些概念
- 一个链队列需要两个指针才能唯一确定,它们分别指示队头和队尾(分别称为头指针和尾指针)
- 与线性表的单链表一样,为了操作方便起见, 给链队列添加一个头结点,并令头指针指向头结点
- 空的链队列的判别条件为头指针和尾指针均指向头结点
图2 队列的链式存储结构(链队列)
链队列的操作
以下代码实现了一个可以保存学生学号(最长12位的字符串)链队列,实现了它的初始化,出队,入队,队列销毁的操作:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define OK 1
#define NO 0
//OK和NO是自定义的状态变量
//链队列结点
typedef struct Qnode{
char* data;//数据域
struct Qnode *next;//指针域,指向下一个结点
}Qnode;
//链队列
typedef struct{
Qnode* front;//队头指针 相当于Qnode *front
Qnode* rear;//队尾指针
}LinkQueue;
//初始化队列函数
int initQueue(LinkQueue* Q){
//给头尾结点分配内存,让队列中头尾指针指向同一个内存
Q->front = (Qnode*)malloc(sizeof(Qnode));
Q->rear = Q->front;
//储存分配失败
if(!Q->front)return NO;
//成功
Q->front->next = NULL;
return OK;
}
//插入元素,成为新的队尾元素
int insertQueue(LinkQueue* Q){
Qnode* tempNode;//声明一个临时结点
char *stuId = (char*)malloc(sizeof(12));//声明变量来接收输入
tempNode = (Qnode*)malloc(sizeof(Qnode));
//如果内存分配失败
if(!tempNode) return NO;
//输入学号并保存
scanf("%s",stuId);
tempNode->data = stuId;
tempNode->next = NULL;
Q->rear->next = tempNode;
Q->rear = tempNode;
return OK;
}
//元素出队
char* outQueue(LinkQueue* Q){
//若队列为空
if(Q->front==Q->rear) {
return NULL;
}
//若队列不空,则让队列头部元素出队
Qnode* p = Q->front->next;
char* data_return = p->data;
Q->front->next = p->next;
//如果只有一个元素,该元素出队后,头尾指针都指向NULL。此时应该重置头尾指针指向,相当于重置链队列
if(Q->rear==p) Q->rear = Q->front;
//如果不止一个元素,就不用重置尾指针位置
free(p);//回收内存
return data_return;
}
//销毁队列
int destroyQueue(LinkQueue* Q){
while(Q->front){
Q->rear = Q->front->next;
free(Q->front);
Q->front = Q->rear;
}
return OK;
}
//返回队列长度,用于计算还剩多少个元素结点在队列里
int queueLenth(LinkQueue* Q){
Qnode* p =Q->front->next;
int count=0;
while(p){
count++;
p = p->next;
}
return count;
}
//主函数
int main(){
// 初始化队列
LinkQueue Q;
//定义一个表示选择的变量
int choice;
//元素插入个数
int num;
//出队返回值
char* back;
do{
printf("==========链队列操作===========\n");
printf("1.初始化链队列\n");
printf("2.元素入队\n");
printf("3.元素出队\n");
printf("4.销毁队列\n");
printf("0.退出\n");
printf("=====================\n");
printf("请输入您的选择:");
scanf("%d",&choice);
switch(choice){
case 1:
initQueue(&Q)?printf("------------\n -->> 链队列初始化成功\n------------\n"):printf("------------\n -->> 链队列初始化失败\n------------\n");
break;
case 2:
printf("请输入插入元素个数:");
scanf("%d",&num);
int i;
for(i=0;i<num;i++){
printf(" 请输入第%d个学生的学号:",i+1);
if(!insertQueue(&Q))printf("------------\n-->> 第%d元素入队失败\n",i+1);
}
break;
case 3:
back = outQueue(&Q);
back? printf("------------\n 元素: %s 出队,还剩%d个元素\n",back,queueLenth(&Q)):printf("------------\n队列为空,无法出队!\n"); ;
break;
case 4:
destroyQueue(&Q)?printf("------------\n队列销毁成功!\n"):printf("------------\n队列销毁失败!\n");
break;
case 0:
printf("\n-->> 退出\n");
exit(0);
break;
default:
break;
}
}while(choice);
}
主函数里主要是考虑到用户交互和视觉效果,所以有很多printf函数用来打分割符。
以上代码经过调试,我自认为没有问题(鄙人才疏学浅)。如果读者朋友们,有疑问和更正,欢迎评论区补充和探讨