队列(下)
上篇文章最后,我们说到了在队列中,链表结构可以代替顺序表结构,从而形成链式队列。并阐述了如果采用链式队列,应该采用什么样的链表来实现,这篇文章将结束队列这部分内容,包括链式队列的具体实现和双端队列。
链式队列结构体和链式队列的初始化
struct node_q {
int data;
node_q *next;
};
struct queue_l {
node_q *front, *rear;
};
queue_l *new_queue() {
queue_l *q = new queue_l;
q->front = q->rear = new node_q;
q->front->next = NULL;
return q;
}
创建链式队列结构体首先要创建链表的结构,再创建链式队列。链表结点node_q就是常规的单链表结点,包括data和next指针;链式队列queue_l包括链式队列的队头指针front和队尾指针rear。
初始化链式队列函数首先new一个新的链式队列q,队列q的front和next都指向一个新node_q结点,同时让队列q的队头指针的next指针指向NULL,最后return队列q。
判队列空
bool isEmpty(queue_l q) {
if (q.front == q.rear) return true;
return false;
}
就是和之前一样的,很普通的判空操作,当front==rear时返回true。
入队列
void EnQueue(queue_l &q, int x) {
node_q *s = new node_q;
s->data = x;
s->next = NULL;
q.rear->next = s;
q.rear = s;
}
入队列是一种插入操作,所以本质上和单链表的插入是一样的。创建一个新的链表结点s,让s的data值等于参数x,再让s结点的next指向NULL,之后让队尾指针指向的结点的next指针指向结点s,最后把队尾指针指向s。基本上就是单链表尾插法插入节点的操作,而且看起来更加直观。
出队列
int DeQueue(queue_l &q) {
if (q.front == q.rear) return -9999;
node_q *s = q.front->next;
int x = s->data;
q.front->next = s->next;
if (q.rear == s) q.rear = q.front;
delete s;
return x;
}
出队列就是一种删除结点释放内存空间的操作,这里采用的int型函数,最后会返回已删除结点的data值。首先要对队列进行判空,如果队列是空的则返回-9999(代表队列中没有数据元素)。之后保存队头指针的next指针指向的结点(代表含有队头数据元素的结点),并用变量x保存该结点data的值。
让队头指针的next指针指向要删除结点的next指针指向的结点。如果删除后队列为空,则让队尾指针rear等于队头指针front,之后释放要删除结点的内存空间,最后返回变量x的值。
双端队列
双端队列,顾名思义,就是允许队列两端都可以进行插入和删除操作的队列,这也是狭义上的双端队列。
广义上的双端队列可以包括以下几种情况:
- 一端允许插入和删除,另一端只允许插入;
- 一端允许插入和删除,另一端只允许删除;
- 两端都允许插入和删除。
这些都可以称之为双端队列,在关于队列的问题中,常常会见到关于队列入队出队顺序的排列问题,不同情况下的双端队列也会影响到数据元素入队出队的次序。