数据结构(八) -- C语言版 -- 栈和队列 - 队列的设计与实现

零、读前说明

  • 本文中没有涉及到很多的相关理论知识,也没有做深入的了解,所以,您如果是想要系统的学习、想要多学习关于理论的知识等,那么本文可能并不合适您。
  • 本文中所有设计的代码均通过测试,并且在功能性方面均实现应有的功能。
  • 设计的代码并非全部公开,部分无关紧要代码并没有贴出来。
  • 如果你也对此感兴趣、也想测试源码的话,可以私聊我,非常欢迎一起探讨学习。
  • 由于时间、水平、精力有限,文中难免会出现不准确、甚至错误的地方,也很欢迎大佬看见的话批评指正。
  • 嘻嘻。。。。 。。。。。。。。收!

一、队列的概述

  在使用的电脑的过程中,有时候会出现在敲键盘的突然间没有任何现象,然后又突然间出现一串刚才输入的字母,这其实就是在系统中好多个程序在运行的过程中需要一个通道,按照先后的顺序排队等待造成的。
  还有在排队买自己喜欢的奶茶的时候,看着前面的一个一个慢慢往前挪心里着实着急的不行,但是却只能按照这个次序一步一步的来(当然要是面子大插队的,只能给你说 :嘻嘻,QNMD,杠精!!!)。
  上面这种通道的情况可以用先进先出的排队功能,其实就是队列啦。

  队列是一种特殊的线性表,它只允许在线性表的前端(front)进行删除操作,而在线性表的后端(rear)进行插入操作。

  简单总结为:

  • 队列也是一种特殊的线性表,是一种操作受限制的线性表。
  • 队列仅在线性表的两端进行操作
      队列头部:从获取队列中数据元素的一端 – 出队
      队列尾部:往队列中插入数据元素的一端 – 入队

二、队列的操作

  和栈的一样,队列的操作主要也包括下面这些。

  • 创建队列 SeqQueue_Create
  • 清空队列 SeqQueue_Clear
  • 销毁队列 SeqQueue_Destory
  • 入队列 SeqQueue_Append
  • 出队列 SeqQueue_Subtract
  • 获取队列头部元素 SeqQueue_Header
  • 获取队列长度 SeqQueue_Length

  如果是用顺序表模拟的队列,那么还需要做下队列最大容量的问题,所以额外添加一个接口

  • 获取队列的最大容量 SeqQueue_Capacity

三、队列的两种存储结构的模型概述

  线性表有顺序存储和链式储存,队列 作为一种特殊的线性表,也有同样的存储结构。
  下面就简单说明一下两种存储结构形式的队列模型。
  顺序存储结构链式存储结构的模型的队列,也存在着左右先后的选择的问题。
在这里插入图片描述
在这里插入图片描述
  只不过,这次的选择貌似并不是那么的难。

四、顺序存储结构的队列及实现

4.1、顺序存储结构的传统队列简易实现与测试

  那么先呢来来热热身体,为了能持久做点铺垫,一个简单的传统实现方式让你直观的感受一下队列这个东东。
  首先,直接糊上代码,有没有闻到一股麦芽的香味~~~

#include <stdio.h>
#include <stdlib.h>

#define CAPACITY 10

//数据类型
typedef int data_t;
//队列类型
typedef struct
{
	data_t a[CAPACITY];
	int front; //队头的下标
	int rear;  //队尾的下一个下标
} seqqueue_t;

//创建空的队列
seqqueue_t *seqqueue_create()
{
	seqqueue_t *queue = (seqqueue_t *)malloc(sizeof(seqqueue_t));
	if (queue == NULL)
		return NULL;

	queue->front = 0;
	queue->rear = 0;

	return queue;
}

//空return 1  非空return 0
int seqqueue_is_empty(seqqueue_t *queue)
{
	if (queue == NULL)
		return -1;

	return queue->rear == queue->front ? 1 : 0;
}

