目录
前言
数据结构和算法整体框架图:
今天归纳总结的是线性表中的队列。
1、队列的定义
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出的线性表,简称FIFO,允许插入的一端称为队尾,允许删除的一端称为队头。
2、队列顺序存储结构—循环队列
因为线性表有顺序存储结构和链式存储结构,队列属于线性表中的一种,因此它同样具有这两种存储方式。
2.1 定义
头尾相接的顺序存储结构称为循环队列。
引入两个指针,front指针指向队头元素,rear指针指向队尾元素的下一个位置。
队列满的条件是:数组中还有一个空闲单元。总共有两种情况:rear可能比front大,也可能比front小。
因此队列满的条件是:(rear + 1)% QueueSize == front。
同理计算长度时,也需要考虑上述两种情况。
因此队列当前长度公式:(rear - front + QueueSize ) % QueueSize
2.2 数据结构
// 循环队列的顺序存储结构
typedef struct
{
int data[100]; // 队列空间
int front; // 头指针
int rear; // 尾指针
}SqQueue;
// 循环队列的初始化代码,初始化一个空队列
Status InitQueue(SqQueue* Q)
{
Q->front = 0;
Q->rear = 0;
return OK;
}
// 求取循环队列的当前长度
int QueueLength(SqQueue* Q)
{
return (Q->rear - Q->front + QueueSize) % QueueSize;
}
// 循环队列的入队
Status EnQueue (SqQueue* Q,int e)
{
if ((Q->rear + 1) % QueueSize == Q->front) // 队列满判断
return ERROR;
Q->data[Q->rear] = e; // 将元素e赋值给队尾。
Q->rear = (Q->rear + 1)% QueueSize; // rear指针向后移动一位,如果到最后则移动到数组头部。
return OK;
}
// 循环队列的出队操作
Status DeQueue (SqQueue* Q,int* e)
{
if (Q->front == Q->rear) // 队列空判断
{
retrun ERROR;
}
*e = Q->data[Q->front];
Q->front= (Q->front+ 1)% QueueSize; // front指针向后移动一位,如果到最后则移动到数组头部。
return OK;
}
如果但是顺序存储结构,不是循环队列,算法的时间性能不高。但是循环队列面临数组可能会溢出的问题。这是它的不足。
3、队列链式存储结构
3.1 定义
队列的链式存储结构,其实就是线性表的单链表,只不过只能尾进头出。将队头指针指向链表的头结点,队尾指针指向终端节点。如下图所示
空队列时,front和rear都指向头结点,如下图所示
3.2 数据结构
typedef struct QNode // 节点结构
{
int data;
struct QNode* next;
}QNode,*QueuePtr;
typedef struct // 队列的链表结构
{
QueuePtr front,rear; // 队头、队尾指针
}LinkQueue;
3.2.1 入队
// 入队操作 -- 尾插
Status EnQueue (LinkQueue* Q,int e)
{
QueuePtr s = malloc (sizeof (QNode));
if (!s)
{
exit(OVERFLOW);
}
s->data = e;
s->next = NULL;
Q->rear->next = s;
Q->rear = s;
return OK;
}
3.2.2 出队
// 出队操作
Status DeQueue(LinkQueue* Q,int* e)
{
QueuePtr p;
if (Q->front == Q->rear)
return EEROR;
p = Q->front->next; // 想要删除的节点
*e = p->data; // 想要删除的节点的值
Q->front->next = p->next; // 更新队列头结点的后继节点
if (Q->rear == p) // 如果队列中队头即是队尾,则删除后需要更新队尾节点,将其指向头结点,如上图中右图所示
{
Q->rear = Q->front;
}
free(p);
return OK;
}
4、实战
4.1 用栈实现队列
1、题目描述:
解决方案:
//队列链式存储结构节点
typedef struct QueueNode{
int data;
struct QueueNode* next;
}node,*QueuePtr;
// 队列的链表结构
typedef struct {
QueuePtr front,rear;
} MyQueue;
/** Initialize your data structure here. */
MyQueue* myQueueCreate() {
QueuePtr queue1 = malloc(sizeof(node)); // 头结点
queue1->next = NULL; // 空队列头节点的next指向为NULL
MyQueue* queue = malloc(sizeof (MyQueue));
queue->front = queue->rear = queue1;// 空队列的条件
return queue;
}
/** Push element x to the back of queue. */
// 建议记忆入队的常规操作。
void myQueuePush(MyQueue* obj, int x) {
QueuePtr MyQueuenode = malloc(sizeof(node));
MyQueuenode->data = x;
MyQueuenode->next = NULL; // 给申请的新节点赋值
obj->rear->next = MyQueuenode; // 因为是队列,所以采用尾插
obj->rear = MyQueuenode;
}
/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
if (obj->front == obj->rear)
{
return -1;
}
QueuePtr Queuenode = obj->front->next;
int data = Queuenode->data;
obj->front->next = Queuenode->next;
if (Queuenode == obj->rear) {
obj->rear = obj->front; // 出队操作的注意事项
}
free(Queuenode);
return data;
}
/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
return obj->front->next->data;
}
/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
return obj->front == obj->rear;
}
void myQueueFree(MyQueue* obj) {
while ( obj->front->next != NULL && obj->front != obj->rear){
QueuePtr p = obj->front->next;
obj->front->next = p->next;
free(p);
}
}
/**
* Your MyQueue struct will be instantiated and called as such:
* MyQueue* obj = myQueueCreate();
* myQueuePush(obj, x);
* int param_2 = myQueuePop(obj);
* int param_3 = myQueuePeek(obj);
* bool param_4 = myQueueEmpty(obj);
* myQueueFree(obj);
*/
4.2 用队列实现栈
题目描述:
解决方案:
typedef struct listNode{
int data;
struct listNode* next;
}ListNode;
typedef struct {
struct listNode* top; // 栈属性
int count;
} MyStack;
/** Initialize your data structure here. */
MyStack* myStackCreate() {
MyStack* Stacks = calloc(1,sizeof (MyStack));
Stacks->top = NULL;
Stacks->count = 0; // 空栈
return Stacks;
}
/** Push element x onto stack. */
void myStackPush(MyStack* obj, int x) {
ListNode* node = malloc (sizeof (ListNode));
node->data = x;
node->next = obj->top;
obj->top = node;
obj->count++;
}
/** Removes the element on top of the stack and returns that element. */
int myStackPop(MyStack* obj) {
ListNode* node = obj->top;
obj->top = obj->top->next;
obj->count--;
int a = node->data;
free(node);
return a;
}
/** Get the top element. */
int myStackTop(MyStack* obj) {
return obj->top->data;
}
/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack* obj) {
return obj->count == 0;
}
void myStackFree(MyStack* obj) {
ListNode* p;
while (obj->count > 0)
{
p = obj->top;
obj->top = obj->top->next;
obj->count--;
free(p);
}
}
/**
* Your MyStack struct will be instantiated and called as such:
* MyStack* obj = myStackCreate();
* myStackPush(obj, x);
* int param_2 = myStackPop(obj);
* int param_3 = myStackTop(obj);
* bool param_4 = myStackEmpty(obj);
* myStackFree(obj);
5、栈和队列的总结对比
参考之前的系列博文
数据结构和算法系列第2篇—栈的基本操作
栈:限定在表尾进行插入和删除操作的线性表。
队列:限定在一端进行插入操作,而在另一端进行删除操作的线性表。
他们都可以用线性表的顺序存储结构和链式存储结构来实现。