队列
本质:线性表
原理:先进先出
队列的2种设计:
- 顺序表设计,称为顺序队列
- 链式设计,称为链式队列
1 顺序队列
设计改进:循环队列
线性表本身不是环,但是解决问题需要考虑成环的问题:
1.约瑟夫环
2.魔法阵
2 循环队列:
结构定义:
API实现
//队列:先进先出的一种线性结构,入队的一端称为队尾,出队的一端称为对头
//队列的存储方式有两种:顺序结构(顺序队列)。链式结构(链式队列)
//顺序队列一定会设计成环形队列,原因是线性队列的入队O(1),出队为O(n)
//环形队列的入队和出队都为O(1)
//判满:尾指针再走一步就到头指针;浪费一个空间不用,主要是为了区分空和满的情况
//判满改进:可以加一个计数器,统计现在队列里有多少个元素,若元素为0,头和尾相等,队列为空;若不为0,头和尾相等,队列满;
//顺序队列主要理解两点设计:1.为什么是环形;2.为什么浪费一个空间
//队满的处理方式:1.固定长度,队满则入队失败(处理简单,不实用):和书本一致
// 2.长度不固定,队满则扩容(实现稍微复杂)
#define SIZE 10
typedef struct
{
int* base;//指向动态内存
int front;//对头指针,对头元素的下标
int rear;//队尾指针,当前可以插入数据的下标(队尾后一个元素的下标)
// int queuesize;//队列的总容量,要做到自动扩容就必须要增加这个成员
}SqQueue,*PSqQueue;
//初始化
void InitQueue(PSqQueue ps);
//判满
static bool IsFull(PSqQueue ps);
//往队列中入数据(入队操作)
bool Push(PSqQueue ps, int val);
//获取对头元素的值,但是不删除
bool GetTop(PSqQueue ps, int* rtval);//rtval:输出参数
//获取对头元素的值,且删除
bool Pop(PSqQueue ps, int* rtval);
//判空
bool IsEmpty(PSqQueue ps);
//获取队列有效数据的个数
int GetLength(PSqQueue ps);
//清空所有的数据
void Clear(PSqQueue ps);
//销毁
void Destory(PSqQueue ps);
3 链式队列
改进一:
改进二:
结构定义:
typedef struct Node
{
ELEM_TYPE data;//数据域
struct Node* next;//指针域
}Node, * PNode;
//我们对头结点重新设计其结构体
typedef struct Head
{
struct Node* front;//一直指向第一个节点
struct Node* rear;//一直指向最后一个节点
//int length;//如果需要频繁获取队列的有效长度,可以加上length
}Head, * PHead;
API实现:
//初始化
void Init_lqueue(PHead pq)
{
assert(pq != nullptr);
if (NULL == pq)
return;
pq->front = NULL;
pq->rear = NULL;
//pq->front = pq->rear = NULL;
}
//入队
bool Push(PHead pq, ELEM_TYPE val)
{
//assert pq
//申请新节点
Node* pnewnode = (PNode)malloc(sizeof(Node) * 1);
assert(pnewnode != NULL);
pnewnode->data = val;
//找到合适的插入位置 不需要找 因为头节点保存着尾部节点的地址
//插入 需要考虑一个问题:插入的时候里面已经有没有数据存在
if (pq->front == NULL)//此时是一个空队列
{
pnewnode->next = NULL;//=pq->front =pq->rear
pq->front = pnewnode;
pq->rear = pnewnode;
}
else
{
pnewnode->next = pq->rear->next;// = NULL;
pq->rear->next = pnewnode;
pq->rear = pnewnode;
}
return true;
}
//出队
bool Pop(PHead pq, int* rtval)
{
assert(pq != NULL && rtval != NULL);
if (pq == NULL || rtval == NULL)
return false;
if (pq->front == NULL)
{
return false;
}
//找到删除的位置 头删 所以不需要找
//删除 需要考虑一个问题:删除的那个节点是不是仅剩的唯一节点
if (pq->front->next == NULL)//如果为真 仅有一个节点存在
{
Node* p = pq->front;
*rtval = p->data;//通过输出参数 将值带出去
free(p);
p = NULL;
pq->front = pq->rear = NULL;
}
else//删除的节点肯定不是仅剩的那个(里面至少有2个节点)
{
Node* p = pq->front;
*rtval = p->data;//通过输出参数 将值带出去
pq->front = p->next;
free(p);
p = NULL;
}
return true;
}
//获取队列顶部元素
bool Top(PHead pq, int* rtval)
{
assert(pq != NULL && rtval != NULL);
if (pq == NULL || rtval == NULL)
return false;
if (pq->front == NULL)//判空
{
return false;
}
*rtval = pq->front->data;
return true;
}
//获取其有效长度个数
int Get_length(PHead pq)
{
//assert
int count = 0;
Node* p = pq->front;
for (p; p != NULL; p = p->next)
{
count++;
}
return count;
}
//判空
bool IsEmpty(PHead pq)
{
return pq->front == NULL;
}
判满 不需要判满
//bool IsFull(PHead pq);
//清空
void Clear(PHead pq)
{
Destroy(pq);
}
//销毁 头删
void Destroy(PHead pq)
{
while (pq->front != NULL)
{
Node* p = pq->front;
pq->front = p->next;
free(p);
p = NULL;
}
pq->front = pq->rear = NULL;
}
void Show(PHead pq)
{
//assert
Node* p = pq->front;
for (p; p != NULL; p = p->next)
{
printf("%d ", p->data);
}
printf("\n");
}