//满return 1  不满return 0
int seqqueue_is_full(seqqueue_t *queue)
{
	if (queue == NULL)
		return -1;

	return (queue->rear + 1) % CAPACITY == queue->front ? 1 : 0;
}

//入队(rear)
int seqqueue_input(seqqueue_t *queue, data_t value)
{
	if (queue == NULL)
		return -1;

	if (seqqueue_is_full(queue))
		return -1;

	queue->a[queue->rear] = value;
	queue->rear = (queue->rear + 1) % CAPACITY;

	return 0;
}

//出队(front)
data_t seqqueue_output(seqqueue_t *queue)
{
	if (queue == NULL)
		return (data_t)-1;

	if (seqqueue_is_empty(queue))
		return (data_t)-1;

	data_t value = queue->a[queue->front];
	queue->front = (queue->front + 1) % CAPACITY;

	return value;
}

int main(int argc, const char *argv[])
{
	int i = 0;

	seqqueue_t *queue = seqqueue_create();
	if (queue == NULL)
		return -1;

	printf("\n入队数据:");
	while (!seqqueue_is_full(queue))
	{
		i++;
		printf("%d\t", i * 10);
		seqqueue_input(queue, i * 10);
	}

	printf("\n出队数据:");
	while (!seqqueue_is_empty(queue))
	{
		printf("%d\t", seqqueue_output(queue));
	}
	putchar(10);
	putchar(10);

	return 0;
}

  可惜大叔牙口不好,那么直接编译运行咯
在这里插入图片描述
  有那个意思了哈,那咱们继续往下。。。

4.2、顺序存储结构的队列的模型选择

  由上面的简单描述可知顺序存储结构的队列有两种形式。
  第一种:
    队尾 <–> 入队 <–> 顺序表的头部
    队头 <–> 出队 <–> 顺序表的尾部
  第二种:
    队尾 <–> 入队 <–> 顺序表的尾部
    队头 <–> 出队 <–> 顺序表的头部
  其简单的示意图可以如下图所示。
在这里插入图片描述
  有图片显示可以看出来,对于上面的两种模型的队列,由于队列只能操作两端(头、尾),所以其实上面两种模型没有什么区别了。
  (PS:虽然是废话,但是还是需要简单的提一下的)

4.3、顺序存储结构队列的代码实现

4.3.1、测试工程的目录结构

  为了负责人起见呢,我还是再简单的说明一下本人测试使用的目录结构。为了兼容unixwindows系统以及方便进行工程管理,特意使用Cmake工具进行编译等,目前测试工程的目录结构如下所示。

seqqueue/
├── CMakeLists.txt
├── README.md
├── build
├── main
│   └── main.c
├── runtime
└── src
    ├── seqlist
    │   ├── seqlist.c
    │   └── seqlist.h
    └── seqqueue
        ├── seqqueue.c
        └── seqqueue.h

6 directories, 7 files
4.3.2、seqqueue.h文件

  seqqueue.h文件主要申明和定义用户自定义变量和函数接口等,其中内容如下。

#ifndef __SeqQueue_H__
#define __SeqQueue_H__

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "../seqlist/seqlist.h"

#define null NULL
typedef void SeqQueue;
typedef void SeqQueueNode;

typedef struct func_SeqQueue
{
    SeqQueue *(*create)();
    int (*destroy)(SeqQueue *);
    int (*clear)(SeqQueue *);
    int (*length)(SeqQueue *);
    int (*capacity)(SeqQueue *);
    int (*append)(SeqQueue *, SeqQueueNode *);
    SeqQueueNode *(*header)(SeqQueue *);
    SeqQueueNode *(*subtract)(SeqQueue *);
} func_SeqQueue;

#endif

4.3.3、seqqueue.c文件

  seqqueue.c文件主要为队列的功能实现等,其中内容如下。

#include "seqqueue.h"

#ifndef null
define null NULL
#endif

/**
 * 通过下面的定义来选择队列饿模型的选择 
 * 如果定义 TAIL_HEADER,第一种模型
 * 如果不定义 TAIL_HEADER,第二种模型
 **/
