队列
队列:结构定义
队列是有一篇连续的存储区,其实连续性不重要,而是队列需要保持一个特性:
从队首出元素,从队尾入元素。这一点与顺序表不一样,元素加入的位置不一样
队列:只允许从尾部加入元素,从头部去除元素
顺序表:允许在任意位置加入或者删除元素
队列的属性:
size, 记录队列的总大小
head, 指向队首的指针, 包含队首
tail,指向队尾的指针,不包含队尾
先进先出,first in - first out(FIFO)
队列:出队(Pop)
队列只允许从队首出队
队列:入队(Push)
只允许从队尾入队
队列:假溢出
最简单的队列是有问题的
假设入队了7, 8, 9三个元素,要入队10号元素
队列已经“满了”,入不了队
实际上只存储了6个元素,但队列的总大小是9
提出一种新的队列结构:循环队列
当tail超出范围的时候,就让tail指向0号位置
队列:循环队列
当tail超出范围的时候,就让tail指向0号位置
多了一个属性项:
count: 记录的是队列中当前一共存放了多少个元素
入队:
循环队列是为了解决队列的假溢出问题
队列:顺序表的实现
队列结构定义: typedef struct Queue{}Queue;
// 队列
typedef struct Queue{
Vector * data;
int size, head, tail, count; // 循环队列
}Queue;
初始化: int * initQueue(int n) {}, 其中使用了顺序表的初始化函数initVector()
// 初始化
Queue * initQueue(int n)
{
Queue *q = (Queue *)malloc(sizeof(Queue));
q->data = initVector(n); // 用顺序表去初始化
q->size = n;
q->head = q->tail = q->count = 0;
return q;
}
判空操作: int empty(Queue *q) {}
// 判空操作
int empty(Queue *q)
{
return q->count == 0;
}
查看队首元素: int frontQueue(Queue *q) {}, 其中用到了顺序表的查找操作vectorSeek()函数
// 查看队首元素
int frontQueue(Queue *q)
{
return vectorSeek(q->data, q->head); // vectorSeek(data, i), 得到顺序表data的第i个元素
}
压入队列: int push(Queue *q, int val) {}, 其中用到了顺序表的插入操作insertVector()函数
// 压入队列
int push(Queue *q, int val)
{
if (q->size == q->count) return 0;
// 把val放到tail所指向的位置
insertVector(q->data, q->tail, val);
q->tail += 1;
// 判断tail是否指向非法位置
if (q->tail == q->size) q->tail = 0; // 队满了后让tail重新指向队首
q->count += 1;
return 1;
}
弹出队列: int pop(Queue *q) {}
// 弹出队列
int pop(Queue *q)
{
if (empty(q)) return 0;
eraseVector(q->data, q->head); // 删掉head指向的元素
q->head += 1;
//判断head是否越界
if (q->head == q->size) q->head = 0; // head重新指向队首
q->count -= 1;
return 1;
}
**销毁:**void clearQueue(Queue *q) {},其中用到了顺序表的销毁函数clearVector()
// 销毁
void clearQueue(Queue *q)
{
if (q == NULL) return;
clearVector(q->data);
free(q);
return;
}
下面实现顺序表:
顺序表结构定义: typedef struct Vector{}Vector;
// 顺序表
typedef struct Vector{
int size, count;
int *data;
} Vector;
初始化顺序表: Vector * initVector(int n) {}
// 初始化
Vector * initVector(int n)
{
Vector *v = (Vector *)malloc(sizeof(Vector));
v->data = (int *)malloc(sizeof(int)* n);
v->size = n;
v->count = 0;
return v;
}
查找顺序表: int vectorSeek(Vector *v, int pos) {}
// 查找顺序表
int vectorSeek(Vector *v, int pos)
{
if (pos < 0 || pos >= v->size) return -1;
if (v->count > 0) return v->data[pos];
return 0;
}
插入元素: int insertVector(Vector *v, int pos, int val)
// 插入元素
int insertVector(Vector *v, int pos, int val)
{
if (pos < 0 || pos >= v->size) return 0;
// 由于pos只能在队尾插入,只要挪一位就行
v->data[pos] = val;
v->count += 1;
return 1;
}
删除元素: int eraseVector(Vector *v, int pos) {}
// 删除元素
int eraseVector(Vector *v, int pos)
{
if (pos < 0 || pos >= v->size) return 0;
v->data[pos] = 0;
v->count -= 1;
return 1;
}
销毁顺序表: void clearVector(Vector *v){}
// 销毁顺序表
void clearVector(Vector *v)
{
if (v == NULL) return;
free(v->data);
free(v);
return;
}
主程序:
void outputQueue(Queue *q)
{
printf("Queue: ");
for (int i = 0; i < q->count; i++)
{
printf("%4d ", vectorSeek(q->data, (q->head + i) % q->size));
}
printf("\n\n");
}
int main()
{
srand(time(0));
#define MAX_OP 10
Queue *q = initQueue(MAX_OP);
for (int i = 0; i < MAX_OP; i++)
{
int op = rand() % 5, val = rand() % 100; // 0, 1: pop; 2, 3, 4: push
switch (op)
{
case 0:
case 1:
// 出队列之前查看一下队首元素
printf("front of queue: %d\n", frontQueue(q));
pop(q);
break;
case 2:
case 3:
case 4:
printf("push %d to queue\n", val);
push(q, val);
break;
}
outputQueue(q);
}
clearQueue(q);
std::cin.get();
return 0;
}
测试结果
队列:链表的实现
队列的结构定义: typedef struct Queue{}Queue;
使用链表实现,不在乎存储空间的大小,也就不存在队列假溢出的问题,实际上也不需要头尾指针了,但是还是可以记录一个count
typedef struct Queue{
LinkList * l;
int count;
} Queue;
初始化队列: Queue *initQueue() {}
Queue *initQueue()
{
Queue *q = (Queue*)malloc(sizeof(Queue));
q->count = 0;
q->l = initLinkList();
return q;
}
判空操作: int empty(Queue *q) {}
// 判空操作
int empty(Queue *q)
{
return q->count == 0;
}
查看队首元素: int frontQueue(Queue *q) {}
// 查看队首元素
int frontQueue(Queue *q)
{
if (empty(q)) return 0;
return frontLinkList(q->l);
}
压入队列: int push(Queue *q, int val) {}
不用判断队列是否满了,直接入队操作
// 压入队列
int push(Queue *q, int val)
{
// 不用判断队列是否满了,直接入队操作
insertTail(q->l, val);
q->count += 1;
return 1;
}
弹出队列: int pop(Queue *q) {}
// 弹出队列
int pop(Queue *q)
{
if (empty(q)) return 0;
eraseHead(q->l);
q->count -= 1;
return 1;
}
销毁队列: void clearQueue(Queue *q) {}
void clearQueue(Queue *q)
{
if (q == NULL) return;
clearLinkList(q->l);
free(q);
return;
}
下面实现链表:
链表节点定义: typedef struct Node{ } Node;
typedef struct Node{
int data;
Node * next;
} Node;
链表结构定义: typedef struct LinkList{ } LinkList;
// 链表结构定义
typedef struct LinkList{
Node head, *tail; // 有头链表
}LinkList;
表节点的初始化: Node *getNewNode(int val) {}
// 链表节点的初始化
Node *getNewNode(int val)
{
Node *p = (Node *)malloc(sizeof(Node));
p->data = val;
p->next = NULL;
return p;
}
初始化链表: LinkList * initLinkList() {}
// 初始化链表
LinkList * initLinkList()
{
LinkList *l = (LinkList *)malloc(sizeof(LinkList));
l->head.next = NULL;
l->tail = &(l->head);
return l;
}
判空操作: int emptyList(LinkList *l) {}
// 判空操作
int emptyList(LinkList *l)
{
return l->head.next == NULL;
}
查找队首元素: int frontLinkList(LinkList *l) {}
// 查找队首元素
int frontLinkList(LinkList *l)
{
if (emptyList(l)) return 0;
return l->head.next->data; //返回
}
链表尾部插入元素: int insertTail(LinkList *l, int val) {}
// 在链表尾部插入元素
int insertTail(LinkList *l, int val)
{
Node *node = getNewNode(val); // 封装成链表的节点
// 节点添加到链表尾部
l->tail->next = node;
l->tail = node;
return 1;
}
删掉链表头节点: int eraseHead(LinkList *l) {}
// 删掉链表头节点
int eraseHead(LinkList *l)
{
if (emptyList(l)) return 0;
Node *p = l->head.next;
l->head.next = p->next;
// 还需要考虑只有一个节点的情况,此时tail指向这个节点,要将tail指向head
if (p == l->tail) l->tail = &(l->head);
free(p);
return 1;
}
销毁链表: void clearLinkList(LinkList *l) {}
// 销毁链表
void clearLinkList(LinkList *l)
{
Node *p = l->head.next, *q;
// 先销毁头节点后面的所有节点
while (p)
{
q = p->next; // 用q记录p的下一个节点
free(p);
p = q;
}
free(l);
return;
}
主程序:
// 输出队列
void outputQueue(Queue *q)
{
printf("Queue: ");
// 用一个p指针来遍历链表
Node *p = q->l->head.next;
for (int i = 0; i < q->count; i++)
{
printf("%4d", p->data);
p = p->next;
}
printf("\n\n");
}
int main()
{
srand(time(0));
#define MAX_OP 10
Queue *q = initQueue();
for (int i = 0; i < MAX_OP; i++)
{
int op = rand() % 5, val = rand() % 100; // 0, 1: pop; 2, 3, 4: push
switch (op)
{
case 0:
case 1:
// 出队列之前查看一下队首元素
printf("front of queue: %d\n", frontQueue(q));
pop(q);
break;
case 2:
case 3:
case 4:
printf("push %d to queue\n", val);
push(q, val);
break;
}
outputQueue(q);
}
clearQueue(q);
std::cin.get();
return 0;
}