引言:
大家在使用电脑时有没有经历过,机器有时会处于疑似死机状态,鼠标点什么似乎都没用,双击任何快捷方式都不动弹。就当你失去耐心,打算reset时突然它就像醒酒了一样,把你刚才单击的所有操作都按顺序执行了一遍。这是因为操作系统在当时可能cpu一时忙不过来,等前面的事情忙完后,后面多个指令需要通过一个通道输出,按先后次序排队执行造成的结果。再比如,像移动、联通、电信等电话客服,客服人员和客户相比总是少数的,在所有客服人员都占线的情况下,客户会被要求等待,直到某个客服人员空下来,才能让最先开始等待的客户接通电话,这里也是将当前所有拨打客服电话的客户进行了排队处理。
操作系统和客服系统中,都是应用了一种数据结构,来实现方才提到的先进先出的排队功能,这就是队列。
一、队列的定义
1、队列是一种先进先出(First in first out)的线性表,简称:FIFO;允许插入的一端称为队尾,允许删除的一端叫做队头。
2、队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
二、队列的实现思路
首先我们用链表来实现队列,队列的结构是由一个个的节点组成的,定义俩个指针,一个指向队头,一个指向队尾。节点也需要定义结构体。
三、代码具体实现
1、定义队列结构
cpp
//1、先定义队列结点的结构
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
QueueNode* next;
}QueueNode;
//2、定义队列的结构
typedef struct Queue
{
QueueNode* phead;//队列头的
QueueNode* ptail;//队列的尾
}Queue;
2、队列结构初始化
cpp
//初始化
void QueueInit(Queue* pq)
{
assert(pq);
return pq->phead =pq->ptail= NULL;
}
3、判空
cpp
//队列判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->phead ==NULL;
}
4、出队
cpp
//出队--队头
void QueuePop(Queue* pq)
{
assert(!QueueEmpty(pq));
if (pq->phead == pq->ptail)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
QueueNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
}
5、队列有效元素
cpp
//队列有效元素
int QueueSize(Queue* pq)
{
assert(pq);
QueueNode* pcur = pq->phead;
int size = 0;
while (pcur)
{
size++;
pcur = pcur->next;
}
return size;
}
6、取队头元素
cpp
//取队头元素(数据)
QDataType QueueFront(Queue* pq)
{
assert(!QueueEmpty(pq));
return pq->phead->data;
}
7、取队尾元素
cpp
//取队尾数据
QDataType QueueBack(Queue* pq)
{
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
8、队列的销毁
cpp
//销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* pcur = pq->phead;
while (pcur)
{
QueueNode* next = pcur->next;
free(pcur);
pcur = next;
}
pq->phead = pq->ptail = NULL;
}
9、入队
cpp
//入队--队尾
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
}
else
{
//队列非空
pq->ptail->next = newnode;
pq->ptail = pq->ptail->next;
}
}
四、总结
1、栈是限定仅在表尾插入和删除操作的线性表,队列是只允许在一端进行插入操作,另一端进行删除操作的线性表。
2、它们都可以用线性表的顺序存储结构来实现,但都存在着顺序存储的一些弊端,因此它们各自有各自的技巧来解决这个问题。
对于栈来说,如果是俩个相同数据类型的栈,则可以用数组的俩端做栈底的方法来让俩个栈共享数据,这就可以最大化利用数组的空间。
对于队列来说,为了避免数组在插入和删除是需要移动数据,于是就引入了循环队列,使得队头和队尾可以在数组中循环变化。解决了移动数据的时间损耗,使得本来插入和删除是O(n)的时间复杂度变成了O(1)。
它们也都可通过链式存储结构来实现,实现原则上与线性表基本相同,如下图所示:
|--------|---|
| 顺序栈 | |
| 俩栈共享空间 | |
| 链栈 | |
[栈]
|------|---|
| 顺序队列 | |
| 循环队列 | |
| 链队列 | |
[队列]
五、结尾语
人生就像一个很大的栈演变。人生又仿佛是一天一天小小的栈重现。童年时,父母每天抱你不断地进出家门,壮年你每天奔波于事业与家之间,老年你每天独自蹒跚于养老院的门里屋前。
人生,更需要又进栈出栈精神的体现。在哪里跌倒就应该在哪里爬起来。无论陷入何等困境,只要抬头能仰望蓝天,就有希望,不断进取,你就可以让出头之日重现。困难不会永远存在,强者才能勇往直前。
人生有是一个又一个小小的队列重现,春夏秋冬轮回年年,早中晚夜循环天天。变化的是时间,不变的是你对未来执着的信念。
人生,更需要有队列精神的体现。南极到北极,不过是南纬90°到北纬90°的队列,如果你中途犹豫,临时转向,也许你就只能和企鹅相伴永远。可事实上,无论那个方向,只要你坚持到底,你都可以到达终点。