// #define TAIL_HEADER

extern funSeqList SeqListfunc;

/**
 * 功 能:
 *      创建一个队列
 * 参 数:
 *      capacity:队列的最大容量
 * 返回值:
 *      成功:操作句柄
 *      失败:NULL
 **/
SeqQueue *SeqQueue_Create(int capacity)
{
    return SeqListfunc.create(capacity);
}
/**
 * 功 能:
 *      销毁一个队列
 * 参 数:
 *      Queue:要操作的队列
 * 返回值:
 *      成功:0
 *      失败:-1
 **/
int SeqQueue_Destory(SeqQueue *Queue)
{
    return SeqListfunc.destory(Queue);
}
/**
 * 功 能:
 *      清空一个队列
 * 参 数:
 *      Queue:要操作的队列
 * 返回值:
 *      成功:0
 *      失败:-1
 **/
int SeqQueue_Clear(SeqQueue *Queue)
{
    return SeqListfunc.clear(Queue);
}

/**
 * 功 能:
 *      获取队列的长度
 * 参 数:
 *      Queue:要操作的队列
 * 返回值:
 *      成功:队列的长度
 *      失败:-1
 **/
int SeqQueue_Length(SeqQueue *Queue)
{
    return SeqListfunc.length(Queue);
}

/**
 * 功 能:
 *      获取队列的最大容量
 * 参 数:
 *      Queue:要操作的队列
 * 返回值:
 *      成功:队列得容量
 *      失败:-1
 **/
int SeqQueue_Capacity(SeqQueue *Queue)
{
    return SeqListfunc.capacity(Queue);
}

/**
 * 功 能:
 *      插入元素
 * 参 数:
 *      Queue:要操作的队列
 *      node : 要插入的元素
 * 返回值:
 *      成功:0
 *      失败:-1
 **/
int SeqQueue_Append(SeqQueue *Queue, SeqQueueNode *node)
{
#ifdef TAIL_HEADER
    return SeqListfunc.insert(Queue, node, 0);
#else
    return SeqListfunc.insert(Queue, node, SeqListfunc.length(Queue));
#endif
}
/**
 * 功 能:
 *      获取队列的元素
 * 参 数:
 *      Queue:要操作的队列
 * 返回值:
 *      成功:操作句柄
 *      失败:NULL
 **/
SeqQueueNode *SeqQueue_Header(SeqQueue *Queue)
{
#ifdef TAIL_HEADER
    return SeqListfunc.get(Queue, SeqListfunc.length(Queue) - 1);
#else
    return SeqListfunc.get(Queue, 0);
#endif
}

/**
 * 功 能:
 *      删除队列
 * 参 数:
 *      Queue:要操作的队列
 * 返回值:
 *      成功:删除后的元素
 *      失败:NULL
 **/
SeqQueueNode *SeqQueue_Subtract(SeqQueue *Queue)
{
#ifdef TAIL_HEADER
    return SeqListfunc.delete(Queue, SeqListfunc.length(Queue) - 1);
#else
    return SeqListfunc.delete(Queue, 0);
#endif
}

func_SeqQueue fun_SeqQueue = {
    SeqQueue_Create,
    SeqQueue_Destory,
    SeqQueue_Clear,
    SeqQueue_Length,
    SeqQueue_Capacity,
    SeqQueue_Append,
    SeqQueue_Header,
    SeqQueue_Subtract
};

4.4、顺序存储结构队列的测试案例

  前面已经说明整体工程的结构,以及需要的文件,下面是测试底层功能函数的测试demo,说到底就是 main 函数功能,详细代码如下。

#include "../src/seqqueue/seqqueue.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 声明底层链表的函数库 */
extern func_SeqQueue fun_SeqQueue;

#define CAPACITY 10

