数据结构与算法(6)- 队列

686 阅读7分钟

队列的概念

队列我们可以理解为单行道的马路上的车辆,通过收费站时,排在前面的车辆先通过,排在后面的车辆后通过,通过收费站的我们叫出队列,后面新加入队列的叫入队。

队列的特点:先进先出,队列也是一种操作受限的线性表数据结构 它具有先进先出的特性,支持队尾插入元素,在队头删除元素。

队列的两个基本操作:

入队 将一个数据放到队列尾部;出队 从队列的头部取出一个元素。

顺序队列

队列示意图:

队列表示与操作实现:

  • 图(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;
}

打印: