数据结构-王道2017-第3章 栈和队列-队列

1.队列简称队,也是一种操作受限的线性表,只允许在表的一端插入,而在表的另一端删除 先进先出FIFO,不能随便读取队列中间的某个数据

   队头(Front):允许删除的一端,又称为队首

   队尾(rear):允许插入的一端

   空队列:不含任何元素的空表

   基本操作: InitQueue(&Q),QueueEmpty(Q),EnQueue(&Q,x),DeQueue(&Q,&x),GetHead(Q,&x)

2.队列的顺序存储

 设队头指针指向队头元素,队尾指针指向队尾元素的下一个位置(队头指针指向队头元素的钱一个位置,队尾指针指向队尾也可以)

typedef struct {
    ElemType data[MaxSize];
    int front, rear;
}SqQueue;

队空条件:Q.front == Q.rear == 0;

 但是队满条件不能是Q.rear==MaxSize,因为当front == Q.rear-1时,仍然妈祖条件,但此时队中只有一个元素,此时入队出现“上溢出”,但这种溢出并不是真正的溢出,这是顺序队列的缺点

3.循环队列

  当队首指针Q.front=MaxSize-1后,再前进一个位置就自动到0,这可以利用除法取余运算(%)来实现

  初始时:Q.front=Q.rear=0

  队首指针进1:Q.front=(Q.front+1)%MaxSize;

  队尾指针进1:Q.rear=(Q.rear+1)%MaxSize;    

  队列长度: (Q.rear+MaxSize - Q.front)%MaxSize;    //加上MaxSize的原因是Q.rear可能循环到Q.front的左边,这时长度就是Q.rear+MaxSize - Q.front,%MaxSize的目的是Q.rear在Q.front的右边时,加上MaxSize就不是准确值了,所以取模,而取模对前一种情况是没有影响的

 出队入队时:指针都按顺时针方向进1

 且队空的条件为Q.front=Q.rear,而队满的条件也是Q.front=Q.rear(队尾指针追上了队首指针),解决方案:

    1)牺牲一个单元来区分队空队满,入队时少用一个队列单元,这是一种较为普遍的做法,约定以“队头指针在队尾指针的下一个位置作为队满的标志”。

           队满条件:(Q.rear+1)%MaxSize==Q.front 

           队空条件:Q.front==Q.rear

           队列中元素的个数:(Q.rear-Q.front+MaxSize)%MaxSize

    2) 类型中增设表示元素个数的数据成员。这样队空的条件为Q.size == 0;队满的条件为Q.size==MaxSize,两种情况都有Q.front==Q.rear

    3) 类型中增设tag数据成员,以区分堆满还是队空。tag等于0的情况下,若因删除导致Q.front==Q.rear则为队空,tag等于1的情况下,若因插入导致Q.front==Q.rear则为队满

3.循环队列的操作

//队列的顺序存储结构
const int MaxSize = 50;

typedef int ElemType;


typedef struct {
    ElemType data[MaxSize];
    int front, rear;
}SqQueue;

void InitQueue(SqQueue &q) {
    q.front = q.front = 0;
}

bool QueueEmpty(SqQueue q) {
    return q.rear == q.front;
}

bool EnQueue(SqQueue &q,ElemType x) {
    if ((q.rear + 1) % MaxSize == q.front)
        return false;
    q.data[q.rear] = x;
    q.rear = (q.rear + 1) % MaxSize;
    return true;
}

bool DeQueue(SqQueue &q, ElemType &x) {
    if (q.front == q.rear)   //队列为空
        return false;
    x = q.data[q.front];
    q.front = (q.front + 1) % MaxSize;
    return true;
}

 4.队列的链式存储结构

typedef struct{
    ElemType data;
    LinkNode *next;
}LinkNode;    //注意不是指针

typedef struct {
    LinkNode *front, *rear;
}LinkQueue;

队列的判空条件为:Q.front==NULL&&Q.rear==NULL,通常将链式队列设计成一个带头节点的单链表,这样插入和删除操作就统一了,不存在队满且产生溢出的问题

//队列的顺序存储结构
const int MaxSize = 50;

typedef int ElemType;

typedef struct{
    ElemType data;
    LinkNode *next;
}LinkNode;    //注意不是指针

typedef struct {
    LinkNode *front, *rear;
}LinkQueue;

void InitQueue(LinkQueue &q) {
    q.rear = q.front = (LinkNode*)malloc(sizeof(LinkNode));
    q.front->next = NULL;
}

bool QueueEmpty(LinkQueue q) {
    return q.rear == q.front;
}

void EnQueue(LinkQueue &q,ElemType x) {
    LinkNode *p = (LinkNode*)malloc(sizeof(LinkNode));
    p->data = x;
    p->next = NULL;
    q.rear->next = p;
    q.rear = p;
}

bool DeQueue(LinkQueue &q, ElemType &x) {
    if (q.rear == q.front)
        return false;
    LinkNode *p = q.front->next;
    x = p->data;
    q.front->next = p->next;
    if (p == q.front)    //只有一个元素的时候要置尾指针为空
        q.rear = q.front;    
    free(p);
    return true;
}

 5.双端队列

  双端队列:指允许两段都可以进行入队和出队操作的队列,其元素的逻辑结构仍然是线性结构,两端分别称为前端和后端

  输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许插入的双端队列称为输出受限的双端队列

  输入受限的双端队列:允许在一端进行插入和删除,但在另一端只允许删除的双端队列称为输入受限的双端队列 

  如果限定双端队列从某个端点插入的元素只能从该端点删除,则该双端队列就蜕变为两个栈底相邻接的栈了

    

6.试题精选

  1)栈和队列都只能顺序存取

  2)进队操作,只有尾指针变化,出队操作除了队中只有一个元素外(也会更改rear指针),只会更改front指针

posted @ 2017-03-15 15:24  陈煜弘  阅读(371)  评论(0编辑  收藏  举报