int main(int argc, const char *argv[])
{
    int i = 0;
    int a[CAPACITY] = {0};

    SeqQueue *queue = fun_SeqQueue.create(CAPACITY);

    if (queue == NULL)
        return -1;

    // 压队列
    for (i = 0; i < CAPACITY - 1; i++)
    {
        a[i] = i + 1;
        fun_SeqQueue.append(queue, &a[i]);
    }

    printf("最大容量为 : %d\n", fun_SeqQueue.capacity(queue));
    printf("目前长度为 : %d\n", fun_SeqQueue.length(queue));
    printf("队列顶数据为 : %d\n", *((int *)fun_SeqQueue.header(queue)));
    printf("\n");

    while (fun_SeqQueue.length(queue) > 0)
    {
        printf("出队列的数据为 :%d\n", *((int *)fun_SeqQueue.subtract(queue)));
    }

    printf("\n");

    return 0;
}

4.5、测试案例构建编译

  前面的所有的内容都是在 windows 环进行编写并完成的,本次由于一些问题,本次测试暂时转到linux(Ubuntu 16.06 LTS) 环境下进行,其他系统等详细说明在README.md中查看。
  使用Cmake编译,使用下面指令。

cd build
cmake -G"Unix Makefiles" .. # 注意 .. ,表示上级目录
make

  效果其实和windows是一样的,如下图所示。
在这里插入图片描述

4.6、测试结果

  经过cmake编译之后,配置cmake可执行文件放在固定目录runtime下,可以使用在当前目录下使用指令 ./../runtime/seqqueue来运行可执行程序,也可以进入到目录runtime中,然后使用指令 ./seqqueue ,即可运行测试程序。实际测试的结果如下图所示。
在这里插入图片描述

五、链式存储结构的队列及实现

5.1、链式存储结构的传统队列简易实现与测试

  惯例热身,直接代码。

/*链式的队列:先进先出*/

#include <stdio.h>
#include <stdlib.h>

//数据类型
typedef int data_t;

//节点类型
typedef struct node
{
	data_t data;	   //存储数据
	struct node *next; //存储下一个节点地址
} linknode_t;

//队列类型
typedef struct
{
	linknode_t *front; //队头的地址
	linknode_t *rear;  //队尾的地址
} linkqueue_t;

//创建空的队列
linkqueue_t *linkqueue_create()
{
	// 开辟头节点的空间
	linknode_t *head = (linknode_t *)malloc(sizeof(linknode_t));
	if (head == NULL)
		return NULL;

	head->next = NULL;

	// 开辟两个指针空间
	linkqueue_t *queue = (linkqueue_t *)malloc(sizeof(linkqueue_t));
	if (queue == NULL)
	{
		free(head);
		head = NULL;
		return NULL;
	}

	queue->front = head;
	queue->rear = head;

	return queue;
}

//入队(尾插入 rear)
int linkqueue_input(linkqueue_t *queue, data_t value)
{
	if (queue == NULL)
		return -1;

	linknode_t *node = (linknode_t *)malloc(sizeof(linknode_t)); //开辟新节点的空间
	if (node == NULL)
		return -1;

	node->data = value;
	node->next = NULL;

	queue->rear->next = node; //最后一个节点与新节点相连
	queue->rear = node;		  //使指向新的最后一个节点

	return 0;
}

//空return 1  非空return 0
int linkqueue_is_empty(linkqueue_t *queue)
{
	if (queue == NULL)
		return -1;
#if 0
	return queue->front->next == NULL  ? 1 : 0;
#else
	return queue->front == queue->rear ? 1 : 0;
#endif
}

//出队(头删除 front)
data_t linkqueue_output(linkqueue_t *queue)
{
	if (queue == NULL)
		return (data_t)-1;

	if (linkqueue_is_empty(queue))
		return (data_t)-1;

	linknode_t *temp = queue->front->next; //临时记录要出队的节点
	data_t value = temp->data;			   //临时记录要出队的数据

	queue->front->next = temp->next; //头节点与第二个节点连接(头删)
	free(temp);
	temp = NULL;

	if (queue->front->next == NULL) //当队列为空时是指针rear指向头
		queue->rear = queue->front;

	return value;
}

