基本概念
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
顺序队列
建立顺序队列结构必须为其静态分配或动态申请一片连续的存储空间,并设置两个指针进行管理。一个是队头指针front,它指向队头元素;另一个是队尾指针rear,它指向下一个入队元素的存储位置
顺序队列中的溢出现象:
(1) “下溢”现象:当队列为空时,做出队运算产生的溢出现象。“下溢”是正常现象,常用作程序控制转移的条件。
(2)”真上溢”现象:当队列满时,做进栈运算产生空间溢出的现象。“真上溢”是一种出错状态,应设法避免。
(3)”假上溢”现象:由于入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用。当队列中实际的元素个数远远小于向量空间的规模时,也可能由于尾指针已超越向量空间的上界而不能做入队操作。该现象称为”假上溢”现象。
//线性表顺序存储设计与实现测试框架
//SeqList.h
#ifndef _SEQLIST_H_
#define _SEQLIST_H_
typedef void SeqList;
typedef void SeqListNode;
//这两句话意思:
//C语言里面的typedef, 字面上理解就是类型的定义, 也就是给内置的或自定义的数据类型重新命名
//SeqList* SeqList_Create(int capacity); //这样一看返回值就知道是返回值是链表
//void * SeqList_Create(int capacity); //如果这样,使用者一看只知道是返回指针,不知道具体的,可读性很差
//所以才会给void起别名,封装好的底层函数,提供给使用者使用,使用者会给容易懂
//创建并且返回一个空的线性表
SeqList* SeqList_Create(int capacity);
//销毁一个线性表list
void SeqList_Destroy(SeqList* list);
//将一个线性表list中的所有元素清空, 线性表回到创建时的初始状态
void SeqList_Clear(SeqList* list);
//返回一个线性表list中的所有元素个数
int SeqList_Length(SeqList* list);
//返回一个线性表list中的容量
int SeqList_Capacity(SeqList* list);
//向一个线性表list的pos位置处插入新元素node
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);
//获取一个线性表list的pos位置处的元素
SeqListNode* SeqList_Get(SeqList* list, int pos);
//删除一个线性表list的pos位置处的元素 返回值为被删除的元素,NULL表示删除失败
SeqListNode* SeqList_Delete(SeqList* list, int pos);
#endif
///
//SeqQueue.h
#ifndef _SEQUEUE_H_
#define _SEQUEUE_H_
typedef void SeqQueue;
//这两句话意思:
//C语言里面的typedef, 字面上理解就是类型的定义, 也就是给内置的或自定义的数据类型重新命名
//SeqQueue* SeqQueue_Create(int capacity);//这样一看返回值就知道是返回值是链表
//void SeqQueue_Destroy(SeqQueue *queue); //如果这样,使用者一看只知道是返回指针,不知道具体的,可读性很差
//所以才会给void起别名,封装好的底层函数,提供给使用者使用,使用者会给容易懂
//创建队列
SeqQueue* SeqQueue_Create(int capacity);
//销毁队列
void SeqQueue_Destroy(SeqQueue *queue);
//清空列表
void SeqQueue_Clear(SeqQueue *queue);
//队尾追加
int SeqQueue_Append(SeqQueue *queue, void *item);
//出队操作
void* SeqQueue_Retrieve(SeqQueue *queue);
//队头操作
void* SeqQueue_Header(SeqQueue *queue);
//队长
int SeqQueue_Length(SeqQueue *queue);
//队列容量
int SeqQueue_Capacity(SeqQueue *queue);
#endif
//
//SeqList.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SeqList.h"
//在结构体中套一级指针
typedef struct _tag_SeqList
{
int length;//线性表的长度
int capacity;//数组的长度-存放线性表的存储空间的长度
unsigned int *node;//链表需要有内存空间存储装元素-根据容量来分配内存空间-需要动态分配内存空间 int node[]
}TSeqList;
//创建并且返回一个空的线性表
SeqList* SeqList_Create(int capacity)
{
int ret = 0;
if (capacity <= 0)
{
ret = -1;
printf("func err (capacity <= 0):%d\t", ret);
return NULL;
}
//1 线性表申请动态内存空间
TSeqList *tmp = (TSeqList *)malloc(sizeof(TSeqList));//开辟一片内存空间,大小是TSeqList类这么大
if (NULL == tmp)
{
ret = -2;
printf("func err malloc:%d\t", ret);
return NULL;
}
//开辟的内存,完成初始化
memset(tmp,0,sizeof(TSeqList));
//2 根据容量分配内存大小
tmp->node = (unsigned int *)malloc(sizeof(unsigned int)*capacity);//创建node结点的内存空间大小-相当于二维数组的行元素个数
if (NULL == tmp->node)
{
ret = -3;
printf("func err malloc:%d\t", ret);
return NULL;
}
tmp->capacity = capacity;
tmp->length = 0;
return tmp;
}
//销毁一个线性表list
void SeqList_Destroy(SeqList* list)
{
int ret = 0;
if (NULL == list)
{
ret = -1;
printf("func err (NULL == list):%d\t", ret);
}
//1 缓存下来 进行操作
TSeqList *tmp = NULL;
tmp = (TSeqList *)list;
//2 先申请 后释放
if (tmp->node != NULL)
{
free(tmp->node);
}
if (tmp!=NULL)
{
free(tmp);
}
}
//将一个线性表list中的所有元素清空, 线性表回到创建时的初始状态
void SeqList_Clear(SeqList* list)
{
int ret = 0;
if (NULL == list)
{
ret = -1;
printf("func err (NULL == list):%d\t", ret);
}
//1 缓存下来 进行操作
TSeqList *tmp = NULL;
tmp = (TSeqList *)list;
//2 线性表中的长度置空
tmp->length = 0;
//3 重新将线性表中创建的结点初始化
memset(list,0,tmp->capacity * sizeof(unsigned int)); // 会把所有的置空
}
//返回一个线性表list中的所有元素个数
int SeqList_Length(SeqList* list)
{
int ret = 0;
if (NULL == list)
{
ret = -1;
printf("func err (NULL == list):%d\t", ret);
return ret;
}
//1 缓存下来 进行操作
TSeqList *tmp = NULL;
tmp = (TSeqList *)list;
return tmp->length;
}
//返回一个线性表list中的容量
int SeqList_Capacity(SeqList* list)
{
int ret = 0;
if (NULL == list)
{
ret = -1;
printf("func err (NULL == list):%d\t", ret);
return ret;
}
//1 缓存下来 进行操作
TSeqList *tmp = NULL;
tmp = (TSeqList *)list;
return tmp->capacity;
}
//向一个线性表list的pos位置处插入新元素node
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos)
{
int ret = 0;
if (NULL == list || NULL == node||pos < 0)
{
ret = -1;
printf("func err (NULL == list || NULL == node||pos < 0):%d\t", ret);
return ret;
}
//1 缓存下来 进行操作
TSeqList *tmp = NULL;
tmp = (TSeqList *)list;
//注意1:线性表长度应该小于等于数组的长度
if (tmp->length >=tmp->capacity)
{
ret = -2;
printf("func err (tmp->length >=tmp->capacity):%d\t", ret);
return ret;
}
//注意2:容错修正 假设容量为20,此时线性表长度为6,pos却为10,这个时候可以做容错修正,直接修正为尾插法
if (pos >tmp->length)
{
pos = tmp->length;
}
//2 元素后移
int i;
for ( i = tmp->length; i > pos; i--)
{
tmp->node[i] = tmp->node[i - 1];//后移
//t[pos+1] = t[pos+1-1]=t[pos]
}
//3 空出的位置插入新结点
tmp->node[i] = (unsigned int )node;//这里装二维数组的行的一个元素,这个行元素指向业务结点teacher这个结构体,所以teacher里面装多少信息,都是可以的
//4 线性表长度+1
tmp->length++;
return ret;
}
//获取一个线性表list的pos位置处的元素
SeqListNode* SeqList_Get(SeqList* list, int pos)
{
int ret = 0;
if (NULL == list || pos < 0)
{
ret = -1;
printf("func err (NULL == list || pos < 0):%d\t", ret);
return NULL;
}
//1 缓存下来 进行操作
TSeqList *tmp = NULL;
tmp = (TSeqList *)list;
//获取一个线性表list的pos位置处的元素
tmp = (SeqListNode *)tmp->node[pos];
return tmp;
}
//删除一个线性表list的pos位置处的元素 返回值为被删除的元素,NULL表示删除失败
SeqListNode* SeqList_Delete(SeqList* list, int pos)
{
int ret = 0;
if (NULL == list || pos < 0)
{
ret = -1;
printf("func err (NULL == list || pos < 0):%d\t", ret);
return NULL;
}
//1 缓存下来 进行操作
TSeqList *tmp = NULL;
tmp = (TSeqList *)list;
//2 缓存删除元素
TSeqList *Deletemp = NULL;
Deletemp = (SeqListNode *)tmp->node[pos];
//3 前移
for (int i = pos+1; i < tmp->length; i++)
{
tmp->node[i-1] = tmp->node[i];
}
//4 删除元素后 线性表长度减1
tmp->length--;
return Deletemp;
}
///
//SeqQueue.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SeqList.h"
#include "SeqQueue.h"
//创建队列:相当于创建一个顺序表
SeqQueue* SeqQueue_Create(int capacity)
{
return SeqList_Create(capacity);
}
//销毁队列:相当于销毁一个顺序表
void SeqQueue_Destroy(SeqQueue *queue)
{
SeqList_Destroy(queue);
}
//清空列表:相当于清空一个线性表
void SeqQueue_Clear(SeqQueue *queue)
{
SeqList_Clear(queue);
}
//队尾追加:相当于在链表的尾部添加元素
int SeqQueue_Append(SeqQueue *queue, void *item)
{
return SeqList_Insert(queue,item,SeqList_Length(queue));
}
//出队操作:相当于删除链表的0号位置
void* SeqQueue_Retrieve(SeqQueue *queue)
{
return SeqList_Delete(queue,0);
}
//队头操作:相当于获取链表顺序存储的0号位置
void* SeqQueue_Header(SeqQueue *queue)
{
return SeqList_Get(queue,0);
}
//队长:相当于链表的长度
int SeqQueue_Length(SeqQueue *queue)
{
return SeqList_Length(queue);
}
//队列容量:相当于链表的容量
int SeqQueue_Capacity(SeqQueue *queue)
{
return SeqList_Capacity(queue);
}
///
//队列的顺序存储设计与实现的测试框架
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SeqList.h"
#include "SeqQueue.h"
int main()
{
int ret = 0;
int a[5] = {0};
//1 建立一个空的队列
SeqQueue *queue = SeqQueue_Create(10);
if (NULL == queue)
{
ret = -1;
printf("func err SeqQueue_Create() NULL == queue\n");
return ret;
}
//2 往队列中添加元素
for (int i = 0; i < 5; i++)
{
a[i] = i + 1;
ret = SeqQueue_Append(queue,(void *)&a[i]);
if (ret != 0)
{
ret = -2;
printf("func err SeqQueue_Append()\n");
return ret;
}
}
//3 队列的属性
//队头操作
//void* SeqQueue_Header(SeqQueue *queue);
ret = *((int *)SeqQueue_Header(queue));
printf("队头:%d\t",ret);
//队长
//int SeqQueue_Length(SeqQueue *queue);
printf("队长:%d\t",SeqQueue_Length(queue));
//队列容量
//int SeqQueue_Capacity(SeqQueue *queue);
printf("队列的容量:%d\t",SeqQueue_Capacity(queue));
//出队操作
//void* SeqQueue_Retrieve(SeqQueue *queue);
for (int i = 0; i < 5; i++)
{
ret = *((int *)SeqQueue_Retrieve(queue));
printf("出队列操作:%d\t", ret);
}
//清空列表
//void SeqQueue_Clear(SeqQueue *queue);
//SeqQueue_Clear(queue);
//销毁队列
//void SeqQueue_Destroy(SeqQueue *queue);
SeqQueue_Destroy(queue);
system("pause");
return 0;
}
队列的链式存储设计与实现
//LinkList.h
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
//单链表的存储结构
//结点中包含后继结点地址的指针域组成-可以理解为指向下一个结构体(结点)
//(这里不包含数据域,是实现了 链表的api(链表的算法) 和 具体的数据分离)
typedef struct _tag_LinkListNode
{
struct _tag_LinkListNode *next;
}LinkListNode;
//为void 再重新多取一个名字,LinkList等价于void
//typedef + 已有的数据类型+新的数据类型(自己取的新名字)
typedef void LinkList;
//创建并且返回一个空的链式的线性表
LinkList* LinkList_Create();
//销毁一个链式的线性表list
void LinkList_Destroy(LinkList* list);
//将一个链式线性表list中的所有元素清空, 链式线性表回到创建时的初始状态
void LinkList_Clear(LinkList* list);
//返回一个链式线性表list中的所有元素个数
int LinkList_Length(LinkList* list);
//向一个链式线性表list的pos位置处插入新元素node
int LinkList_Insert(LinkList* list, LinkListNode* node, int pos);
//获取一个链式线性表list的pos位置处的元素
LinkListNode* LinkList_Get(LinkList* list, int pos);
//删除一个链式线性表list的pos位置处的元素 返回值为被删除的元素,NULL表示删除失败
LinkListNode* LinkList_Delete(LinkList* list, int pos);
#endif
//LinkQueue.h
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_
typedef void LinkQueue;//队列的句柄
//队列的链式存储设计
//这句话意思:
//C语言里面的typedef, 字面上理解就是类型的定义, 也就是给内置的或自定义的数据类型重新命名
//LinkQueue* LinkQueue_Create(); //这样一看返回值就知道是返回值是栈
//void LinkQueue_Destroy(LinkQueue *queue); //如果这样,使用者一看只知道是返回指针,不知道具体的,可读性很差
//所以才会给void起别名,封装好的底层函数,提供给使用者使用,使用者会给容易懂
//创建队列
LinkQueue* LinkQueue_Create();
//销毁队列
void LinkQueue_Destroy(LinkQueue *queue);
//清空队列
void LinkQueue_Clear(LinkQueue *queue);
//队列中添加元素
int LinkQueue_Append(LinkQueue *queue, void *item);
//出队操作
void* LinkQueue_Retrieve(LinkQueue *queue);
//获取队列头部元素
void* LinkQueue_Header(LinkQueue *queue);
//队列长度
int LinkQueue_Length(LinkQueue *queue);
#endif
//
//LinkList.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "LinkList.h"
//定义头结点 链式存储头结点:表示链表中第一个节点,包含指向第一个数据元素的指针以及链表自身的一些信息
//这样能把所有结点串起来
typedef struct _tag_LinkList
{
LinkListNode header;//要有个头指针---指向头结点的指针
int length;//底层库中加了多少个结点
}TLinkList;
//创建并且返回一个空的链式的线性表
LinkList* LinkList_Create()
{
//1 申请动态内存空间
TLinkList *tmp = NULL;
tmp = (TLinkList *)malloc(sizeof(TLinkList));
if (NULL == tmp)
{
printf("func err malloc\n");
return NULL;
}
//2 让开辟的内存 完成链式线性表初始化
memset(tmp,0,sizeof(TLinkList));
//3 链表的初始化
tmp->header.next = NULL;
tmp->length = 0;
return tmp;
}
//销毁一个链式的线性表list
//链表节点的生命周期由调用者负责,也就是main()函数负责,链表的销毁只需释放头结点空间
void LinkList_Destroy(LinkList* list)
{
//1 缓存下来 进行操作
TLinkList *tmp = NULL;
tmp = (TLinkList *)list;
if (NULL == list)
{
printf("func err LinkList_Destroy\n");
}
//2 释放头结点空间
if (tmp!=NULL)
{
free(tmp);
}
}
//将一个链式线性表list中的所有元素清空, 链式线性表回到创建时的初始状态
//链表的清空只是将头结点的指针域指向NULL,以及链表的长度length赋值为0
void LinkList_Clear(LinkList* list)
{
//1 缓存下来 进行操作
TLinkList *tmp = NULL;
tmp = (TLinkList *)list;
if (NULL == list)
{
printf("func err LinkList_Clear\n");
}
//2 清空链表
tmp->header.next = NULL;
tmp->length = 0;
}
//返回一个链式线性表list中的所有元素个数
int LinkList_Length(LinkList* list)
{
int ret = 0;
//1 缓存下来 进行操作
TLinkList *tmp = NULL;
tmp = (TLinkList *)list;
if (NULL == list)
{
ret = -1;
printf("func err LinkList_Length:%d\n",ret);
return ret;
}
ret = tmp->length;
return ret;
}
//向一个链式线性表list的pos位置处插入新元素node
int LinkList_Insert(LinkList* list, LinkListNode* node, int pos)
{
int ret = 0;
//1 缓存下来 进行操作
TLinkList *tmp = NULL;
tmp = (TLinkList *)list;
//辅助指针 用来遍历当前指针位置
LinkListNode *pCur = NULL;
if (NULL == list || NULL == node || pos < 0)
{
ret = -1;
printf("func err (NULL == list || NULL == node || pos < 0):%d\n", ret);
return ret;
}
//1 当前指针 初始化 指向 头结点
pCur = &(tmp->header);
//2 进行遍历 找到插入位置
for (int i = 0; i < pos; i++)
{
pCur = pCur->next;
}
//3 进行插入操作
node->next = pCur->next;//1
pCur->next = node;
//4 链表长度++
tmp->length++;
return ret;
}
//获取一个链式线性表list的pos位置处的元素
LinkListNode* LinkList_Get(LinkList* list, int pos)
{
int ret = 0;
//1 缓存下来 进行操作
TLinkList *tmp = NULL;
tmp = (TLinkList *)list;
//辅助指针 用来遍历当前指针位置
LinkListNode *pCur = NULL;
if (NULL == list || pos < 0)
{
ret = -1;
printf("func err (NULL == list|| pos < 0):%d\n", ret);
return NULL;
}
//1 当前指针 初始化 指向 头结点
pCur = &(tmp->header);
//2 进行遍历 找到pos位置
for (int i = 0; i < pos; i++)
{
pCur = pCur->next;
}
return pCur->next;
}
//删除一个链式线性表list的pos位置处的元素 返回值为被删除的元素,NULL表示删除失败
LinkListNode* LinkList_Delete(LinkList* list, int pos)
{
int ret = 0;
//1 缓存下来 进行操作
TLinkList *tmp = NULL;
tmp = (TLinkList *)list;
//辅助指针 用来缓存要删除的结点
LinkListNode *Deletemp = NULL;
//辅助指针 用来遍历当前指针位置
LinkListNode *pCur = NULL;
if (NULL == list || pos < 0)
{
ret = -1;
printf("func err (NULL == list|| pos < 0):%d\n", ret);
return NULL;
}
//1 当前指针 初始化 指向 头结点
pCur = &(tmp->header);
//2 进行遍历 找到要删除的pos位置
for (int i = 0; i < pos; i++)
{
pCur = pCur->next;
}
//3 缓存要删除的元素
Deletemp = pCur->next;
//4 进行删除操作
pCur->next = Deletemp->next;
//5 链表长度--
tmp->length--;
return Deletemp;
}
///
//LinkQueue.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "LinkList.h"
#include "LinkQueue.h"
//链式队列的存储节点定义
//包含链表的指针域节点和栈的业务结点
//定义一个抽象的栈节点模型
//也就是用链式队列的抽象模型来存储item 以便插入元素
typedef struct _tag_LinkstackNode
{
LinkListNode node; //包含链表的结点
void* item; //队列的业务结点
}TLinkStack;
//创建队列:相当于创建一个链式的队列
LinkQueue* LinkQueue_Create()
{
return LinkList_Create();
}
//销毁队列:首先清除队列中所有元素,然后在销毁队列
void LinkQueue_Destroy(LinkQueue *queue)
{
//1 首先要清除所有元素,释放所有结点
LinkList_Clear(queue);
//2 再销毁栈
LinkList_Destroy(queue);
}
//清空队列
//清空一个队列 相当于 清空一个线性表
//清空队列的时候 涉及到 栈元素生命周期的管理
//清空链式队列:当栈的长度不为0时,一直Pop出队列中的元素。直到队列中不存在任何元素
//所有入队列的结点都是malloc
//若要清空队列 把队列中元素弹出 并且释放结点内存
void LinkQueue_Clear(LinkQueue *queue)
{
if (NULL == queue)
{
return;
}
while (LinkList_Length(queue) > 0)
{
LinkQueue_Retrieve(queue);
}
LinkList_Clear(queue);
}
//队列中添加元素
//向队列中压入元素相当于链式队列的头插法
// void *item栈的业务节点===>链表的业务节点
int LinkQueue_Append(LinkQueue *queue, void *item)
{
int ret = 0;
TLinkStack *tmp = NULL;
tmp = (TLinkStack *)malloc(sizeof(TLinkStack));
if (NULL == tmp || NULL == queue || NULL == item)
{
ret = -1;
printf("func err NULL == tmp|| NULL == queue||NULL == item:%d\n", ret);
return ret;
}
memset(tmp,0,sizeof(TLinkStack));//初始化
tmp->item = item;
//节点转换适配过程
//需要把队列的业务结点 转换成链表的结点
ret = LinkList_Insert(queue,(LinkListNode *)tmp,LinkList_Length(queue));
if (ret != 0)
{
ret = -2;
printf("func errLinkList_Insert():%d\n", ret);
if (tmp != NULL)//防止内存泄漏问题
{
free(tmp);
}
return ret;
}
return ret;
}
//出队操作:相当于从链表的头部删除元素
void* LinkQueue_Retrieve(LinkQueue *queue)
{
TLinkStack *tmp = NULL;
void *item = NULL;
tmp = (TLinkStack *)LinkList_Delete(queue,0);
if (NULL == tmp)
{
printf("func err LinkList_Delete()\n");
return NULL;
}
item = tmp->item;//缓存要删除的元素
if (tmp != NULL)
{
free(tmp);//出队列不能忘记释放结点
}
return item;
}
//获取队列头部元素:相当于从链表的头部删除
void* LinkQueue_Header(LinkQueue *queue)
{
TLinkStack *tmp = NULL;
void *item = NULL;
tmp = (TLinkStack *)LinkList_Get(queue, 0);
if (NULL == tmp)
{
printf("func err LinkQueue_Header()\n");
return NULL;
}
return tmp->item;
}
//队列长度
int LinkQueue_Length(LinkQueue *queue)
{
return LinkList_Length(queue);
}
///
//队列的链式存储实现与设计测试框架.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "LinkQueue.h"
int main()
{
int ret = 0;
int a[5];
//1 建立一个空的队列
LinkQueue *linkqueue = NULL;
linkqueue = LinkQueue_Create();
if (NULL == linkqueue)
{
ret = -1;
printf("func err LinkQueue_Create():%d\n");
return ret;
}
//往队列添加元素
for (int i = 0; i < 5; i++)
{
a[i] = i + 1;
ret = LinkQueue_Append(linkqueue, &a[i]);
if (ret != 0)
{
ret = -2;
printf("func err LinkQueue_Append():%d\n");
return ret;
}
}
//队列的属性
printf("队列的长度:%d\n", LinkQueue_Length(linkqueue));
ret = *((int *)LinkQueue_Header(linkqueue));
printf("队列队长是:%d\n", ret);
//出队列
while (LinkQueue_Length(linkqueue) > 0)
{
int tmp = *((int *)LinkQueue_Retrieve(linkqueue));
printf("%d\n", tmp);
}
//销毁队列
LinkQueue_Destroy(linkqueue);
system("pause");
return 0;
}