队列是在链表的基础上,进行一种规范(增删的顺序)和属性的增加(增加了项数)
这里的例子是"顾客咨询排队问题"
一.数据的构建
1.项的构建---------只包含基础数据类型,并用结构体打包
typedef struct item
{
long arrive;//一位顾客加入队列的时间
int processtime;//该顾客咨询话费的时间
}Item;
2.节点的构建------项加上指向其他节点的指针
typedef struct node//创建链表-------------------------------节点
{
Item item;
struct node *next;
}Node;
3.队列的构建(相对于链表的新增)-------2个指针(用来表明的前指针和用来标明队列的尾指针)和项数
typedef struct queue//建立队列---节点排列的一种方式----------队列
{
Node * front;
Node * rear;
int items;
}Queue;
二.队列的一些申明
- 第一部分-----初始化以及一些检查函数
/*初始化*/
void InitializeQueue(Queue * pq)
{
//头指针
pq->front = NULL;
//尾指针
pq->rear = NULL;
//项数
pq->items = 0;
}
/*判断----空*/
bool QueueIsEmpty(const Queue* pq)
{
//如果项数是0 就返回true
return pq->items == 0;
}
/*判断---满*/
bool QueueIsFull(const Queue* pq)
{
//如果项数是满的 就返回true
return pq->items == MAXQUEUE;
}
/*查---项数*/
int QueueItemCount(const Queue* pq)
{
//返回队列中的项数
return pq->items;
}
说明:
(1)初始化:队列的初始化,让2个指针指向null,项数为0
(2)判断是否为空,是否为满:通过判断项数的情况进行判断
(3)查项数:返回项数的值即可
- 第二部分-----队列的一些操作
/*增---添加项数*/
bool EnQueue(Item item, Queue*pq)
{
/*添加项数只能从队列后面的添加*/
//创建一个节点
Node * pnew;
//---检测是否创建成功---1.看队列是否满了 2.看分配空间是否成功
if (QueueIsFull(pq))
return false;
pnew = (Node*)malloc(sizeof(Node));
if (pnew == NULL)
{
fprintf(stderr, "为节点分配空间出现问题\n");
exit(EXIT_FAILURE);
}
//初始化节点----项,指针
CopyToNode(item, pnew);
pnew->next = NULL;
//判断节点是否为第一个节点
if (QueueIsEmpty(pq))
pq->front = pnew;
//---不是的话 节点的拼接操作----尾端的下一个指针 指向新节点
else
{
pq->rear->next = pnew;
}
//更新尾部指针
pq->rear = pnew;
//项数加1
pq->items++;
return true;
}
/*删---删除项数*/
bool DeQueue(Item *pitem, Queue* pq)
{
/*删除项数只能从队列前面删*/
//创建一个节点(替死鬼)指针
Node * pt;
//判断队列是否为空--为空的话 就执行不了删除操作
if (QueueIsEmpty(pq))
return false;
//调用函数 取出非指针部分(项)---这样的删除是可以恢复的(数据没丢)??
CopyToItem(pq->front, pitem);
//新节点指针指向要被删除的节点(首节点)
pt = pq->front;
//让队列首指针指向下一个节点
pq->front = pq->front->next;
//释放替死鬼指针里的内容
free(pt);
//队列项目数减1
pq->items--;
//判断队列里的项目数是否为0---如果为0 就不存在尾指针
if (pq->items == 0)
{
pq->rear = NULL;
}
return true;
}
/*清空*/
void EmptyTheQueue(Queue* pq)
{
/*相当于清空了所有的节点*/
//创建一个项 作为垃圾站(存放要被删除的项)
Item dummy;
//如果队列不是空的--就一直删除节点
while (!QueueIsEmpty(pq))
{
DeQueue(&dummy, pq);
}
}
说明:
- (1)增添项数的操作:通过创建节点,把节点加入到队列中实现
其中节点的初始化用到了辅助函数
static void CopyToNode(Item item, Node* pn)
{
pn->item = item;
}
辅助函数是把项中的内容(item)送到节点中项的部分(pn->item = item;)
这个函数的操作流程是:
1.创建一个指向节点结构的指针(pnode)------作用类似于"current"指针
2.对队列情况的判断-------满了就不能添加了
3.对此指针指向节点的初始化(先请求分配空间,分配空间同意后,项(辅助函数)和指针(指向下一节点)的初始化)
4.对队列情况的判断------如果(1)队列是空的,这个新节点就是"头部了"(用前指针指向这个节点)
(2)队列不是空的:那就让队列"屁股节点"(pq->rear)的"小尾巴"(pq->rear->next)指向这个节点(pnode)
5.更新"屁股节点"-------让"屁股节点"指针指向新节点(pq->rear = pnew;)
6.更新项数;
- (2)删除项数的操作:通过创建节点指针,让这个指针指向要删除的节点,删除这个指针所指向的内容即可
其中将节点中项部分的内容取出(我感觉这里这么做只是为了做个删除备份)用到了辅助函数:
static void CopyToItem(Node*pn, Item*item)
{
*item = pn->item;
}
辅助函数是把要删除的节点(pn)中的项(pn->item)做个备份(让item指向这个数据)(注意这里不是地址传递而是值传递)
这个函数的操作流程是:
1.创建一个指向节点结构的指针(pt)------作用类似于"current"指针
2.对队列的情况的判断-----空的就无法执行操作
3.对前指针进行备份,转移处理:
(1)备份:(把第一个节点的项的内容复制一下)
(2)转移:让pt和前指针指向同一个节点(形成current"节点")------pt = pq->front;
再让前指针指向前指针的"下一个"从而形成转移--------pq->front = pq->front->next;
4.释放pt指针所指向的节点
5.项数的改变
6.如果项数为0了,队列的尾部(pq->rear"后指针")要指向空(注意"后指针"和"后指针的尾巴",前者是有实际节点的,后者一般为null).
- (3)清空处理:就是对每个节点进行删除操作