原文:http://www.jianshu.com/p/5d020c00fcb8
你还在为开发中频繁切换环境打包而烦恼吗?快来试试 Environment Switcher 吧!使用它可以在app运行时一键切换环境,而且还支持其他贴心小功能,有了它妈妈再也不用担心频繁环境切换了。https://github.com/CodeXiaoMai/EnvironmentSwitcher
上一章:程序猿必修课之数据结构(七)栈2
队列的定义
队列(Queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
队列的抽象数据类型
队列是特殊的线性表,因此它的各种操作类似线性表,不同的是插入数据只能在队尾进行,删除数据只能在队头进行。
ADT 队列(Queue)
Data
同线性表。元素的类型相同,相邻元素具有前驱和后继关系。
Operation
initQueue(*Q): 队列初始化,建立一个空队列。
destroyQueue(*Q): 若队列 Q 存在,就销毁它。
clearQueue(*Q): 将队列清空。
isEmpty(*Q): 若队列为空,返回 true;否则返回 false。
getHead(Q, *e): 若队列 Q 存在且不为空,则用 e 返回队列 Q 中的队头元素。
enQueue(*Q, e): 若队列 Que 存在,插入新元素 e 到队列 Q 中并成为队尾元素。
deQueue(*Q, *e): 删除队列 Q 中队头元素,并用 e 返回。
getLength(Q): 返回队列 Q 的元素个数。
endADT
循环队列
我们把头尾相接的队列的顺序存储结构称为循环队列。
用 front 指针指向队头元素,用 rear 指针指向队尾元素的下一个位置(为了避免数组越界,队尾元素后要留一个空闲单元,保存 rear 指针)。所以当 front == rear 时,表示队列为空;当数组中只有一个空闲单元时队列就满了。
因为是循环队列,所以 rear 可能比 front 大,也可能比 fornt 小,如下图所示:
可以看出,它们可能只相差一个位置,但也可能相差整整一圈。所以若队列的最大容量为 queueSize,那么队列满的条件是:(rear + 1) % queueSize == front。
当 rear > front 时(即左图),队列的长度为 rear - front;当 rear < front 时(即右图),队列的长度由两部分构成,一部分是 queueSize - front,另一部分是 0 + rear,所以队列长度为 rear - front + queueSize。所以通用的计算队列长度的公式为:
(rear - front + QueueSize) % queueSize
循环队列的顺序存储结构
define MAXSIZE 10
typedef struct {
int data[MAXSIZE];
int front;
int rear;
} Queue;
初始化:
void initQueue(Queue *q) {
q->front = 0;
q->rear = 0;
}
求队列长度
int queueLength(Queue q) {
return (q->rear - q->front + MAXSIZE) % MAXSIZE;
}
入队操作:
boolean enQueue(Queue *q, int e) {
/* 队列已满 */
if ((q->rear + 1) % MAXSIZE == q->front)
return false;
q->data[q->rear] = e;
q->rear = (q->rear + 1) % MAXSIZE;
return true;
}
出队操作:
boolean deQueue(Queue *q, int *e) {
/* 队列为空 */
if (q->front == q->rear)
return false;
*e = q->data[q->front];
q->front = (q->front + 1) % MAXSIZE;
return true;
}
虽然循环队列解决了出队时算法的时间复杂度不高的问题,但是它仍然有数组可能会溢出的问题,所以还需要链式存储结构来解决这个问题。
队列的链式存储结构及实现
队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,简称为链队列。
为了方便操作,队头指针指向链队列的头结点;尾指针指向终端结点。当 front 和 rear 都指向头结点时,队列为空。
链队列的结构为:
typedef struct QNode {
int data;
struct QNode *next;
}QNode, *pQueue;
typedef struct{
pQueue front;
pQueue rear;
}LinkQueue;
入队操作
boolean enQueue(LinkQueue *q, int e) {
pQueue s = (pQueue)malloc(sizeof(QNode));
if(!s)
return false;
s->data = e;
s->next = NULL;
q->rear->next = s;
q->rear = s;
return true;
}
出队操作
出队操作就是头结点的后继结点 p 出队,将头结点的后继改为 p 的后继结点,如果p 是 rear 指向的结点,那么还要将 rear 指向头结点。
boolean deQueue(LinkQueue *q, int *e) {
pQueue p;
if (q->front == q->rear)
return false;
p = q->front->next;
*e = p->data;
q->front->next = p->next;
if(q->rear == p)
q->rear = q->front;
free(p);
return true;
}
比较循环队列和链队列
从时间复杂度,它们的基本操作都是 O(1);但是循环队列需要先申请好空间,使用期间不释放,而对于链队列,每次申请和释放结点也会存在一些时间开销。
从空间上来说,循环队列必须有一个固定的长度,这样就可能存在空间浪费的问题;而链队列不存在这个问题,尽管它需要一个指针域。
总的来说,在可以确定队列长度最大值的情况下,建议用循环队列,反之用链队列。
下一章:程序猿必修课之数据结构(九)串