数据结构学习第二弹 栈与队列(2)

队列

队列,可以说是日常生活中最常见的一种现象,队列与平时排队有着相似的特点。队列也是一种运算受限制的线性表,与栈不同的是,其是限制在两端操作的线性表

定义:

队列就跟日常排队有一样的特点:先进先出,只允许在队列的一端插入数据元素(入队),只允许在队列的另一端删除数据元素(出队),可删除的一端称为队头,可插入的一端称为队尾。
队列的修改总是按照先进先出的原则进行的,也就是说新的元素,只能添加在对尾,每次离开的元素只能从队头离开,就和排队打饭一样,不过这个队不允许中途离队,比如队列中加入元素a1,a2,a3……an,a1就是队头元素,an就是队尾元素,退出队列的顺序也只能是a1,a2……an.

队列的基本操作

  • InitQueue(Q): 构造一个空队列
  • EmptyQueue(Q): 判断一个队列是否为空
  • LengthQueue(Q): 求队列的长度
  • FrontQueue(Q,e): 获取队头元素
  • EnQueue(Q,e): 入队操作
  • DeQueue(Q,e): 出队操作

和栈同理,队列也可以分为顺序队列和链队列。

顺序队

顺序存储结构的类型定义

#define MAXSIZE 20          //单纯的空间大小,并不能代表队列中的元素有多少
typedef int ElemType;
typedef struct
{
    ElemType data[MAXSIZE];
    int front,rear;         //尾指针和头指针
}SeQueue;

具体操作

#define OK 1
#define ERROR 0
typedef int Status;
//将队列初始化置为空
Status InitQueue(SeQueue *q)
{
    q->front = 0;
    q->rear = 0;
    return OK;
}
//判断队列是否为空
int EmptyQueue(SeQueue *q)
{
    if(q->front == q->rear)
        return 1;
    else
        return 0;
}
//获取队列长度
int LengthQueue(SeQueue q)
{
    return q.rear-q.front;          //队尾指针减去队头获得长度
}
//入队操作
Status EnQueue(SeQueue *q,ElemType e)
{
    if(q->rear == MAXSIZE)          //队满
    {
        printf("FULL\n");
        return ERROR;
    }
    q->data[q->rear] = e;           //入队操作
    q->rear++;                      //队尾指针向后移
    return OK;
}
//获取队头元素
Status FrontQueue(SeQueue *q,ElemType *e)
{
    if(EmptyQueue(q))               //队空
    {
        printf("Empty\n");
        return ERROR;
    }
    *e = q->data[q->front];
    return OK;
}
//出队操作
Status DeQueue(SeQueue *q,ElemType *e)
{
    if(EmptyQueue(q))
    {
        printf("Empty\n");
        return ERROR;
    }
    *e = q->data[q->front];         //出队操作
    q->front++;
    return OK;
}

缺点:

由于出队操作和入队操作中,头尾指针只会增加,所以会导致被删除元素无法被重新利用,所以有一种情况,当队列中元素个数少于最大空间时,但也会因为尾指针超过空间上限而不能做入队操作。所以这种队列还是很弱的,但是如果表示成循环队列就能克服这种情况了

循环队列

(脑补长成一条的队列被拗成环的样子)当队尾指针移动到空间的上限位置时,因为这是一个环,所以队尾指针还可以继续移动下去进入到数组最小下标的位置(用取模来实现),这样就可以最大限度的利用存储空间了。
还有一个要解决的问题就是如何判断队满,有两种解决方案:

  • 因为是环所以当头尾指针相等就是队满的情况了,但是一开始的
    时候如果头尾指针相等就是队空了,所以可以多开设一个一个变量
    来记录队空还是队满
  • 还有一个比较简便的方法就是空出一格来,这样队满和队空就可
    以被区分了(下面有这种来处理队满情况)

循环队列的具体操作:

循环队列也是用的顺序存储结构,所以类型定义和顺序队是基本一样的,而在部分运算操作上就有些不同了。

//队列长度
int LengthQueue(SeQueue q)
{
    int len = (q.rear-q.front+MAXSIZE)%MAXSIZE;
    return len;
}
//入队操作
Status EnQueue(SeQueue *q,ElemType e)
{
    if((q->rear+1)%MAXSIZE == q->front)//牺牲多一个空间来判断队满的情况
        return ERROR;
    q->data[rear] = e;              //入队
    q->rear = (q->rear+1)%MAXSIZE;  //尾指针加一并保证循环状态
    return OK;
}
//出队操作
Status DeQueue(SeQueue *q,ElemType *e)
{
    if((q->rear+1)%MAXSIZE == q->front)
        return ERROR;
    *e = q->data[q->front];         //出队
    q->front = (q->front+1)%MAXSIZE;//头指针加一并保证循环意义
}

链队

顾名思义,用链表来表示的队列被称为链队。链队的结点结构和单链表是一样的,只不过队列的插入和删除操作是分别在队尾和队头进行的。因此除了设置头指针外还要设置尾指针在指向队尾,为了方便,这里采用有头结点的单链表来表示队列

队列的链式存储结构类型定义

typedef int ElemType;       //数据类型
typedef struct node
{
    ElemType data;          //数据域
    struct node *next;      //指针域
}QueueNode;     
typedef struct
{
    QueueNode *front;       //头指针
    QueueNode *rear;        //尾指针
}LinkQueue;

链队运算的具体操作

//初始化队列置为空
Status InitQueue(LinkQueue *q)
{
    QueueNode *head;
    head = (QueueNode*)malloc(sizeof(QueueNode));//为头结点开辟存储空间
    if(!head)               //空间申请失败
        return ERROR;
    head->next = NULL;      //头结点指针域置为空
    q->front   = head;      //头指针指向头结点
    q->rear    = head;      //尾指针指向头结点
    return OK
}
//判断队列是否为空
Status EmptyQueue(LinkQueue *q)
{
    return q->rear == q->front;
}
//获取队头元素
Status FrontQueue(LinkQueue *q.ElemType *e)
{
    if(EmptyQueue(q))       //队列为空
        return ERROR;
    *e = q->front->data;
    return OK;
}
//元素入队
Status EnQueue(LinkQueue *q,ElemType e)
{
    QueueNode *s;
    s = (QueueNode*)malloc(sizeof(QueueNode));  //为新结点申请空间
    if(!s)                  //申请空间失败
        return ERROR;
    s->data = e;            //新结点数据域赋值
    s->next = NULL;         //新结点指针域置为空
    q->next->next = s;      //新结点插入到队尾
    q->rear = s;            //更新尾结点
    return OK;
}
//元素出队
Status DeQueue(LinkQueue *q,ElemType *e)
{
    if(EmptyQueue(q))           //队空
        return ERROR;
    QueueNode *s;
    s = q->front->next;         //s指向队头元素
    q->front->next = s->next;   //将s指向的元素移出队列
    if(q->rear = s)             //如果移出的元素是最后一个
        q->rear = q->front;     //队尾指针指向头结点
    *e = s->data;               //用e返回移出的元素值
    free(s);
    return OK;
    
}

你可能感兴趣的:(数据结构学习第二弹 栈与队列(2))