int main(int argc, const char *argv[])
{
	int i = 0;
	linkqueue_t *queue = linkqueue_create();
	if (queue == NULL)
		return -1;

	printf("\n入队数据:");
	for (i = 1; i < 10; i++)
	{
		printf("%d\t", i * 10);
		linkqueue_input(queue, i * 10);
	}
	printf("\n出队数据:");
	while (!linkqueue_is_empty(queue))
	{
		printf("%d\t", linkqueue_output(queue));
	}
	putchar(10);
	putchar(10);

	return 0;
}

  效果看图。
在这里插入图片描述
  现在好啦,身也热完了,那就开干咯。

5.2、链式存储结构的队列模型的业务节点转换

  链式存储结构的队列也有两种形式。只是这两种形式其实和顺序结构的一模一样。往前面看一下就知道是什么样子的了,这儿就不再凑热闹了。

  按照原来线性表的测试案例,如果想要把业务节点插入到链表中,那么就要业务节点去包含链表节点,那么现在还有一个问题就是如何将栈的业务节点转化成一个链表的业务节点呢。
  我们已经在 数据结构(三) – C语言版 – 线性表的链式存储 - 单链表4.7、调用测试用例 章节已经说明了,通用链表并不关心业务节点是什么样式的,只是让业务节点去包含链表节点即可形成链表,那么现在在进行栈的操作中我们也需要将栈的业务节点转换成链表的业务节点。其实,就像前文中的 “老师节点”、“学生节点” 一样 ,我们也需要将链表节点包含到栈的节点中即可,那么,本文中的节点的定义可以为这样。
  定义存在于文件 linkqueue.h中。

// 定义队列的业务节点类型
typedef struct _tag_LinkQueueNode
{
    LinkListNode node;   // 链表的业务节点
    LinkQueueNode *item; // 队列的业务节点
} TLinkQueueNode;

  而原本的链表的业务节点的定义为这样的,定义存在于文件 linklist.h中。

typedef void Linklist;
#define null NULL

typedef struct _tag_linklistNode
{
    struct _tag_linklistNode *next;
} LinkListNode;

typedef struct _tag_Linklist
{
    LinkListNode header;
    int length;
} TLinklist;

  通过这样的包含关系,那么栈的节点已经包含了链表的节点。

  是的,上面这些文字在上一篇栈的文章已经写了,而且这儿出现和那个是一模一样的。

5.3、链式存储结构队列的代码实现

5.3.1、测试工程的目录结构

  目录结构都是一样的,除了个别文件的文件名称不一样、文件的多少、文件内容不一样外。

5.3.2、linkqueue.h文件

  linkqueue.h文件内容如下所示。

#ifndef __LINKQUEUE_H__
#define __LINKQUEUE_H__

#include "../linklist/linklist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define null NULL

typedef void LinkQueue;
typedef void LinkQueueNode;

// 定义队列的业务节点类型
typedef struct _tag_LinkQueueNode
{
    LinkListNode node;   // 链表的业务节点
    LinkQueueNode *item; // 队列的业务节点
} TLinkQueueNode;

typedef struct func_LinkQueue
{
    LinkQueue *(*create)();
    int (*destroy)(LinkQueue *);
    int (*clear)(LinkQueue *);
    int (*length)(LinkQueue *);
    int (*append)(LinkQueue *, LinkQueueNode *);
    LinkQueueNode *(*header)(LinkQueue *);
    LinkQueueNode *(*subtract)(LinkQueue *);
} func_LinkQueue;

#endif

5.3.3、linkqueue.c文件

  linkqueue.c文件内容如下所示。

#include "linkqueue.h"

extern func_linklist fun_linklist;

#ifndef null
define null NULL
#endif

/**
 * 通过下面的定义来选择队列饿模型的选择 
 * 如果定义 TAIL_HEADER,第一种模型
 * 如果不定义 TAIL_HEADER,第二种模型
 **/
