数据结构专题——队列

一、队列(queue)(可与栈对比进行学习)

思想:队列实现的是一种先进先出(first-in,first-out,FIFO)策略。(《算法导论》)

定义:队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表(具有线性关系/前驱后继关系)。(《大话数据结构》)

术语:

队列的两端:队头(head):进行删除操作的一端。队尾(tail):进行插入操作的一端。

操作:队列的插入操作(insert):入队(enqueue)。 队列的删除操作(delete):出队(dequeue)

空队列:不含任何数据元素的队列(empty queue)。(与此对应的应该有术语满队列(full queue),它们强调队列中数据状态)

队列下溢(underflow):对空队列进行出队操作。队列上溢(overflow):对满队列进行入队操作。

二、队列的抽象数据类型(ADT)

队列的主要方法实现/操作(主要参考《大话数据结构》):

1.初始化队列(InitQueue)

2.清空队列(ClearQueue)(将队列的数据清空,若内存是动态分配则释放内存)

3.判断队列是否为空队列(QueueEmpty)

4.获得队头元素(GetHead)

5.入队(EnQueue)

6.出队(DeQueue)

7.获得队列的元素个数(QueueLength)

队列的数据结构实现:(队列属于线性表这一类数据结构)

1.顺序队列:顺序表实现,采用顺序存储结构。(数据按照前驱后继关系在连续储存单元中存储,array implementation)

2.链队列:链表实现,采用链式存储结构。(数据通过指针或索引连接并在存储单元中不连续存储, linked-list implementation)

三、队列的思考

1.顺序表实现时采用循环队列(头尾相接的顺序存储结构)来节约队列存储空间和避免队列数据元素的大量移动。(实现:使用数组时引入指向队头和队尾的两个指针)

