数据结构——队列(C语言版)

简介: 数据结构——队列(C语言版)

前言:


在学习完数据结构顺序表和链表之后,其实我们就可以做很多事情了,后面的栈和队列,其实就是对前面的顺序表和链表的灵活运用,今天我们就来学习一下队列的原理和应用。


准备工作:本人习惯将文件放在test.c、SeqList.c、SeqList.h三个文件中来实现,其中test.c用来放主函数,SeqList.c用来放调用的函数,SeqList.h用来放头文件和函数声明


什么是队列?

队列中的数据是按照先进先出的顺序的,也就是说先进去的数字也先出来

因为队列的这种性质,所以队列我们用链表来实现比顺序表方便很多,因为用顺序表每插入一个数或者删除一个数都需要遍历整个数组,这样就会很容易出错且不够方便,我们一般采用单链表来实现队列

队列的节点结构

队列采用的单链表的结构,所以与单链表差异不大

typedef int QDataType;
typedef struct QueueNode
{
  struct QueueNode* next;
  QDataType data;
}QNode;

但是我们在操作中,因为队列是要先进先出,其实也就是尾部进数据,头部出数据,所以我们要定义一个头指针和一个尾指针指向头尾两个数据,这样有利于我们后面增加或者删除数据,我们可以再定义一个结构体来存放这两个指针

typedef struct Queue
{
  QNode* phead;    //头指针
  QNode* ptail;    //尾指针
  int size;        //队列中的元素个数
}Queue;

队列的基本操作

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);
//增加
void QueuePush(Queue* pq,QDataType x);
//删除
void QueuePop(Queue* pq);
//取队头
QDataType QueueFront(Queue* pq);
//取队尾
QDataType QueueBack(Queue* pq);
//取长度
QDataType QueueSize(Queue* pq);
//判断是否为空
bool QueueEmpty(Queue* pq);

看上面的函数声明部分我们就可以看到我们每一步要实现的内容,接下来,我们就来一步一步进行实现

1、初始化

//初始化
void QueueInit(Queue* pq)
{
  assert(pq);
  pq->phead = NULL;
  pq->ptail = NULL;
  pq->size = 0;
}

参数全是一个结构体指针,而且肯定不能为空,所以我们下面的函数基本都要对pq进行断言

2、销毁

将头指针指向的空间逐个释放,最后把头指针和尾指针均赋为空指

//销毁
void QueueDestroy(Queue* pq)
{
  QNode* cur = pq->phead;
  while (cur)
  {
    QNode* next = cur->next;
    free(cur);
    cur = next;
  }
  pq->phead = pq->ptail = NULL;
  pq->size = 0;
}

3、增加(插入数据)

因为先进先出,实际上数据就是从尾部插入,从头部出去,即插入形式为尾插

//插入
void QueuePush(Queue* pq, QDataType x)
{
  assert(pq);
  QNode* newnode = (QNode*)malloc(sizeof(QNode));
  newnode->data = x;
  newnode->next = NULL;
    //判断头部是否为空
  if (pq->phead == NULL)
  {
    pq->phead = pq->ptail = newnode;
  }
  else
  {
    pq->ptail->next = newnode;
    pq->ptail = newnode;
  }
  pq->size++;
}

用单链表插入数据必须要考虑头部为空和不为空两种情况

4、删除

栈和队列都有一个特点就是,数据只会输出一遍,也就是数据一经输出就会销毁删除

如下:

1在输出之后就会被删除,删除的代码如下:

//删除
void QueuePop(Queue* pq)
{
  assert(pq);
  assert(!QueueEmpty(pq));
 
  //1、一个节点
  //2、多个节点
  if (pq->phead->next == NULL)
  {
    free(pq->phead);
    pq->phead = pq->ptail = NULL;
  }
  else
  {
    QNode* ptr = pq->phead->next;
    free(pq->phead);
    pq->phead = ptr;
  }
}

这里面又分为两种情况,难度不大,根据注释自行体会即可

5、取队头、取队尾、取长度、判断头指针是否为空

这几步难度不大,放在一起看看

取队头

//取队头
QDataType QueueFront(Queue* pq)
{
  assert(pq);
  assert(!QueueEmpty(pq));
    
  return pq->phead->data;
}

取队尾

//取队尾
QDataType QueueBack(Queue* pq)
{
  assert(pq);
  assert(!QueueEmpty(pq));
 
  return pq->ptail->data;
}

取长度

//取长度
QDataType QueueSize(Queue* pq)
{
  assert(pq);
  return pq->size;
}

判断头指针是否为空(这个函数应用上可以在下面的完整案列上体会一下)