// #define ABCDEFGH

/**
 * 功 能:
 *      创建一个队列
 * 参 数:
 *      无
 * 返回值:
 *      成功:操作句柄
 *      失败:NULL
 **/
LinkQueue *LinkQueue_Create(void)
{
    return fun_linklist.create();
}

/**
 * 功 能:
 *      插入元素,压队列
 * 参 数:
 *      queue:要操作的队列
 *      node : 要插入的元素,队列的业务节点
 * 返回值:
 *      成功:0
 *      失败:-1
 **/
int LinkQueue_Append(LinkQueue *queue, LinkQueueNode *node)
{
    if (queue == NULL || node == NULL)
        return -1;

    TLinkQueueNode *temp = NULL;
    int ret = 0;

    temp = (TLinkQueueNode *)malloc(sizeof(TLinkQueueNode));
    if (temp == NULL)
        return -1;

    memset(temp, 0, sizeof(TLinkQueueNode));

    temp->item = node;
#ifdef ABCDEFGH
    ret = fun_linklist.insert(queue, (LinkListNode *)temp, fun_linklist.length(queue));
#else
    ret = fun_linklist.insert(queue, (LinkListNode *)temp, 0);
#endif
    if (ret != 0 && temp != NULL)
        free(temp);

    return ret;
}

/**
 * 功 能:
 *      弹出队列元素
 * 参 数:
 *      queue:要操作的队列
 * 返回值:
 *      成功:删除后的元素
 *      失败:NULL
 **/
LinkQueueNode *LinkQueue_Subtract(LinkQueue *queue)
{
    if (queue == NULL)
        return NULL;

    LinkQueueNode *item = NULL;
    TLinkQueueNode *temp = NULL;

#ifdef ABCDEFGH
    temp = (TLinkQueueNode *)fun_linklist.delete(queue, 0);
#else
    temp = (TLinkQueueNode *)fun_linklist.delete(queue, fun_linklist.length(queue) - 1);
#endif
    if (temp == NULL)
        return NULL;

    // 把线性表的业务节点转换成队列的业务节点
    item = temp->item;
    // 在 insert 的时候malloc的内存需要free掉
    if (temp != NULL)
        free(temp);

    return item;
}

/**
 * 功 能:
 *      清空一个队列
 * 参 数:
 *      queue:要操作的队列
 * 返回值:
 *      成功:0
 *      失败:-1
 **/
int LinkQueue_Clear(LinkQueue *queue)
{
    if (queue == NULL)
        return -1;

    while (fun_linklist.length(queue) > 0)
    {
        /*
            涉及到队列的元素声明周期的处理
            所有入队列的节点都是通过mallo静态申请的,那么想要清空队列的时候,需要将malloc的节点free掉
            所以需要将队列中元素全都pop掉,并且free节点的内存
        */
        LinkQueueNode *temp = LinkQueue_Subtract(queue);
        if (temp == NULL)
        {
            return -1;
        }
    }
    return 0;
}

/**
 * 功 能:
 *      销毁一个队列
 * 参 数:
 *      queue:要操作的队列
 * 返回值:
 *      成功:0
 *      失败:-1
 **/
int LinkQueue_Destory(LinkQueue *queue)
{
    LinkQueue_Clear(queue);
    return fun_linklist.destory(&queue);
}

/**
 * 功 能:
 *      获取队列的长度
 * 参 数:
 *      queue:要操作的队列
 * 返回值:
 *      成功:队列的长度
 *      失败:-1
 **/
int LinkQueue_Length(LinkQueue *queue)
{
    return fun_linklist.length(queue);
}

/**
 * 功 能:
 *      获取队列顶的元素
 * 参 数:
 *      queue:要操作的队列
 * 返回值:
 *      成功:操作句柄
 *      失败:NULL
 **/
