目录
一、队列的概念及结构
队列:只允许在一端进行数据的插入,在另一端进行数据的删除操作的特殊线性表,队列的特点是先进先出,进行插入数据的一端称为队尾,删除数据的一端称为队头。
二、队列的实现
队列同样可以用数组或者链表来实现,但是我们这里就不采取数组的方法,因为队列需要不断的头删,用数组效率较低,而单链表却特别适合在队头出数据。
1.队列的结构体
队列的结构体我们有两个部分:一是每一个的节点,二是用来存储头尾指针以及size的结构体
//队列建议用单链表实现,因为不仅需要头删,还需要尾插,所以我们还要添加一个头尾指针方便操作
typedef int Datatype;
typedef struct QueueNode
{
Datatype data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
int size;
}Queue;
2.队列的初始化
void QueueInit(Queue* queue)
{
assert(queue);
queue->head = NULL;
queue->tail = NULL;
queue->size = 0;
}
3.队列的判空
bool QueueEmpty(Queue* queue)
{
assert(queue);
return queue->size == 0;
}
4.队列的插入数据
void QueuePush(Queue* queue,Datatype x)
{
assert(queue);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
newnode->data = x;
newnode->next = NULL;
if (newnode==NULL)
{
perror("malloc");
return;
}
//如果是空链表,头插
if (queue->head==NULL)
{
queue->head = newnode;
queue->tail = newnode;
}
else
{
queue->tail->next = newnode;
queue->tail = newnode;
}
queue->size++;
}
5.队列的删除数据
void QueuePop(Queue* queue)
{
assert(queue);
//头删
//如果是空不能Pop
assert(!QueueEmpty(queue));
//一个节点
if (queue->head->next==NULL)
{
free(queue->head);
queue->head = queue->tail=NULL;
}
//多个节点(因为有tail的存在,必须要分类讨论)
else
{
QueueNode* second = queue->head->next;
free(queue->head);
queue->head = second;
}
queue->size--;
}
6.查看队头的数据
Datatype QueueFront(Queue* queue)
{
assert(queue);
assert(!QueueEmpty(queue));
return queue->head->data;
}
7.查看队尾的数据
Datatype QueueBack(Queue* queue)
{
assert(queue);
assert(!QueueEmpty(queue));
return queue->tail->data;
}
8.求出队列的元素个数
int Queuesize(Queue* queue)
{
assert(queue);
return queue->size;
}
9.队列的销毁
void QueueDestroy(Queue* queue)
{
assert(queue);
QueueNode* cur = queue->head;
while (cur!=NULL)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
queue->head = queue->tail = NULL;
queue->size = 0;
}
三、队列相关的oj题
1.思路
这是一道初学队列时候的经典题目,他能让我们很好的理解队列的相关特点。针对这道题目,我们的思路是:如果Push就让数据先都入一个队列中,如果要出栈就只保留一个数据在当前队列中,先把其余的导入到另外一个队列中,把剩下的那个出栈即可。由于队列的性质,数据的来回倒,并不会影响数据的顺序,从而用两个队列实现一个栈!
2.完整答案
由于这道题目并没有什么特殊的情况,所以我们这里直接给出结果
//定义MyStack结构体
typedef struct
{
Queue q1;
Queue q2;
} MyStack;
//创建一个MyStack栈
MyStack* myStackCreate()
{
MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
if(obj==NULL)
{
perror("malloc");
return NULL;
}
//初始化
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
//压栈
void myStackPush(MyStack* obj, int x)
{
//在有数据的队列才能进行插入数据
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
//出栈
int myStackPop(MyStack* obj)
{
//找到没有数据和有数据的队列
Queue* PEmpty=&obj->q1;
Queue* PNoEmpty=&obj->q2;
//如果假设错误了,就换过来
if(QueueEmpty(PNoEmpty))
{
PNoEmpty=&obj->q1;
PEmpty=&obj->q2;
}
//把非空的队列移动到另外一个,直到只剩下一个数据
while(Queuesize(PNoEmpty)>1)
{
QueuePush(PEmpty,QueueFront(PNoEmpty));
QueuePop(PNoEmpty);
}
int top=QueueFront(PNoEmpty);
QueuePop(PNoEmpty);
return top;
}
//查看栈顶的数据
int myStackTop(MyStack* obj)
{
//返回非空的队列的尾
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
//判断栈是否为空
bool myStackEmpty(MyStack* obj)
{
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj)
{
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
/**
* 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);
*/