队列的顺序及链式实现

队列定义

  队列的工作原理和现实中的队列完全一致,比如排队上车,排前面的先上车,排后面的后上车。队列只支持两种操作,入队和出队,是一种先进先出的数据结构(First In First Out,FIFO),顺序和链式两种结构的队列如下:
队列存储结构

队列基本操作

  1. 初始化队列 initQueue():初始化队列空间及队首队尾指针 head、tail
  2. 判断队列是否为空 isEmpty():当队首、队尾相等时,队为空,即 head == tail 是返回真,否返回假
  3. 入队 enQueue():将元素从队尾 tail 处加入队列,并且更新队尾指针,即 tail++
  4. 出队 delQueue():从队首元素开始出队,并且更新队首指针,即 head++
  5. 读队首元素 getHeadValue():读取队首元素的值

顺序队列实现

#include <stdio.h>
#include <string.h>
#define true 1
#define false 0
#define MAX_SIZE 100

typedef struct {
    char data[MAX_SIZE];
    int head;
    int tail;
}Queue;

void initQueue(Queue *queue){
    memset(queue -> data, 0, MAX_SIZE);
    queue -> head = 0;
    queue -> tail = 0;
    printf("######## 队列初始化完成! ########\n");
}

int isEmpty(Queue *queue){
    if(queue -> head == queue -> tail) {
    printf("完成出队! Queue -> head = Queue -> tail = %d\n", queue -> head);
        return true;
    }
    return false;
}

char getHeadValue(Queue *queue){
    return queue -> data[queue -> head];
}

void enQueue(Queue *queue, char value){
    queue -> data[queue -> tail] = value;
    queue -> tail++;
    printf("元素 %c 入队!\n",queue -> data[queue -> tail - 1]);
}

void delQueue(Queue *queue){
    printf("元素 %c 将出队!\n",getHeadValue(queue));
    queue -> data[queue -> head] = 0;
    queue -> head++;
}

int main()
{
    Queue Q;
    initQueue(&Q);
    char str[MAX_SIZE] = {0};
    printf("请输入数据,按Enter结束!\n");
    scanf("%s", str);
    int i;
    printf("######### 开始入队 #########\n");
    for(i = 0; i < strlen(str); i++) {
        enQueue(&Q, str[i]);
    }
    printf("######### 开始出队 #########\n");
    while(1) {
        if(isEmpty(&Q)) {
            break;
        }
        delQueue(&Q);
    }
    return 0;
}

该程序模拟队列基本操作,执行过程如下:
顺序队列

链式队列实现

链式队列通过链表来存储数据,因此需要建立一个链表结构(关于链表看这里),在此基础上再建立队列结构,定义如下:

typedef struct ListNode{
    char data;
    struct ListNode *next;
}Lnode;

typedef struct ListQueue{
    Lnode *head;
    Lnode *tail;
}Lqueue;
初始化链式队列

链式队列头尾指针指向同一个空节点

void Init_ListQueue(Lqueue *queue) {
    queue -> head = queue -> tail = (Lnode *)malloc(sizeof(Lnode));
    if(queue -> head == NULL) {
        printf("Malloc Failed!\n");
        exit(0);
    }
    queue -> head -> next = NULL;
}
元素入队

由队尾添加元素,队尾指针始终指向新增节点(node)的地址,移动 tail 指针

void Push_ListValue(Lqueue *queue, char value) {
    Lnode *node = (Lnode *)malloc(sizeof(Lnode));
    node -> data = value;
    node -> next = NULL;
    queue -> tail -> next = node;
    queue -> tail = queue -> tail -> next;
    printf("Push: %c\n", queue -> tail -> data);
}
元素出队

移动队头指针出队,head -> next 指向第一个节点地址,当 head -> next 等于队尾指针时,已到链队末尾,队尾指针出队,出队结束,释放 head -> next 空间,链队空!

void Pop_ListValue(Lqueue *queue) {
    if(queue -> head -> next == queue -> tail) {
        printf("Pop: %c\n", queue -> tail -> data);
        free(queue -> head -> next);
        queue -> head -> next = NULL;
        printf("队列已空!\n");
        return;
    }
    else {
        Lnode *tmp = queue -> head -> next;
        printf("Pop: %c\n", tmp -> data);
        queue -> head -> next = tmp -> next;
        free(tmp);
        tmp = NULL;
    }
}
测试代码

当队列为空时,需要释放头节点指针 LQ.head. 此处多一次Pop操作来验证队列是否已释放空

void test_1() {
    Lqueue LQ;
    Init_ListQueue(&LQ);
    Push_ListValue(&LQ, 'A');
    Push_ListValue(&LQ, 'B');
    Push_ListValue(&LQ, 'C');
    Pop_ListValue(&LQ);
    Pop_ListValue(&LQ);
    Pop_ListValue(&LQ);
    Pop_ListValue(&LQ);
    free(LQ.head);
}

执行结果打印如下:

Push: A
Push: B
Push: C
Pop: A
Pop: B
Pop: C
队列已空!

循环队列

  循环队列最多存储(MAXSIZE - 1)个队列元素,因此当循环队列中只剩下一个空存储单元时,说明队列已经满了,即队列判满条件是 head = (tail + 1) % MAXSIZE;队列判空的条件是 head == tail;
在实际开发中,比较常见的队列是循环双端队列,就是队列两端都可以进出的队列,如下图:
   双端队列
对于循环双端队列的实现,Leetcode上面有一道题目,小编之前有总结一篇博客,这里贴出链接C语言实现循环双端队列
好了,关于队列的学习总结就到这了,之后有新的感悟再做更新…

参考文章

关于链式队列的一篇博客

发布了52 篇原创文章 · 获赞 81 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/xiaoma_2018/article/details/103837088