LinkQueueNode *LinkQueue_Header(LinkQueue *queue)
{
    if (queue == NULL)
        return NULL;
    TLinkQueueNode *temp = NULL;
#ifdef ABCDEFGH
    temp = (TLinkQueueNode *)fun_linklist.get(queue, 0);
#else
    temp = (TLinkQueueNode *)fun_linklist.get(queue, fun_linklist.length(queue) - 1);
#endif

    if (temp == NULL)
        return NULL;

    return temp->item;
}

func_LinkQueue fun_LinkQueue = {
    LinkQueue_Create,
    LinkQueue_Destory,
    LinkQueue_Clear,
    LinkQueue_Length,
    LinkQueue_Append,
    LinkQueue_Header,
    LinkQueue_Subtract
};

5.4、链式存储结构队列的测试案例

  是呀,好听了就叫测试案例,其实就是 main 函数所在文件,那么main.c文件内容如下所示。

#include "../src/linkqueue/linkqueue.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 声明底层栈的函数库 */
extern func_LinkQueue fun_LinkQueue;

#define CAPACITY 10

int main(int argc, const char *argv[])
{
    int i = 0;
    int a[CAPACITY] = {0};

    LinkQueue *queue = fun_LinkQueue.create();
    if (queue == NULL)
        return -1;

    // 压栈
    printf("入栈的数据为 :");
    for (i = 0; i < CAPACITY; i++)
    {
        a[i] = i + 1;
        printf("%d ", a[i]);
        fun_LinkQueue.append(queue, &a[i]);
    }
    printf("\n\n");

    printf("目前长度为 : %d\n", fun_LinkQueue.length(queue));
    printf("栈顶数据为 : %d\n", *((int *)fun_LinkQueue.header(queue)));
    printf("\n");

    printf("出栈的数据为 :");
    while (fun_LinkQueue.length(queue) > 0)
    {
        LinkQueueNode *tmp = fun_LinkQueue.subtract(queue);
        printf("%d ", *((int *)tmp));
    }
    printf("\n\n");

    return 0;
}

5.5、测试案例构建编译

  和上面的一样,CMake,没什么可解释的,直接上去啪啪啪就是几个命令干就完事儿了。
在这里插入图片描述

5.6、测试结果

  说多了就矫情,直接上图图图图。
在这里插入图片描述

六、强行来个总结

  • 和栈一样,可以用线性表的顺序存储结构、线性表的链式存储结构分别来模拟栈
  • 和栈不一样的就是 先进先出,可以用来做 缓冲
  • 顺序结构实现的队列可以有空、满的判断,但是本文使用的是长度
  • 是的,这么一堆说下来,其实栈是一样的

七、简单说明 – 栈和队列的异同

7.1、栈和队列的共同点

  • 都是一种操作受限的线性结构。
  • 只允许在端点处插入和删除元素(头部、尾部)
  • 都可以通过顺序结构和链式结构来模拟实现
  • 有相同的基本操作(创建、销毁、清空、入栈/队列、出栈/队列、获取栈/队列元素)
  • 入栈/队列、出栈/队列的操作的时间复杂度都可以是O(1),在空间复杂度上两者也一样(但是本文中的队列没有引入rear指针,所以队列的复杂度为O(1)、O(n))。
  • 多链栈和多链队列的管理模式可以相同。

7.2、栈和队列的不同点

  • 栈和队列的操作形式不同,栈先进后出队列先进先出
  • 栈的操作都在线性表的一端(要么头部,要么尾部)、队列的操作是在线性表的两端(一端入队列,一端出队列)
  • 获取元素的操作的速度不同,
  • 应用场景不同(最主要特性新不同导致)
  • 顺序结构的栈可以实现多栈空间的共享、而顺序结构队列却不能

八、一句话的总结

  吃多了吐就是栈
  吃多了拉就是队列

在这里插入图片描述

上一篇:数据结构(七) – C语言版 – 栈和队列 - 栈的应用解析
下一篇:数据结构(九) – C语言版 – 栈和队列 - 队列的特殊实现

猜你喜欢

转载自blog.csdn.net/zhemingbuhao/article/details/104486957