队列的概念
队列我们可以理解为单行道的马路上的车辆,通过收费站时,排在前面的车辆先通过,排在后面的车辆后通过,通过收费站的我们叫出队列,后面新加入队列的叫入队。
队列的特点:先进先出,队列也是一种操作受限的线性表数据结构 它具有先进先出的特性,支持队尾插入元素,在队头删除元素。
队列的两个基本操作:
入队 将一个数据放到队列尾部;出队 从队列的头部取出一个元素。
顺序队列
队列示意图:
队列表示与操作实现:
- 图(a)当q.rear = q.front = 0 时,为空队列;
- 图(b)当 C1,C2,C3 三个元素相继入队,q.fong = 0 位置不变,q.rear = 3 往后移三个位置;
- 图(c)当 C1,C2 两个元素相继出队,q.front = 2,往后移两个位置,q.rear = 3,位置不变;
- 图(d)当沾满后,C4 C5 C6相继⼊入队; C3 C4 相继出队,此时 q.front = 4,q.front = 5.
此时看起来最后一个位置都为C6占了,感觉像是队列满了,但实际上没有满的,我们称此为 "假溢出"。
循环队列
为了解决"假溢出"这个问题就有引出循环队列,数组可以想象成一条直线,我们把这条直线掰成一个圆环,就是循环队列,为了更形象的表示,可以看下图所示
如上图所示,就向是一个循环出对入队的过程,可以想象一下,当最后一个位置被占用后,要继续入栈的话,就会入队到第一个位置;出队也是一样。
因为空队列图(c)的时候 q.front == q.rear; 队空间被沾满时图(b),也是 q.front == q.rear 。所以这样就无法分辨出是空队列还是满队列了,为了解决这个问题就在队列尾空出一个位置留白不用,如图(d)。
q.front == q.rear 时,队列为空;
(q.rear + 1)%MAXSIZE == q.front 时,队列为满。
循环队列表示与操作实现
循环队列的顺序存储表示与操作实现
1、循环队列的顺序存储结构定义
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */
typedef int status;
typedef int Element;//可以根据自己实际情况而定,这里假设为 int
typedef struct {
Element data[MAXSIZE];
int front; //头指针
int rear; //尾指针,若队列不空,指向队列尾元素的下一个位置
}SqQueue;
2、初始化一个空队列
status initQueue(SqQueue *q) {
q->front = 0;
q->rear = 0;
return = OK;
}
3、将队列清空
status clearQueue(SqQueue *q) {
q->front = 0;
q->rear = 0;
return = OK;
}
4、若队列Q为空队列,则返回TRUR,否则返回FALSE
status isEmpty(SqQueue q) {
if (q.front == q.rear) {
return TURE;
} else {
return FALSE;
}
}
5、返回Q的元素个数,也就是队列的当前长度
int queueLength(SqQueue q) {
return (q.rear - q.front + MAXSIZE) % MAXSIZE;
}
6、若队列不为空,则用e返回Q的队头元素,并返回OK,否则返回ERROR
status getHead(SqQueue q, Element *e) {
//队列为空
if (q.front == q.rear) return ERROR;
*e = q.data[q.front];
return OK;
}
7、若队列未满,则插入元素e为新队尾元素
status enQueue(SqQueue *q, Element e) {
//队列已满
if ((q->rear + 1)%MAXSIZE == q->front) return ERROR;
//将元素e赋值给队尾
q->data[q->rear] = e;
//尾指针往后移一位,遇到最后则转到队列头部
q->rear = (q.rear + 1)%MAXSIZE;
return OK;
}
8、若队列不空,则删除Q中队头的元素,用e返回值
status deQueue(SqQueue *q, Element *e) {
//判断队列是否为空
if (q->front == q->rear) return ERROR;
//将队头元素赋值给e
*e = q->data[q->front];
//头指针往后移一位,遇到最后则转到队列头部
q->front = (q->front + 1)%MAXSIZE;
return OK;
}
9、从队头到队尾依次对队列的每个元素数组
void queueTraverse(SqQueue Q) {
if (q.front == q.rear)
printf("队列为空!");
int i = 0;
i = q.front;
while(i != q.rear) {
printf("%d ",q.data[i]);
i = (i + 1)%MAXSIZE;
}
printf("\n");
}
验证:
int main(int argc, const char * argv[]) {
// insert code here...
printf("001--顺序队列表示与操作实现\n");
Status j;
int i=0,l;
Element d;
SqQueue Q;
initQueue(&Q);
printf("初始化队列后,队列空否?%u(1:空 0:否)\n",isEmpty(Q));
printf("入队:\n");
while (i < 10) {
EnQueue(&Q, i);
i++;
}
queueTraverse(Q);
printf("队列长度为: %d\n",queueLength(Q));
printf("现在队列空否?%u(1:空 0:否)\n",isEmpty(Q));
printf("出队:\n");
//出队
deQueue(&Q, &d);
printf("出队的元素:%d\n",d);
queueTraverse(Q);
deQueue(&Q, &d);
printf("出队的元素:%d\n",d);
queueTraverse(Q);
//获取队头
j=getHead(Q,&d);
if(j)
printf("现在队头元素为: %d\n",d);
clearQueue(&Q);
printf("清空队列后, 队列空否?%u(1:空 0:否)\n",queueEmpty(Q));
return 0;
}
打印结果:
循环队列的链式存储表示与操作实现
1、循环队列的顺序存储结构定义
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */
typedef int status;
typedef int Element;//Element根据实际情况而定,这里是int
//结点结构
typedef struct qNode {
Element data;
struct qNode *next;
}qNode, *queuePtr;
//队列的链表结构
typedef struct {
queuePtr front, rear; /*队头队尾指针*/
}LinkQueue;
2、初始化队列
status initQueue(LinkQueue *q) {
//1.头/尾指针都指向新生成的阶段
q->front = q->rear = (queuePtr)malloc(sizeof(qNode));
//判断是否创建新节点成功
if(!q->front) {
return ERROR;
}
//3.头结点的指针域置空
q->front->next = NULL;
return OK;
}
3、销毁队列
status destoryQueue(LinkQueue *q) {
//遍历整个队列,并释放每个节点
while(q->front) {
q->rear = q->front->next;
free(q->front);
q->front = q->rear;
}
return OK;
}
4、将队列Q置空
status cleanQueue(LinkQueue *q) {
QueuePtr temp,p ;
q->rear = q->front;
p = q->front;
q->front = q->rear = NULL;
while(p) {
temp = p;
p = temp->next;
free(temp);
}
}
5、判断队列Q是否为空
status isEmpty(LinkQueue q) {
if (q->front == q->rear) {
return TURE;
} else {
return FLASE;
}
}
6、获取队列长度
int queueLength(LinkQueue q) {
int i = 0;
queuePtr p;
p = q->front;
while(p) {
i++;
p = p->next;
}
return i;
}
7、插入元素e为队列Q的新元素
status enQueue(LinkQueue *q, Element *e) {
//为新元素创建节点,分配内存空间
queuePtr p = (queuePtr)malloc(sizeof(qNode));
//判断是否分配成功
if(!p) return ERROR;
p->data = e;
p->next = NULL;
//将新节点插入到队尾
q->rear->next = p;
//修改队尾指针
q->rear = p;
return OK;
}
8、出队列
status deQueue(LinkQueue *q, Element *e) {
queuePtr p;
//判断队列是否为空
if(q->front == q->rear) return ERROR;
//将要删除的队头结点暂时存储在p
p = q->front->next;
//将要删除的队头结点的值赋值给e
*e = p->data;
//将原队列头结点的后继p->next 赋值给头结点后继
q->front->next = p->next;
//若队头就是队尾,则删除后将rear指向头结点.
if (q->rear == p) q->rear = q->front;
free(p);
return OK;
}
9、获取队头元素
status getHead(LinkQueue q,Element *e) {
//判断队列是否为空
if(q->front == q->rear) return ERROR;
*e = q->front->next->data;
return OK;
}
10、遍历队列
status queueTraverse(LinkQueue q) {
queuePtr p;
while(p) {
printf("%d ",p->data);
}
printf("\n");
return OK;
}
验证:
int main(int argc, const char * argv[]) {
// insert code here...
printf("链队列的表示与操作!\n");
status iStatus;
Element d;
LinkQueue q;
//1.初始化队列q
iStatus = initQueue(&q);
//2. 判断是否创建成
if (iStatus) {
printf("成功地构造了一个空队列\n");
}
//3.判断队列是否为空
printf("是否为空队列?%d (1:是 0:否)\n",isEmpty(q));
//4.获取队列的长度
printf("队列的长度为%d\n",queueLength(q));
//5.插入元素到队列中
enQueue(&q, -3);
enQueue(&q, 6);
enQueue(&q, 12);
printf("队列的长度为%d\n",queueLength(q));
printf("是否为空队列?%d (1:是 0:否)\n",isEmpty(q));
//6.遍历队列
printf("队列中的元素如下:\n");
queueTraverse(q);
//7.获取队列头元素
iStatus = getHead(q, &d);
if (iStatus == OK) {
printf("队头元素是:%d\n",d);
}
//8.删除队头元素
iStatus =deQueue(&q, &d);
if (iStatus == OK) {
printf("删除了的队头元素为:%d\n",d);
}
//9.获取队头元素
iStatus = getHead(q, &d);
if (iStatus == OK) {
printf("新的队头元素为:%d\n",d);
}
//10.清空队列
clearQueue(&q);
//11.销毁队列
dDestoryQueue(&q);
return 0;
}
打印: