队列的实现
我们在学习数据在内存中的存储方式时就会了解到堆栈和队列,堆栈和队列是数据存储的最常见的两种,那么,我现在来介绍一下堆栈和队列的异同点:
相同点:
栈与队列的相同点:
1.都是线性结构。
2.插入操作都是限定在表尾进行。
3.都可以通过顺序结构和链式结构实现。、
4.插入与删除的时间复杂度都是O(1),在空间复杂度上两者也一样。
5.多链栈和多链队列的管理模式可以相同。
不同点:
1、队列先进先出,栈先进后出;
2、遍历数据速度不同:
栈只能从头部取数据,也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性
队列则不同,它基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多
3、删除数据元素的位置不同:
栈的删除操作在表尾进行
队列的删除操作在表头进行。
相比而言,最普遍、最实用的不同点在于 数据存储时:
堆栈是先入后出,
而队列则是先入先出
关于堆栈的知识,本人将在本人的另一篇博文——《堆栈的实现》中进行讲解。
那么,现在本人来用C语言方式实现队列的存储结构:
首先,因为我们在之后的代码中会用到boolean类型,所以,我们要编写"mec.h"头文件:
#ifndef _MEC_H_
#define _MEC_H_
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
#define MAX_QUEUE_CAPACITY 100000 //这个宏本来应该放在主函数或者队列的相应的.h文件中定义,但为了缩短代码量,本人就将这个宏放在mec.h头文件中
#define SET(v, i) (v |= (1 << ((i) ^ 7)))
#define CLR(v, i) (v &= ~(1 << ((i) ^ 7)))
#define GET(v, i) (((v) & (1 << ((i) ^ 7))) != 0)
#endif
我们来构建一个结构体:
typedef struct QUEUE {
void **data; //这里的void **类型是为了方便各种类型的数据存储
int capacity; //这个成员表示数据容量,便于之后对data 的初始化
int head; //这个成员用于表示头节点的位置
int tail; //这个成员用于表示尾节点的位置
boolean lastAction; //这个成员用于表示上一个指令时入队列还是出队列,重要性将会在之后的代码中体现
}QUEUE;
我们要处理数据,那么第一步就是初始化数据,现在,我们来编写初始化函数:
因为可能会传递错误的指针,所以可能会初始化失败,所以返回值类型为boolean类型,至于参数,当然是队列(结构体数组)的指针以及队列的数据容量,相关代码如下:
boolean initQueue(QUEUE **queueHead, const int capacity) {
if (NULL == queueHead || NULL != *queueHead
|| capacity <= 0 || capacity > MAX_QUEUE_CAPACITY) { //这里一定要注意"MAX_QUEUE_CAPACITY"是宏,需要使用者在使用时自己在头文件或者主函数中定义,本人将其定义放在了mec.h的头文件中,以便减少代码量
return FALSE;
} //这里是防止传递空指针,或者原来传递的指针所指向的空间已被赋过值,造成内存泄漏
*queueHead = (QUEUE *) calloc(sizeof(QUEUE), 1);
if (NULL == *queueHead) {
return FALSE;
}
(*queueHead)->data = (void **) calloc(sizeof(void *), capacity);
if (NULL == (*queueHead)->data) {
free(*queueHead);
*queueHead = NULL;
return FALSE;
}
(*queueHead)->capacity = capacity;
(*queueHead)->head = (*queueHead)->tail = 0; //这里是让头节点和尾节点都是第一个节点
(*queueHead)->lastAction = OUT;
return TRUE;
}
那么,初始化完成了,我们就要立刻联想到销毁函数:
void destoryQueue(QUEUE **queueHead) {
if (NULL == queueHead || NULL == *queueHead) {
return;
}
free((*queueHead)->data);
free(*queueHead);
*queueHead = NULL;
}
因为我们基本操作是对于队列内数据的录入和取出,所以我们先编写判空、判满函数:
判空函数:
boolean isQueueEmpty(const QUEUE *queue) {
return queue != NULL && OUT == queue->lastAction && queue->head == queue->tail;
}
判满函数:
boolean isQueueFull(const QUEUE *queue) {
return queue != NULL && IN == queue->lastAction && queue->head == queue->tail;
}
那么,接下来就是录入和取出函数了:
录入函数:
boolean in(QUEUE *queue, const void *value) {
if (NULL == queue || isQueueFull(queue)) {
return FALSE;
}
queue->data[queue->tail] = value;
queue->tail = (queue->tail + 1) % queue->capacity;
queue->lastAction = IN;
return TRUE;
}
取出函数:
boolean out(QUEUE *queue, void **value) {
if (NULL == queue || isQueueEmpty(queue)) {
return FALSE;
}
*value = queue->data[queue->head];
queue->head = (queue->head + 1) % queue->capacity;
queue->lastAction = OUT;
return TRUE;
}
这些函数就是队列实现的最基本的函数了,剩下的任何操作 几乎都是在这几个函数的基础上实现的
至于堆栈的相关讲解以及实现,请观看本人的博文 ———— 《堆栈的实现》