2.拓展:双端队列(deque):其插入和删除操作都可以在两端进行。在C++中含有STL实现deque(#include <deque>)(来自《算法导论》习题)

3.拓展:优先级队列。(来自《编程珠玑》)

4.打印机处理就是一个队列的简单应用。

四、算法C实现(C++可使用STL中queue: #include<queue>)

这里队列的顺序表实现采用静态数组,链表实现采用动态内存;

循环队列:顺序表实现(顺序存储结构):

1.顺序表实现采用循环队列而不是简单的顺序队列实现,避免数据元素整体前后移动,提高算法时间性能;

2.head指示队头数据在数组中的索引值,tail指示队尾数据在数组中的索引值加1(注意循环),队满时:(队尾tail + 1) %队列长度size = 队头head;空队时:队尾tail = 队头head;队列数据元素长度:(队尾tail - 队头head + 队列长度size) % 队列长度size

头文件 queue.h:

/**
 * @file queue.h
 * @brief queue data structure header.
 * The queue use sequence list.
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-01-21
 */

#ifndef __QUEUE_H__
#define __QUEUE_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #define NDEBUG
#include <assert.h>

/// function return state
#ifndef OK
#define OK        1
#endif
#ifndef ERROR
#define ERROR     0
#endif
#ifndef TRUE
#define TRUE      1
#endif
#ifndef FALSE
#define FALSE     0
#endif

/// data type, can be modified
typedef int Status;         ///< status data type
typedef int ElementType;    ///< element data type
typedef int CommonType;     ///< common data type

/// queue array maz length, can be modified
#define QUEUE_MAXSIZE      1000

/// queue data structure
typedef struct {
    ElementType data[QUEUE_MAXSIZE];   ///< queue elements
    CommonType head;                   ///< queue head data index
    CommonType tail;                   ///< queue tail data index
}Queue;

/// queue methods

/// initialize queue
void InitQueue(Queue* Q);
/// clear queue
void ClearQueue(Queue* Q);
/// detect if queue is empty
Status QueueEmpty(Queue* Q);
/// get queue length
CommonType QueueLength(Queue* Q);
/// get head element from the queue
Status GetHead(Queue* Q, ElementType* e);
/// insert element to the queue
Status EnQueue(Queue* Q, ElementType e);
/// delete element from the queue
Status DeQueue(Queue* Q, ElementType* e);

#endif  // __QUEUE_H__

实现文件 queue.c:

/**
 * @file queue.c
 * @brief queue method implements.
 * The methods use <assert.h> to help debug the program.
 * The queue use sequence list.
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-01-21
 */

#include "queue.h"

/**
 * @brief initialize the queue.
 *
 * @param[in,out]  Q     queue struct pointer
 *
 */
void InitQueue(Queue* Q)
{
    assert(Q != NULL);
    /// initialize index only, ignore the queue data
    Q->head = 0;
    Q->tail = 0;
}

/**
 * @brief clear the queue.
 *
 * @param[in,out]  Q     queue struct pointer
 *
 */
void ClearQueue(Queue* Q)
{
    assert(Q != NULL && Q->head >= 0 && Q->tail >= 0);
    /// set index only, ignore the queue data
    Q->head = 0;
    Q->tail = 0;
}

/**
 * @brief detect if the queue is empty.
 *
 * @param[in]      Q     queue struct pointer
 *
 * @return return TRUE if empty, else return FALQE
 */
Status QueueEmpty(Queue* Q)
{
    assert(Q != NULL && Q->head >= 0 && Q->tail >= 0);
    /// detect index value
    if (Q->head == Q->tail)
        return TRUE;
    else
        return FALSE;
}

/**
 * @brief  get queue length.
 *
 * @param[in]      Q     queue struct pointer
 *
 * @return queue length
 */
CommonType QueueLength(Queue* Q)
{
    assert(Q != NULL && Q->head >= 0 && Q->tail >= 0);
    return (Q->tail - Q->head + QUEUE_MAXSIZE) % QUEUE_MAXSIZE;
}

/**
 * @brief get head element from the queue.
 *
 * @param[in]      Q     queue struct pointer
 * @param[out]     e     the element
 *
 * @return return OK if success, else return ERROR
 */
Status GetHead(Queue* Q, ElementType* e)
{
    assert(Q != NULL && e != NULL && Q->head >= 0 && Q->tail >= 0);
    if (Q->head == Q->tail)
        return ERROR;
    /// get element from queue
    *e = Q->data[Q->head];
    return OK;
}

/**
 * @brief insert element to the queue.
 *
 * @param[in,out]  Q     queue struct pointer
 * @param[in]      e     the element to be insert
 *
 * @return return OK if success, else return ERROR
 */
Status EnQueue(Queue* Q, ElementType e)
{
    assert(Q != NULL && Q->head >= 0 && Q->tail >= 0);
    /// queue is full
    if ((Q->tail + 1) % QUEUE_MAXSIZE == Q->head)
        return ERROR;
    /// set data first and then increase tail index
    Q->data[Q->tail] = e;
    Q->tail = (Q->tail + 1) % QUEUE_MAXSIZE;
    return OK;
}

/**
 * @brief delete element from the queue.
 *
 * @param[in,out]  Q     queue struct pointer
 * @param[out]     e     the element to be deleted
 *
 * @return return OK and set e if success, else return ERROR
 */
Status DeQueue(Queue* Q, ElementType* e)
{
    assert(Q != NULL && e != NULL && Q->head >= 0 && Q->tail >= 0);
    /// queue is empty
    if (Q->tail == Q->head)
        return ERROR;
    /// get data first and then increase head index
    *e = Q->data[Q->head];
    Q->head = (Q->head + 1) % QUEUE_MAXSIZE;
    return OK;
}

链队列:链表实现(链式存储结构):

1.单链表的头部指针head为头结点,本身不存放数据,内部的next指向存放队头元素的结点,单链表的尾部指针tail存放队尾元素,内部的next指向NULL;

2.采用辅助变量count记录数据元素长度,避免遍历链表获取长度信息(不使用头结点的数据位是因为这个数据类型可能不足以存放长度)

头文件 queue.h:

/**
 * @file queue.h
 * @brief queue data structure header.
 * The queue use linked list.
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-01-21
 */

#ifndef __QUEUE_H__
#define __QUEUE_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #define NDEBUG
#include <assert.h>

/// function return state
#ifndef OK
#define OK        1
#endif
#ifndef ERROR
#define ERROR     0
#endif
#ifndef TRUE
#define TRUE      1
#endif
#ifndef FALSE
#define FALSE     0
#endif

/// data type, can be modified
typedef int Status;         ///< status data type
typedef int ElementType;    ///< element data type
typedef int CommonType;     ///< common data type

/// queue data structure
typedef struct QueueNode {
    ElementType data;                  ///< queue elements
    struct QueueNode* next;            ///< queue node pointer
}QueueNode, *QueuePtr;

typedef struct Queue {
    QueuePtr head;                     ///< queue head data pointer
    QueuePtr tail;                     ///< quaue tail data pointer
    CommonType count;                  ///< quaue data count
}Queue;

/// queue methods

/// initialize queue
void InitQueue(Queue* Q);
/// clear queue
void ClearQueue(Queue* Q);
/// detect if queue is empty
Status QueueEmpty(Queue* Q);
/// get queue length
CommonType QueueLength(Queue* Q);
/// get head element from the queue
Status GetHead(Queue* Q, ElementType* e);
/// insert element to the queue
Status EnQueue(Queue* Q, ElementType e);
/// delete element from the queue
Status DeQueue(Queue* Q, ElementType* e);

#endif  // __QUEUE_H__

实现文件 queue.c:

/**
 * @file queue.c
 * @brief queue method implements.
 * The methods use <assert.h> to help debug the program.
 * The queue use linked list.
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-01-21
 */

#include "queue.h"

/**
 * @brief initialize the queue.
 *
 * @param[in,out]  Q     queue struct pointer
 *
 */
void InitQueue(Queue* Q)
{
    assert(Q != NULL);
    if ((Q->head = (QueuePtr)malloc(sizeof(QueueNode))) == NULL) {
        assert(0);
        exit(EXIT_FAILURE);
    }
    Q->tail = Q->head;
    Q->head->next = NULL;
    Q->count = 0;
}

/**
 * @brief clear the queue.
 *
 * @param[in,out]  Q     queue struct pointer
 *
 */
void ClearQueue(Queue* Q)
{
    assert(Q != NULL && Q->count >= 0);
    /// set index only, ignore the queue data
    QueuePtr new_node = Q->head;
    while (new_node != Q->tail) {
        Q->head = new_node->next;
        free(new_node);
        new_node = Q->head;
    }
    Q->count = 0;
}

/**
 * @brief detect if the queue is empty.
 *
 * @param[in]      Q     queue struct pointer
 *
 * @return return TRUE if empty, else return FALQE
 */
Status QueueEmpty(Queue* Q)
{
    assert(Q != NULL && Q->count >= 0);
    /// detect index value
    if (Q->head == Q->tail)
        return TRUE;
    else
        return FALSE;
}

/**
 * @brief  get queue length.
 *
 * @param[in]      Q     queue struct pointer
 *
 * @return queue length
 */
CommonType QueueLength(Queue* Q)
{
    assert(Q != NULL && Q->count >= 0);
    return Q->count;
}

/**
 * @brief get head element from the queue.
 *
 * @param[in]      Q     queue struct pointer
 * @param[out]     e     the element
 *
 * @return return OK if success, else return ERROR
 */
Status GetHead(Queue* Q, ElementType* e)
{
    assert(Q != NULL && e != NULL && Q->count >= 0);
    if (Q->head == Q->tail)
        return ERROR;
    /// get element from queue
    *e = Q->head->next->data;
    return OK;
}

/**
 * @brief insert element to the queue.
 *
 * @param[in,out]  Q     queue struct pointer
 * @param[in]      e     the element to be insert
 *
 * @return return OK if success, else return ERROR
 */
Status EnQueue(Queue* Q, ElementType e)
{
    assert(Q != NULL && Q->count >= 0);
    QueuePtr new_node;
    if ((new_node = (QueuePtr)malloc(sizeof(QueueNode))) == NULL) {
        assert(0);
        exit(EXIT_FAILURE);
    }
    new_node->data = e;
    new_node->next = NULL;
    Q->tail->next = new_node;
    Q->tail = new_node;
    Q->count++;
    return OK;
}

/**
 * @brief delete element from the queue.
 *
 * @param[in,out]  Q     queue struct pointer
 * @param[out]     e     the element to be deleted
 *
 * @return return OK and set e if success, else return ERROR
 */
Status DeQueue(Queue* Q, ElementType* e)
{
    assert(Q != NULL && e != NULL && Q->count >= 0);
    /// queue is empty
    if (Q->tail == Q->head)
        return ERROR;
    QueuePtr old_node = Q->head->next;
    *e = old_node->data;
    Q->head->next = old_node->next;
    if (Q->tail == old_node)
        Q->tail = Q->head;
    free(old_node);
    Q->count--;
    return OK;
}

循环队列和链队列对比:

循环队列适用于数据元素个数的最大值可控的情况;

链队列适用于数据元素个数的最大值不可控的情况,因而外存放指针使内存开销增加;

入队和出队的时间复杂度都是O(1)。

五、实践——使用两个队列实现一个栈

思考:我们可以利用其中一个空队列作为缓存区,压栈时对非空的队列进行入队操作,入队的元素为压栈的元素;弹栈时将非空队列除去最后一个元素外的其他所有元素出队并入队到空队列中,最后元素再出队作为弹栈的元素。这种方案下,两个队列中总有一个空队列作为缓存区,每次弹栈都要对所有元素进行一组出队和入队操作(最后一个元素只进行出队操作),压栈的时间复杂度为O(1)(队列是否为空队列的检测不通过遍历队列而是通过count变量或辅助变量获得,或采用其他辅助标志位来判定哪个为空队列),而弹栈的时间复杂度为O(n)。

其他方案:上面的方案不固定哪个队列(A或B)为缓存区,只要为空队列即可。这个方案固定使用1个队列(如队列A)存放元素,另一个队列(如队列B)在没有操作的情况下保持为空队列,弹栈操作时,将除去队尾的元素由队列A入队到队列B,再将这些元素由队列B入队回队列A。这种方案效率不如上面的方案,因为弹栈时它多了一个从队列B将B中所有元素入队到队列A的操作,实际上时可以避免的。

实现时需注意:

1.两各队列均为空时弹栈错误处理,压栈时可任意采用一个(也可指定使用空队列1);非空队列为满队列时压栈错误处理。(主要思考的方向是队列为空队列和满队列这两种特殊状态)

2.调用栈的各个方法实现队列各个方法,而不是对队列的数据结构含的变量直接进行处理(假设队列的数据结构中的数据是封闭的,只有方法可以调用)。

算法C实现:

头文件 stack_by_queue.h(这里除了队列使用的数据结构和不同于普通队列外,方法实现的函数接口均一致):

/**
 * @file stack_by_stack.h
 * @brief implement of a stack by two queues.
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-01-22
 */
#ifndef __STACK_BY_QUEUE_H__
#define __STACK_BY_QUEUE_H__

#include "queue.h"

/// stack data structure by two queue
typedef struct Stack {
    Queue q1;
    Queue q2;
}Stack;

/// stack methods

/// initialize stack
void InitStack(Stack* S);
/// clear stack
void ClearStack(Stack* S);
/// detect if stack is empty
Status StackEmpty(Stack* S);
/// get stack length
CommonType StackLength(Stack* S);
/// get top element from the stack
Status GetTop(Stack* S, ElementType* e);
/// push element to the stack
Status Push(Stack* S, ElementType e);
/// pop element from the stack
Status Pop(Stack* S, ElementType* e);

#endif  // __STACK_BY_QUEUE_H__

实现文件 stack_by_queue.c:

/**
 * @file stack_by_queue.c
 * @brief stack method implements.
 * The methods use <assert.h> to help debug the program.
 * The stack use two queues.
 * @author chenxilinsidney
 * @version 1.0
 * @date 2015-01-21
 */

#include "stack_by_queue.h"

/**
 * @brief initialize the stack.
 *
 * @param[in,out]  S     stack struct pointer
 *
 */
void InitStack(Stack* S)
{
    assert(S != NULL);
    InitQueue(&S->q1);
    InitQueue(&S->q2);
}

/**
 * @brief clear the stack.
 *
 * @param[in,out]  S     stack struct pointer
 *
 */
void ClearStack(Stack* S)
{
    assert(S != NULL);
    ClearQueue(&S->q1);
    ClearQueue(&S->q2);
}

/**
 * @brief detect if the stack is empty.
 *
 * @param[in]      S     stack struct pointer
 *
 * @return return TRUE if empty, else return FALSE
 */
Status StackEmpty(Stack* S)
{
    assert(S != NULL);
    if (QueueEmpty(&S->q1) && QueueEmpty(&S->q2))
        return TRUE;
    else
        return FALSE;
}

/**
 * @brief  get stack length.
 *
 * @param[in]      S     stack struct pointer
 *
 * @return stack length
 */
CommonType StackLength(Stack* S)
{
    assert(S != NULL);
    return QueueLength(&S->q1) + QueueLength(&S->q2);
}

/**
 * @brief get top element from the stack.
 *
 * @param[in]      S     stack struct pointer
 * @param[out]     e     the element
 *
 * @return return OK if success, else return ERROR
 */
Status GetTop(Stack* S, ElementType* e)
{
    assert(S != NULL && e != NULL);
    if (QueueEmpty(&S->q1) == FALSE) {
        while (DeQueue(&S->q1, e)) {
            EnQueue(&S->q2, *e);
        }
        return OK;
    } else if (QueueEmpty(&S->q2) == FALSE) {
        while (DeQueue(&S->q2, e)) {
            EnQueue(&S->q1, *e);
        }
        return OK;
    } else {
        return ERROR;
    }
}

/**
 * @brief push element to the stack.
 *
 * @param[in,out]  S     stack struct pointer
 * @param[in]      e     the element to be insert
 *
 * @return return OK if success, else return ERROR
 */
Status Push(Stack* S, ElementType e)
{
    assert(S != NULL);
    if (QueueEmpty(&S->q1) == FALSE) {
        return EnQueue(&S->q1, e);
    } else {
        return EnQueue(&S->q2, e);
    }
}

/**
 * @brief pop element from the stack.
 *
 * @param[in,out]  S     stack struct pointer
 * @param[out]     e     the element to be deleted
 *
 * @return return OK and set e if success, else return ERROR
 */
Status Pop(Stack* S, ElementType* e)
{
    assert(S != NULL && e != NULL);
    if (QueueEmpty(&S->q1) == FALSE) {
        CommonType keep_one = QueueLength(&S->q1) - 1;
        while (keep_one--) {
            DeQueue(&S->q1, e);
            EnQueue(&S->q2, *e);
        }
        DeQueue(&S->q1, e);
        return OK;
    } else if (QueueEmpty(&S->q2) == FALSE) {
        CommonType keep_one = QueueLength(&S->q2) - 1;
        while (keep_one--) {
            DeQueue(&S->q2, e);
            EnQueue(&S->q1, *e);
        }
        DeQueue(&S->q2, e);
        return OK;
    } else {
        return ERROR;
    }
}

六、优先队列

(待学习《编程珠玑》补充)

六、总结

  关于队列的知识点和栈一样本身不多,主要是理解队列的先进先出的思想,能够结合实际的问题综合这种思想进行应用和法实现。

时间: 2024-10-11 15:33:51

数据结构专题——队列的相关文章

数据结构专题——队列的应用 A1056.Mice and Rice ( 25)

#include <bits/stdc++.h> #include<math.h> #include <string> using namespace std; const int maxn = 1010; struct mouse{ int weight;//质量 int R;//排名 }mouse[maxn]; int main(){ int np,ng,order; scanf("%d%d",&np,&ng); for(int

线段树--数据结构专题学习

这两周是数据结构专题的学习,,被专题的题目虐得死去活来== 线段树:简单的说就是把[1,n]的区间二分,[1,(1+n)/2]左子树,[(1+n)/2+1,n]右子树 就这样一直分下去,直到都是[x,x]这样的区间.这样就构成了一颗树了^-^ 有这样一棵树,我们就可以在节点中储存区间的和啊,区间内的最大值啊,最小值等等..这就是线段树的附加信息了,也是题目中的重点.. 我们可以用一个数组(长度为k)储存原区间的初始值,然后根据这个建树,所以这个树的节点数最多为4*K: 对于每个节点i,其左子树为

2014 UESTC暑前集训数据结构专题解题报告

A.Islands 这种联通块的问题一看就知道是并查集的思想. 做法:从高水位到低水位依序进行操作,这样每次都有新的块浮出水面,可以在前面的基础上进行合并集合的操作.给每个位置分配一个数字,方便合并集合.同时将这些数字也排一个序,降低枚举的复杂度.合并集合时向四周查询浮出水面但是没有合并到同一集合的点进行合并. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath&

简单数据结构之队列模拟

1 /************************************************************************************** 2 * Function : 模拟队列 3 * Create Date : 2014/04/23 4 * Author : NTSK13 5 * Email : [email protected] 6 * Copyright : 欢迎大家和我一起交流学习,转载请保持源文件的完整性. 7 * 任何单位和个人不经本人允许不

数据结构 - 链队列的实行(C语言)

数据结构-链队列的实现 1 链队列的定义 队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已, 我们把它简称为链队列.为了操作上的方便,我们将队头指针指向链队列的头结点,而队尾指针指向终端结点,如下图所示. 空队列时,front和rear都指向头结点,如下图所示. 链队列的结构为: typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */ typedef struct QNode /* 结点结构 */ { QElemTy

数据结构 - 顺序队列的实行(C语言)

数据结构-顺序队列的实现 1 顺序队列的定义 线性表有顺序存储和链式存储,队列作为一种特殊的线性表,也同样存在这两种存储方式.我们先来看队列的顺序存储结构. 队列的顺序储存结构:用数组存储队列,为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针:front指针指向队头元素,rear指针指向队尾元素的下一个位置,当front=rear时,为空队列,结构如下图所示. 假设是长度为5的数组,初始状态,空队列如下图左所示,front与 rear指针均指向下标为0的位置.然后入队a

数据结构之队列and栈总结分析

一.前言: 数据结构中队列和栈也是常见的两个数据结构,队列和栈在实际使用场景上也是相辅相成的,下面简单总结一下,如有不对之处,多多指点交流,谢谢. 二.队列简介 队列顾名思义就是排队的意思,根据我们的实际生活不难理解,排队就是有先后顺序,先到先得,其实在程序数据结构中的队列其效果也是一样,及先进先出.    队列大概有如下一些特性: 1.操作灵活,在初始化时不需要指定其长度,其长度自动增加(默认长度为32) 注:在实际使用中,如果事先能够预估其长度,那么在初始化时指定长度,可以提高效率    

数据结构之队列【转】

转自http://www.cnblogs.com/kaituorensheng/archive/2013/02/28/2937865.html 队列特性:先进先出(FIFO)——先进队列的元素先出队列.来源于我们生活中的队列(先排队的先办完事). 队列有下面几个操作: InitQueue() ——初始化队列 EnQueue()        ——进队列 DeQueue()        ——出队列 IsQueueEmpty()——判断队列是否为空 IsQueueFull()    ——判断队列是

数据结构之队列c代码实现

一学期的数据结构,总不能毫无收获吧,因为书上的很多代码并不能实现,我一个编程小白可怎么过呢,难得假期有时间,于是我寻求度娘,从四面八方找了些可以编译通过的源码,这一次是队列,后面我还会逐渐补充,如果有什么差错,欢迎大佬们来留言啊. 交一下代码: 1 #include<stdio.h> 2 #include<stdlib.h> 3 #define QUEEN_SIZE 50 4 5 typedef struct SeqQueue 6 { 7 int data[QUEEN_SIZE];