bool QueueEmpty(Queue* pq)
{
  assert(pq);
  return pq->phead == NULL;
}

完整的队列实例

test.c

//队列
void TestQNode()
{
  Queue q;
  QueueInit(&q);
  QueuePush(&q, 1);
  QueuePush(&q, 2);
  QueuePush(&q, 3);
  QueuePush(&q, 4);
  printf("%d\n", QueueSize(&q));
  while (!QueueEmpty(&q))
  {
    printf("%d ", QueueFront(&q));
    QueuePop(&q);
  }
  QueueDestroy(&q);
}
int main()
{
  TestQNode();
  return 0;
}

SeqList.h

//队列
typedef int QDataType;
typedef struct QueueNode
{
  struct QueueNode* next;
  QDataType data;
}QNode;
 
typedef struct Queue
{
  QNode* phead;
  QNode* ptail;
  int size;
}Queue;
 
//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);
//增加
void QueuePush(Queue* pq,QDataType x);
//删除
void QueuePop(Queue* pq);
//取队头
QDataType QueueFront(Queue* pq);
//取队尾
QDataType QueueBack(Queue* pq);
//取长度
QDataType QueueSize(Queue* pq);
//判断是否为空
bool QueueEmpty(Queue* pq);

SeqList.c

//初始化
void QueueInit(Queue* pq)
{
  assert(pq);
  pq->phead = NULL;
  pq->ptail = NULL;
  pq->size = 0;
}
//销毁
void QueueDestroy(Queue* pq)
{
  QNode* cur = pq->phead;
  while (cur)
  {
    QNode* next = cur->next;
    free(cur);
    cur = next;
  }
  pq->phead = pq->ptail = NULL;
  pq->size = 0;
}
//插入
void QueuePush(Queue* pq, QDataType x)
{
  assert(pq);
  QNode* newnode = (QNode*)malloc(sizeof(QNode));
  newnode->data = x;
  newnode->next = NULL;
  if (pq->phead == NULL)
  {
    pq->phead = pq->ptail = newnode;
  }
  else
  {
    pq->ptail->next = newnode;
    pq->ptail = newnode;
  }
  pq->size++;
}
//删除
void QueuePop(Queue* pq)
{
  assert(pq);
  assert(!QueueEmpty(pq));
 
  //1、一个节点
  //2、多个节点
  if (pq->phead->next == NULL)
  {
    free(pq->phead);
    pq->phead = pq->ptail = NULL;
  }
  else
  {
    QNode* ptr = pq->phead->next;
    free(pq->phead);
    pq->phead = ptr;
  }
}
//取队头
QDataType QueueFront(Queue* pq)
{
  assert(pq);
  assert(!QueueEmpty(pq));
    
  return pq->phead->data;
}
//取队尾
QDataType QueueBack(Queue* pq)
{
  assert(pq);
  assert(!QueueEmpty(pq));
 
  return pq->ptail->data;
}
//取长度
QDataType QueueSize(Queue* pq)
{
  assert(pq);
  return pq->size;
}
 
bool QueueEmpty(Queue* pq)
{
  assert(pq);
  return pq->phead == NULL;
}

运行后结果:


总结

总之,其实队列就是对链表的应用,熟练栈和队列,对我们巩固顺序表和链表帮助很大,当然,队列在一些场景下很实用,后面我会出一个专门的习题讲解篇章,讲数据结构的一些经典题型,感兴趣的可以点赞关注一下

创作不易,还请各位大佬点赞支持一下!!!

相关文章
【数据结构】栈和队列
【数据结构】栈和队列
|
4天前
|
存储 C语言
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
|
11天前
|
存储
【数据结构】栈和队列-->理解和实现(赋源码)
【数据结构】栈和队列-->理解和实现(赋源码)
14 5
|
1月前
|
存储 前端开发 DataX
【数据结构】栈和队列
数据结构中的栈和队列
24 1
【数据结构】栈和队列
|
3天前
|
算法
【数据结构与算法】优先级队列
【数据结构与算法】优先级队列
6 0
|
3天前
|
存储 算法
【数据结构与算法】队列(顺序存储)
【数据结构与算法】队列(顺序存储)
5 0
|
5天前
|
设计模式 算法 C语言
【CPP】栈、双端队列、队列、优先级队列与反向迭代器
【CPP】栈、双端队列、队列、优先级队列与反向迭代器
|
5天前
【数据结构】用栈实现队列
【数据结构】用栈实现队列
|
5天前
【数据结构】用队列实现栈
【数据结构】用队列实现栈
|
5天前
|
存储
【数据结构】队列
【数据结构】队列