【嵌入式学习历程10】数据结构之队列
2017-12-03 10:55
561 查看
前面 我们讲了栈,知道栈有顺序存储和链式存储两种方式,今天的主角--队列也是一样的。
我们先来理清队列的概念:
1、队列是特殊的线性表;
队列仅在线性表两端进行操作;
队头(Front):取出数据的一端;
队尾(Rear):放入数据的一端;
2、性质:先进先出(FIFO)
3、顺序队列(一般采用循环队列)
规定front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样当front等于rear的时候,就表示该队列为空队列。
下面,我们上图
我们可以举个栗子,比如食堂打饭,下课之前,食堂空无一人,也就是空队列。下课后,大家陆陆续续来到食堂开始排队,但是由于空间限制,当这个队伍到一定的长度时,后面有一堵墙挡住了,就相当于队列有一个最大容量MAXSIZE,我们就假设是8,。但是我们看这个rear是如何规定的---rear指针指向队尾元素的下一个位置,所以,这个队伍实际上只能容纳7人,即对于一个容量为MAXSIZE的队列,最多只能存放MAXSIZE-1个元素。那么有人要问了,为啥不直接让rear指向最后一个元素呢,这样不就万事大吉了。然实则非也,之所以这样规定是有它的道理的。如果rear直接指向最后一个元素,那么当这个队列只有一个元素时,那么这个元素既是第一个也是最后一个,那么,front和rear就同时指向这个元素,当队列为空的时候也是front等于rear。那我们怎么区分0和1呢?
看到这里,我们就发现了顺序队列有个缺点,如果再来一个元素插进来,那么rear将何去何从呢,这时候会出现假溢出的现象,为了解决这个问题,我们一般采用循环队列。
循环队列,说白了就是头尾相接的队列,那么这种情况下,rear也会出现在front的前面。比如,上面图中的1号出队了,这时候又来个8号,这时候8号就排在了7号之后,但是rear就指向了1号出队后的空缺,那么front去哪了呢。我们为什么要取名为front呢,因为它是队头呀,那么当原本的队头1号出队了,front还有必要指向它吗?所以,当1号出队时,front已经指向了2号。我们说队列是先进先出的,那么当队列变成循环的了,应该先出那个呢?没错,这就是front的作用了,当然是先出front所指向的队头啦。
那么我们怎么知道队列的长度以及是否满了呢?
1.判断队列是否满:
(rear + 1) % MAXSIZE = = front;
2.求取队列的长度:
(rear - front + MAXSIZE) % MAXSIZE;
注意:这里的rear和front都是数组的下标。
typedef struct {
int data[MAXSIZE];
int front;
int rear;
}SqQueue;
初始化一个空队列Q:
int InitQueue(SqQueue *Q)
{
Q->front = 0;
Q->rear = 0;
return OK;
}
循环队列的入队列:
int EnQueue(SqQueue *Q, int e)
{
if((Q->rear+1)%MAXSIZE ==Q->front)
return ERROR;
Q->data[Q->rear] = e;
Q->rear=(Q->rear+1)%MAXSIZE;
return OK;
}
循环队列的出队列:
int DeQueue(SqQueue *Q, int *e)
{
if(Q->rear==Q->front)
return ERROR;
*e=Q->data[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
return OK;
}
4、链队
队列的链式存储结构,其实就是线性表的单链表,只是它只能够从尾进队、从头出队。一般,我们将队头指针指向链队列的头结点,而将队尾指针指向尾结点。
下面我们来实现链队的基本功能
头文件:
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_
#define FAILURE 10000
#define SUCCESS 10001
typedef int ElemType;
typedef struct node
{
ElemType data;
struct node *next;
}Node;
typedef Node *LinkNode;
typedef struct queue
{
LinkNode front;
LinkNode rear;
}Queue;
typedef Queue *LinkQueue;
#endif
接口函数实现:
#include <stdio.h>
#include <stdlib.h>
#include "LinkQueue.h"
主函数:
#include <stdio.h>
#include "LinkQueue.h"
int main()
{
int ret, i;
LinkQueue q;
ElemType e;
ret = InitQueue(&q);
if (FAILURE == ret)
{
printf("Init Failure!\n");
}
else
{
printf("Init Success!\n");
}
for(i = 0; i < 10; i++)
{
e = i;
ret = EnQueue(&q, e);
if (FAILURE == ret)
{
printf("Enter Element Failure!\n");
}
else
{
printf("Enter %d Success!\n",e);
}
}
printf("Length is %d\n", QueueLength(q));
for(i = 0; i < 7; i++)
{
ret = DelQueue(&q);
if (FAILURE == ret)
{
printf("Delete Element Failure!\n");
}
else
{
printf("Delete %d Success!\n",ret);
}
}
printf("Length is %d\n", QueueLength(q));
printf("Front is %d\n", GetFront(&q));
ret = ClearQueue(&q);
if (FAILURE == ret)
{
printf("Clear Failure!\n");
}
else
{
printf("Clear Success!\n");
}
printf("Length is %d\n", QueueLength(q));
ret = DestroyQueue(&q);
if (FAILURE == ret)
{
printf("Destroy Failure!\n");
}
else
{
printf("Destroy Success!\n");
}
return 0;
}
我们先来理清队列的概念:
1、队列是特殊的线性表;
队列仅在线性表两端进行操作;
队头(Front):取出数据的一端;
队尾(Rear):放入数据的一端;
2、性质:先进先出(FIFO)
3、顺序队列(一般采用循环队列)
规定front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样当front等于rear的时候,就表示该队列为空队列。
下面,我们上图
我们可以举个栗子,比如食堂打饭,下课之前,食堂空无一人,也就是空队列。下课后,大家陆陆续续来到食堂开始排队,但是由于空间限制,当这个队伍到一定的长度时,后面有一堵墙挡住了,就相当于队列有一个最大容量MAXSIZE,我们就假设是8,。但是我们看这个rear是如何规定的---rear指针指向队尾元素的下一个位置,所以,这个队伍实际上只能容纳7人,即对于一个容量为MAXSIZE的队列,最多只能存放MAXSIZE-1个元素。那么有人要问了,为啥不直接让rear指向最后一个元素呢,这样不就万事大吉了。然实则非也,之所以这样规定是有它的道理的。如果rear直接指向最后一个元素,那么当这个队列只有一个元素时,那么这个元素既是第一个也是最后一个,那么,front和rear就同时指向这个元素,当队列为空的时候也是front等于rear。那我们怎么区分0和1呢?
看到这里,我们就发现了顺序队列有个缺点,如果再来一个元素插进来,那么rear将何去何从呢,这时候会出现假溢出的现象,为了解决这个问题,我们一般采用循环队列。
循环队列,说白了就是头尾相接的队列,那么这种情况下,rear也会出现在front的前面。比如,上面图中的1号出队了,这时候又来个8号,这时候8号就排在了7号之后,但是rear就指向了1号出队后的空缺,那么front去哪了呢。我们为什么要取名为front呢,因为它是队头呀,那么当原本的队头1号出队了,front还有必要指向它吗?所以,当1号出队时,front已经指向了2号。我们说队列是先进先出的,那么当队列变成循环的了,应该先出那个呢?没错,这就是front的作用了,当然是先出front所指向的队头啦。
那么我们怎么知道队列的长度以及是否满了呢?
1.判断队列是否满:
(rear + 1) % MAXSIZE = = front;
2.求取队列的长度:
(rear - front + MAXSIZE) % MAXSIZE;
注意:这里的rear和front都是数组的下标。
typedef struct {
int data[MAXSIZE];
int front;
int rear;
}SqQueue;
初始化一个空队列Q:
int InitQueue(SqQueue *Q)
{
Q->front = 0;
Q->rear = 0;
return OK;
}
循环队列的入队列:
int EnQueue(SqQueue *Q, int e)
{
if((Q->rear+1)%MAXSIZE ==Q->front)
return ERROR;
Q->data[Q->rear] = e;
Q->rear=(Q->rear+1)%MAXSIZE;
return OK;
}
循环队列的出队列:
int DeQueue(SqQueue *Q, int *e)
{
if(Q->rear==Q->front)
return ERROR;
*e=Q->data[Q->front];
Q->front=(Q->front+1)%MAXSIZE;
return OK;
}
4、链队
队列的链式存储结构,其实就是线性表的单链表,只是它只能够从尾进队、从头出队。一般,我们将队头指针指向链队列的头结点,而将队尾指针指向尾结点。
下面我们来实现链队的基本功能
头文件:
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_
#define FAILURE 10000
#define SUCCESS 10001
typedef int ElemType;
typedef struct node
{
ElemType data;
struct node *next;
}Node;
typedef Node *LinkNode;
typedef struct queue
{
LinkNode front;
LinkNode rear;
}Queue;
typedef Queue *LinkQueue;
#endif
接口函数实现:
#include <stdio.h>
#include <stdlib.h>
#include "LinkQueue.h"
/* 初始化队列 */ int InitQueue(LinkQueue *Q) { (*Q)->front = (LinkNode)malloc(sizeof(Node)); if ((*Q) == NULL) { return FAILURE; } (*Q)->rear = (*Q)->front; (*Q)->front->next = NULL; return SUCCESS; }
/* 进队 */ int EnQueue(LinkQueue *Q, ElemType x) { LinkNode p = (LinkNode)malloc(sizeof(Node)); if(NULL == p) { return FAILURE; } p->data = x; p->next = NULL; (*Q)->rear->next = p; (*Q)->rear = p; return SUCCESS; }
/* 求队列长度 */ int QueueLength(LinkQueue Q) { int length = 0; LinkNode q = Q->front; while(q->next) { length++; q = q->next; } return length; }
/* 出队 */ int DelQueue(LinkQueue *Q) { ElemType x; LinkNode p = (*Q)->front->next; if (NULL == p) { return FAILURE; } x = p->data; (*Q)->front->next = p->next; free(p); if (NULL == p->next) { (*Q)->rear = (*Q)->front; } return x; }
/* 获取队头 */ int GetFront(LinkQueue *Q) { LinkNode p = (*Q)->front->next; if (NULL == p) { return FAILURE; } return p->data; }
/* 清空队列 */ int ClearQueue(LinkQueue *Q) { LinkNode p = (*Q)->front->next; if(NULL == p) { return FAILURE; } while(p) { (*Q)->front->next = p->next; free(p); p = (*Q)->front->next; } if(NULL == p) { (*Q)->rear = (*Q)->front; } return SUCCESS; }
/* 销毁队列 */ int DestroyQueue(LinkQueue *Q) { if(NULL == (*Q)) { return FAILURE; } free((*Q)->front); (*Q)->front = NULL; (*Q)->rear = NULL; return SUCCESS; }
主函数:
#include <stdio.h>
#include "LinkQueue.h"
int main()
{
int ret, i;
LinkQueue q;
ElemType e;
ret = InitQueue(&q);
if (FAILURE == ret)
{
printf("Init Failure!\n");
}
else
{
printf("Init Success!\n");
}
for(i = 0; i < 10; i++)
{
e = i;
ret = EnQueue(&q, e);
if (FAILURE == ret)
{
printf("Enter Element Failure!\n");
}
else
{
printf("Enter %d Success!\n",e);
}
}
printf("Length is %d\n", QueueLength(q));
for(i = 0; i < 7; i++)
{
ret = DelQueue(&q);
if (FAILURE == ret)
{
printf("Delete Element Failure!\n");
}
else
{
printf("Delete %d Success!\n",ret);
}
}
printf("Length is %d\n", QueueLength(q));
printf("Front is %d\n", GetFront(&q));
ret = ClearQueue(&q);
if (FAILURE == ret)
{
printf("Clear Failure!\n");
}
else
{
printf("Clear Success!\n");
}
printf("Length is %d\n", QueueLength(q));
ret = DestroyQueue(&q);
if (FAILURE == ret)
{
printf("Destroy Failure!\n");
}
else
{
printf("Destroy Success!\n");
}
return 0;
}
相关文章推荐
- 【嵌入式学习历程8】数据结构之链表
- 【嵌入式学习历程11】数据结构之二叉树
- 【嵌入式学习历程7】 数据结构之线性表
- 数据结构与算法学习之队列及队列的相关操作
- (三)Android数据结构学习之队列
- 数据结构再学习--队列
- 学习笔记--数据结构(之二)队列
- 【嵌入式学习历程9】数据结构之栈
- 记录我的数据结构(C语言)学习历程(2017年3月30号开始):
- 信管16数据结构:第三章栈和队列的课前翻转学习任务
- 菜鸟学习历程【11】数据结构
- 数据结构——链队列学习
- [算法学习笔记]数据结构之栈和队列
- 数据结构(Java 队列模拟)本代码重在学习数据结构思路,代码完整性欠缺,请见谅
- 嵌入式学习之路(二十二)——数据结构(4)
- C语言学习历程(十七)数据结构与排序(冒泡、选择、希尔排序)算法
- 学习java数据结构基础知识之队列
- 慕课网学习笔记之数据结构队列(C++)
- 数据结构与算法学习笔记——队列
- 【算法学习笔记】06.数据结构基础 队列与堆栈初步