7、数据结构与算法 - 队列

 大纲

  1.  队列假溢出问题
  2.  循环队列的空间/满判断推理
  3.  队列的顺序存储设计/链式存储设计


 一、队列假溢出问题

队列

队列同    一样也是一种线性表,它是在表的一端插入数据,在另一端取出元素。插入元素的这个一端称之为队尾,取出元素的一端称之为队头


队列的特性:

  •  只能从队尾插入,从队头取出(入队,出队)
  •  FIFO,先进先出。


 
 假溢出

如上图C 假设队列的长度为6,初始化空队列如下如所示。c1、c2 出队,这时候fornt(队头)指针指向2,rear(队尾)指针指向3。
然后 c4、c5、c6 相继入队,c3、c4相继出队。这个时候正常情况下front 指针指向下标为 4的位置,rear 指向5。如果出队的速度快与入队的速度,就有可能 front 和 rear 都指向4的位置,这时候认为队满,但是实际上队里还有许多空闲位置。 这种情况是由于一头出队,一头入队的机制照成的,这种现象叫 - - 假溢出

为了解决在队尾空出来一个位置,不通过front 与 rear 相等的方式判断队满(因为 front  与rear 相等也是空队列的标识,所以只能用来判断一种状态)。

 

二、循环队列的空间/满判断推理

加入不是循环的,因为队列是一端进一端出的,如果一直出队入队,前面出队的位置是空着的这样有再多的内存也不够用(或者是进行位移,但是消耗会很大)。为了解决这样的情况,所以使用 循环队列(为什么要用循环的,后面”假溢出“会说明),也就是逻辑上队列是一个环形(并不是说内存是环形,而是逻辑上)。在使用队列时,使用连个变量表示队头和队尾(例如:Q.front 队头  Q.rear 队尾)。
 
通过队头队尾的指针可以判断队列是否为空或则是否存满。如下图
 
 
 如上图,队空的时候 Q.front == Q.rear,队满的时候也是 Q.front == Q.rear。这样的话就无法判断到这个时候是队空还是队满,多以就需要在队尾空出一个位置,不能存满。也就是说只有队空的时候Q.front == Q.rear,其他时候就不会是Q.front == Q.rear。

 判断队空和队满的公式:
 队空:Q.front == Q.rear
 队满:(Q.rear + 1) % MAXSIZE == Q.front;       MAXSIZE 为开辟的队列长度


 三、队列的顺序存储设计/链式存储设计

同栈一样,队列也可以通过 顺序 来实现,也可以通过 链式 来实现。

#include <stdio.h>
#include "stdlib.h"
#include "math.h"
#include "time.h"
    
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OK 1
#define MAXSIZE 20 //存储空间初始分配量
    
typedef int Status;//Status 是函数的类型,其值是函数结果状态代码l,如OK等
typedef int QElemType;//QElemType 类型根据实际情况而定,这里假设为int


 1、顺序存储

//循环队列 顺序存储队列结构
typedef struct{
    //  1、开辟连续空间存储队列数据内容
    QElemType data[MAXSIZE];
    //    2、索引(对头/队尾)
    int fornt;
    int rear
} SqQueue;
//2、将队列清空
//队列也是线性表,所以不需要把数据删除,只需要把队头和队尾都指向0,即可。在用到的时候自动会覆盖掉对应位置上的数据
Status ClearQueue(SqQueue *Q){
    Q->fornt = Q->rear = 0;
    return OK;
}

//3、是否为空
//判断队列是否为空
//为空返回 TRUE  否者返回 FALSE
Status QueueEmpty(SqQueue Q){
    if (Q.fornt == Q.rear) return TRUE;
    return FALSE;
}

//4、长度
//返回队列的元素个数(长度)
//因为是循环的所以队尾指针指向的值可能小于队头,实际的长度应该是两个值的差的绝对值
//也可以写成 (Q.rear - Q.fornt + MAXSIZE) % MAXSIZE;
int QueueLength(SqQueue Q){
    return abs(Q.rear - Q.fornt);
}

//5、队头
//若队列不为空,则用e返回Q的队头元素,并返回OK,否者返回ERROR
Status GetHead(SqQueue Q, QElemType *e){
    if (Q.rear == Q.fornt) return ERROR;
    *e = Q.data[Q.fornt];
    return OK;
}

//6、入队
//若队列没有满,则插入元素e 为新队尾元素
Status EnQueue(SqQueue *Q, QElemType e){
    if ((Q->rear + 1) % MAXSIZE == Q->fornt) return ERROR;//已满
    Q->data[Q->rear] = e;//元素e赋值给队尾
    Q->rear = (Q->rear + 1) % MAXSIZE;//因为是循环队列,所以不能简单的用 ++
    return OK;
}

//7、出队
//若队列不为空,则删除Q中队头的元素,用e 返回值。
Status DeQueue(SqQueue *Q, QElemType *e){
    if (Q->rear == Q->fornt) return ERROR;//为空
    *e = Q->data[Q->fornt];
    Q->fornt = (Q->fornt + 1)  % MAXSIZE;//因为是循环队列,所以不能简单的用 ++
    return OK;
}

