数据结构---栈和队列详解(下)
引言:
大家在使用电脑时有没有经历过,机器有时会处于疑似死机状态,鼠标点什么似乎都没用,双击任何快捷方式都不动弹。就当你失去耐心,打算reset时突然它就像醒酒了一样,把你刚才单击的所有操作都按顺序执行了一遍。这是因为操作系统在当时可能cpu一时忙不过来,等前面的事情忙完后,后面多个指令需要通过一个通道输出,按先后次序排队执行造成的结果。再比如,像移动、联通、电信等电话客服,客服人员和客户相比总是少数的,在所有客服人员都占线的情况下,客户会被要求等待,直到某个客服人员空下来,才能让最先开始等待的客户接通电话,这里也是将当前所有拨打客服电话的客户进行了排队处理。
操作系统和客服系统中,都是应用了一种数据结构,来实现方才提到的先进先出的排队功能,这就是队列。
一、队列的定义
1、队列是一种先进先出(First in first out)的线性表,简称:FIFO;允许插入的一端称为队尾,允许删除的一端叫做队头。
2、队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
二、队列的实现思路
首先我们用链表来实现队列,队列的结构是由一个个的节点组成的,定义俩个指针,一个指向队头,一个指向队尾。节点也需要定义结构体。
三、代码具体实现
1、定义队列结构
//1、先定义队列结点的结构
typedef int QDataType;
typedef struct QueueNode
{QDataType data;QueueNode* next;
}QueueNode;
//2、定义队列的结构
typedef struct Queue
{QueueNode* phead;//队列头的QueueNode* ptail;//队列的尾
}Queue;
2、队列结构初始化
//初始化
void QueueInit(Queue* pq)
{assert(pq);return pq->phead =pq->ptail= NULL;
}
3、判空
//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead ==NULL;
}
4、出队
//出队--队头
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、队列有效元素
//队列有效元素
int QueueSize(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;int size = 0;while (pcur){size++;pcur = pcur->next;}return size;
}
6、取队头元素
//取队头元素(数据)
QDataType QueueFront(Queue* pq)
{assert(!QueueEmpty(pq));return pq->phead->data;
}
7、取队尾元素
//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->ptail->data;
}
8、队列的销毁
//销毁队列
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、入队
//入队--队尾
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°的队列,如果你中途犹豫,临时转向,也许你就只能和企鹅相伴永远。可事实上,无论那个方向,只要你坚持到底,你都可以到达终点。