数据结构与算法-队列

1. 顺序存储

在使用队列时,我们使用两个变量表示队列的头和尾。

以长度为5的顺序队列为例:

  1. 开始队列头Q.front和队列尾Q.rear相等为0,且在数组头部;
  2. C1、C2、C3入队,Q.front不动,Q.rear指向3。
  3. C1、C2出队,Q.rear不动,Q.front指向2。
单向队列

之前使用过两个位置,我们无法访问,造成了资源的浪费。

如果将Q.frontQ.rear范围的数据移动倒表头,可以解决这个问题,但是这样就太麻烦了。

所以提出了循环队列的概念。

循环队列

Q.rear到达队尾时,会来到表头位置,如上图(a)所示。

但是这样又有了一个新问题:

  • 空队列时,Q.frontQ.rear相等,如上图(c)所示。
  • 当队列被占满,Q.frontQ.rear也相等,如上图(b)所示。

这样就没法判断队列是空队列还是满队列了。

这里,我们牺牲一个存储单元,当Q.rearQ.front之间只空一个存储单元时认为队列是满的,就可以解决这个问题。

1.1 结构定义

#define MAXSIZE 10

typedef int Status;
typedef int QElemType;

typedef struct
{
    QElemType data[MAXSIZE];
    int front;
    int rear;
} SqQueue;

1.2 函数实现

// 1.构建一个空队列S
Status InitQueue(SqQueue *Q) {
    Q->front = Q->rear = 0;
    return OK;
}

// 2.将队列置空
Status ClearQueue(SqQueue *Q) {
    Q->front = Q->rear = 0;
    return OK;
}

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

// 4.返回队列的长度
int QueueLength(SqQueue Q){
    return (Q.rear + MAXSIZE - Q.front) % MAXSIZE;
}

// 5.获取队列头元素
Status QueueFront(SqQueue Q, QElemType *e){
    if (Q.rear == Q.front) // 队列是空的
        return ERROR;
    else
        *e = Q.data[Q.front];
    return OK;
}

// 6.入队
Status Enqueue(SqQueue *Q, QElemType e){
    if ((Q->rear + 1) % MAXSIZE == Q->front) return ERROR;//队列满了
    Q->data[Q->rear] = e;
    Q->rear = (Q->rear + 1) % MAXSIZE;
    return OK;
}

// 7.出队
Status Dequeue(SqQueue *Q, QElemType *e){
    if (Q->rear == Q->front) return ERROR; // 队列是空的
    *e = Q->data[Q->front];
    Q->front = (Q->front + 1) % MAXSIZE;
    return OK;
}

// 8.从队列头开始遍历
Status QueueTraverse(SqQueue Q){
    int i = Q.front;
    while (i != Q.rear) {
        printf("%d ", Q.data[i]);
        i = (i + 1) % MAXSIZE;
    }
    printf("\n");
    return OK;
}

int main(int argc, const char * argv[]) {
    SqQueue Q;
    int e;
    if (InitQueue(&Q) == OK) {
        for (int j = 1 ; j < 11; j++) {
            Enqueue(&Q, j);
        }
    }
    printf("循环队列中元素为: \n");
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Dequeue(&Q, &e);
    printf("出队: %d\n", e);
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Enqueue(&Q, e);
    printf("入队: %d\n", e);
    QueueTraverse(Q);
    printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
    QueueFront(Q, &e);
    printf("队列头元素: %d \n", e);
    printf("队列长度: %d\n", QueueLength(Q));
    ClearQueue(&Q);
    printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
    printf("队列长度: %d\n", QueueLength(Q));
    
    return 0;
}
// 输出
循环队列中元素为: 
1 2 3 4 5 6 7 8 9 
队列长度: 9
出队: 1
2 3 4 5 6 7 8 9 
队列长度: 8
入队: 1
2 3 4 5 6 7 8 9 1 
是否为空队列: 0 (1:空 0:否)
队列头元素: 2 
队列长度: 9
是否已经清空队列 1 (1:空 0:否)
队列长度: 0

2. 链式存储

链式存储不会遇到顺序存储的问题,所以直接使用单向链表就可以实现。

2.1 结构定义

typedef int Status;
typedef int QElemType;

/* 链栈结构 */
typedef struct QueueNode
{
    QElemType data;
    struct QueueNode *next;
} QueueNode, *QueueNodePtr;

typedef struct
{
    QueueNodePtr front;
    QueueNodePtr rear;
    int count;
} LinkQueue;

2.2 函数实现

无头结点
// 1.构建一个空队列S
Status InitQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    Q->front = Q->rear = NULL;
    Q->count = 0;
    return OK;
}

// 2.将队列置空
Status ClearQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    QueueNodePtr p, q;
    p = Q->front;
    while (p) {
        q = p;
        p = p->next;
        free(q);
    }
    Q->front = Q->rear = NULL;
    Q->count = 0;
    return OK;
}

// 3.判断队列是否为空;
Status QueueEmpty(LinkQueue Q) {
    if (!Q.front)
        return TRUE;
    else
        return FALSE;
}

// 4.返回队列的长度
int QueueLength(LinkQueue Q) {
    return Q.count;
}

// 5.获取队列头元素
Status QueueFront(LinkQueue Q, QElemType *e) {
    if (!Q.front) // 队列是空的
        return ERROR;
    else
        *e = Q.front->data;
    return OK;
}