//8、遍历
//从队头到队尾依次输出队列的每个元素数组
//只是看一下内部的数据,无关从从头还是从尾开始
Status QueueTraverse(SqQueue Q){
    int i;
    i = Q.fornt;
    while ((i + Q.fornt) != Q.rear) {
        printf("%d  ",Q.data[i]);
        i = (i + 1) % MAXSIZE;
    }
    printf("\n");
    return OK;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    
    Status j;
    int i = 0;
    SqQueue Q;
    QElemType d;
    
    InitQueue(&Q);
    printf("初始化队列后,队列空否?%u(1:空 0:否)\n",QueueEmpty(Q));
    
    printf("入队:\n");
    while (i < 10) {
        EnQueue(&Q, i);
        i++;
    }
    QueueTraverse(Q);
    printf("队列长度为: %d\n",QueueLength(Q));
    printf("现在队列空否?%u(1:空 0:否)\n",QueueEmpty(Q));
    printf("出队:\n");
    
    //出队
    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(1:空 0:否)
入队:
0  1  2  3  4  5  6  7  8  9
队列长度为: 10
现在队列空否?0(1:空 0:否)
出队:
出队的元素:0
1  2  3  4  5  6  7  8
现在队头元素为: 1
清空队列后, 队列空否?1(1:空 0:否)


 2、链式队列

//结点结构
typedef struct QNode{
    QElemType data;
    struct QNode * next;
}QNode, *QueuePtr;

// 队列链表结构
typedef struct{
    QueuePtr front,rear;//队头、队尾指针
}LinkQueue;
//1、初始化队列
Status InitQueue(LinkQueue * Q){
    //    1、新创建一个结点
    QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
    if (p == NULL) return ERROR;
    
    //    2、将队头、队尾指向新创建的结点
    Q->front = p;
    Q->rear = p;
    
    //   3、头结点的指针置空
    Q->front->next = NULL;
    return OK;
}

//2、销毁队列Q
//用Q->fornt 去遍历队列,然后释放。用 Q->rear 指向遍历到的下一个结点 防止链表后面的结点丢失
Status DestoryQueue(LinkQueue *Q){
    
    while (Q->front) {
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    return OK;
}

//3、将队列置空
Status ClearQueue(LinkQueue *Q){
    
    QueuePtr p,q;
    p = Q->front->next;
    Q->rear = Q->rear;
    Q->front->next = NULL;
    
    while (p) {
        q = p->next;
        free(p);
        p = q;
    }
    return OK;
}

//4、判断队列 Q 是否为空
Status QueueEmpty(LinkQueue Q){
    if (Q.front == Q.rear)return TRUE;
    return FALSE;
}

//5、队列长度
int QueueLength(LinkQueue Q){
    int count = 0;
    QueuePtr p;
    
    //    p = Q.front;
    //    while (p != Q.rear) {
    
    p = Q.front->next;
    while (p) {
        p = p->next;
        count ++;
    }
    return count;
}

//5、插入元素 e 为队列Q的新元素
Status EnQueue(LinkQueue *Q, QElemType e){
    
    //    创建新结点并赋值
    QueuePtr p = malloc(sizeof(QNode));
    if (!p) return ERROR;
    p->data = e;
    p->next = NULL;
    
    Q->rear->next = p;//将新结点插到队尾
    Q->rear = p;//修改队尾指针
    return OK;
}

//7、出队
Status DeQueue(LinkQueue *Q, QElemType *e){
    
    if (Q->front == Q->rear) return ERROR;
    
    //将要删除的队头结点暂时存储在p
    QueuePtr p;
    p = Q->front->next;
    //将要删除的队头结点的值赋值给e
    *e = p->data;
    //将原队列头结点的后继p->next 赋值给头结点后继
    Q->front->next = p->next;
    
    //若队头就是队尾,则删除后将rear指向头结点
    if (Q->rear == p->next) Q->rear = Q->front;
    
    free(p);
    
    return OK;
}

//8、获取头元素
Status GetHead(LinkQueue Q, QElemType *e){
    
    if (Q.front == Q.rear) return FALSE;
    *e = Q.front->next->data;//返回队头元素的值,队头不变
    
    return TRUE;
}

//9、遍历队列
Status QueueTraverse(LinkQueue Q){
    QueuePtr p;
    p = Q.front->next;
    while (p) {
        printf("%d  ",p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    
    Status iStatus;
    QElemType d;
    LinkQueue q;
    
    //1.初始化队列q
    iStatus = InitQueue(&q);
    
    //2. 判断是否创建成
    if (iStatus) {
        printf("成功地构造了一个空队列\n");
    }
    
    //3.判断队列是否为空
    printf("是否为空队列?%d (1:是 0:否)\n",QueueEmpty(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",QueueEmpty(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.销毁队列
    DestoryQueue(&q);
    
    return 0;
}
//*******************************
成功地构造了一个空队列
是否为空队列?1 (1:是 0:否)
队列的长度为0
队列的长度为3
是否为空队列?0 (1:是 0:否)
队列中的元素如下:
-3  6  12
队头元素是:-3
删除了的队头元素为:-3
新的队头元素为:6
发布了104 篇原创文章 · 获赞 13 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/shengdaVolleyball/article/details/105522167