// 6.入队
Status Enqueue(LinkQueue *Q, QElemType e) {
    if (!Q) return ERROR;
    QueueNodePtr node = (QueueNodePtr)malloc(sizeof(QueueNode));
    if (!node) return ERROR; //没有空间了
    node->data = e;
    node->next = NULL;
    if (!Q->front) Q->front = node;
    if (Q->rear) Q->rear->next = node;
    Q->rear = node;
    Q->count++;
    return OK;
}

// 7.出队
Status Dequeue(LinkQueue *Q, QElemType *e) {
    if (!Q || !Q->front) return ERROR; // 队列是空的
    QueueNodePtr node = Q->front;
    Q->front = node->next;
    if (!Q->front) Q->rear = NULL; // 队列空了
    Q->count--;
    *e = node->data;
    free(node);
    return OK;
}

// 8.从队列头开始遍历
Status QueueTraverse(LinkQueue Q) {
    QueueNodePtr node = Q.front;
    while (node) {
        printf("%d ", node->data);
        node = node->next;
    }
    printf("\n");
    return OK;
}

int main(int argc, const char * argv[]) {
    LinkQueue Q;
    int e;
    if (InitQueue(&Q) == OK) {
        for (int j = 1 ; j < 11; j++) {
            Enqueue(&Q, j);
        }
    }
    printf("链式队列中元素为: \n");
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Dequeue(&Q, &e);
    printf("出队: %d\n", e);
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Enqueue(&Q, e);
    printf("入队: %d\n", e);
    QueueTraverse(Q);
    printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
    QueueFront(Q, &e);
    printf("队列头元素: %d \n", e);
    printf("队列长度: %d\n", QueueLength(Q));
    ClearQueue(&Q);
    printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
    printf("队列长度: %d\n", QueueLength(Q));
    return 0;
}

// 输出
链式队列中元素为: 
1 2 3 4 5 6 7 8 9 10 
队列长度: 10
出队: 1
2 3 4 5 6 7 8 9 10 
队列长度: 9
入队: 1
2 3 4 5 6 7 8 9 10 1 
是否为空队列: 0 (1:空 0:否)
队列头元素: 2 
队列长度: 10
是否已经清空队列 1 (1:空 0:否)
队列长度: 0
有头节点
// 1.构建一个空队列S
Status InitQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    Q->front = Q->rear = (QueueNodePtr)malloc(sizeof(QueueNode)); //生成头结点
    if (!Q->front) return ERROR;
    Q->front->next = NULL;
    return OK;
}

// 2.销毁队列(要包括头结点)
Status DestoryQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    while (Q->front) {
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    Q->count = 0;
    return OK;
}

// 3.将队列置空(不释放头结点)
Status ClearQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    QueueNodePtr p, q;
    Q->rear = Q->front; // 都指向头结点
    p = Q->front->next; // 释放之后的节点
    Q->front->next = NULL;
    while (p) {
        q = p;
        p = p->next;
        free(q);
    }
    Q->count = 0;
    return OK;
}

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

// 5.返回队列的长度
int QueueLength(LinkQueue Q) {
    return Q.count;
}

// 6.获取队列头元素
Status QueueFront(LinkQueue Q, QElemType *e) {
    if (Q.front == Q.rear) { // 队列是空的
        return FALSE;
    }
    *e = Q.front->next->data;
    return TRUE;
}

// 7.入队
Status Enqueue(LinkQueue *Q, QElemType e) {
    if (!Q) return ERROR;
    QueueNodePtr node = (QueueNodePtr)malloc(sizeof(QueueNode));
    if (!node) return ERROR;
    node->data = e;
    node->next = NULL;
    Q->rear->next = node;
    Q->rear = node;
    Q->count++;
    return OK;
}

// 8.出队
Status Dequeue(LinkQueue *Q, QElemType *e) {
    if (!Q || Q->front == Q->rear) return ERROR; // 队列是空的
    QueueNodePtr node = Q->front->next;
    Q->front->next = node->next;
    Q->count--;
    *e = node->data;
    if (Q->rear == node) Q->rear = Q->front; // *删空后将rear指向头结点
    free(node);
    return OK;
}

// 9.从队列头开始遍历
Status QueueTraverse(LinkQueue Q) {
    QueueNodePtr node = Q.front->next;
    while (node) {
        printf("%d ", node->data);
        node = node->next;
    }
    printf("\n");
    return OK;
}

int main(int argc, const char * argv[]) {
    LinkQueue Q;
    int e;
    if (InitQueue(&Q) == OK) {
        for (int j = 1 ; j < 11; j++) {
            Enqueue(&Q, j);
        }
    }
    printf("链式队列中元素为: \n");
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Dequeue(&Q, &e);
    printf("出队: %d\n", e);
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Enqueue(&Q, e);
    printf("入队: %d\n", e);
    QueueTraverse(Q);
    printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
    QueueFront(Q, &e);
    printf("队列头元素: %d \n", e);
    printf("队列长度: %d\n", QueueLength(Q));
    ClearQueue(&Q);
    printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
    printf("队列长度: %d\n", QueueLength(Q));
    return 0;
}
// 输出
链式队列中元素为: 
1 2 3 4 5 6 7 8 9 10 
队列长度: 10
出队: 1
2 3 4 5 6 7 8 9 10 
队列长度: 9
入队: 1
2 3 4 5 6 7 8 9 10 1 
是否为空队列: 0 (1:空 0:否)
队列头元素: 2 
队列长度: 10
是否已经清空队列 1 (1:空 0:否)
队列长度: 0

你可能感兴趣的:(数据结构与算法-队列)