数据结构 ⑦ 队列

159 阅读4分钟

队列的概念

    队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出线性表。 队列

顺序队列

建立顺序队列结构必须为其静态分配或动态申请一片连续的存储空间,并设置两个指针进行管理。一个是队头指针front,它指向队头元素;另一个是队尾指针rear,它指向下一个入队元素的存储位置。

"假上溢"现象**

由于入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用。当队列中实际的元素个数远远小于向量空间的规模时,也可能由于尾指针已超越向量空间的上界而不能做入队操作。该现象称为"假上溢"现象。

顺序队列-假溢出

循环队列

在实际使用队列时,为了使队列空间能重复使用,往往对队列的使用方法稍加改进:无论插入或删除,一旦rear指针增1或front指针增1 时超出了所分配的队列空间,就让它指向这片连续空间的起始位置。
  从`MaxSize-1`1变到0,可用取余运算`rear%MaxSize``front%MaxSize`来实现。
在循环队列中,队列判空的条件时`front=rear`,而队列判满的条件时`front=(rear+1)%MaxSize`,实际使用空间为 `MaxSize - 1`

代码实现

结构
typedef struct 
{
    int data[MaxSize];//数据
    int front;//队头
    int rear;//队尾
}SeQueue;
初始化(初始条件:队列 不存在。操作结果:构造了一个空队;)
SeQueue *Init_queue(){

    SeQueue *queue = (SeQueue *)malloc(sizeof(MaxSize));
    if (queue == NULL)
    {
        printf("初始化队列失败\n");
        return NULL;
    }
    queue->front = 0;
    queue->rear = 0;
    printf("初始化队列成功\n");
    return queue;
}
入队(初始条件: 队列存在且队未满。操作结果: 对已存在的队列q,插入一个元素x 到队尾,队发生变化;)
void Insert_queue(SeQueue *queue,int value){

    if ((queue->rear + 1) % MaxSize == queue->front)
    {
        printf("队满\n");
        return;
    }
    queue->data[queue->rear] =  value;
    queue->rear = (queue->rear + 1) % MaxSize;
    printf("%d\n", queue->rear);
}
出队 (初始条件: 队列存在且非空,操作结果: 删除队首元素,并返回其值,队发生变化;)
void delete_queue(SeQueue *queue){
    if (queue->rear == queue->front)
    {
    printf("队空\n");
    return;
    }
    queue->front = (queue->front + 1) % MaxSize;    
}
遍历
void queueDescription(SeQueue queue){

for (int i = queue.front; i != queue.rear; i=(i+1)%MaxSize)
{
printf("i=%d--data=%d   ", i,queue.data[i]);
}
    printf("\n");
}
清空队列
void clearQueue(SeQueue *queue){
    queue->front = 0;
    queue->rear = 0;
    printf("清空队列\n");
}
获取当前队列长度
 int getQueueLength(SeQueue queue){
    return (queue.rear - 0 + MaxSize - queue.front) % MaxSize ;
}

链式队列

在队列的形成过程中,可以利用线性链表的原理,来生成一个队列。

基于链表的队列,要动态创建和删除节点,效率较低,但是可以动态增长。

每次读取一个元素,释放一个元素。所谓的动态创建,动态释放。因而也不存在溢出等问题。由于链表由结构体间接而成,遍历也方便。

链式队列

代码实现

结构
typedef struct SeQueueNode
{
    int data;//数据
    struct SeQueueNode * next;
}SeQueueNode,*LinkQueueNode;

typedef struct 
 {
    LinkQueueNode rear;
    LinkQueueNode front;
}LinkQueue;
初始化(初始条件:队列 不存在。操作结果:构造了一个空队;)
LinkQueue init_LinkQueue(){
    LinkQueue *queue = (LinkQueue *)malloc(sizeof(LinkQueue));
    queue->rear = queue->front = (LinkQueueNode)malloc(sizeof(LinkQueueNode));
    return *queue;
}
入队(初始条件: 队列存在。操作结果: 对已存在的队列q,插入一个元素x 到队尾,队发生变化;)
void insert_LinkQueue(LinkQueue *Q,int data){

    LinkQueueNode node = (LinkQueueNode )malloc(sizeof(LinkQueueNode));
    if (node == NULL)
    {
    //创建失败
    return;
    }
    node->next = NULL;
    node->data = data;
    Q->rear->next = node;
    Q->rear = node;
}
出队 (初始条件: 队列存在且非空,操作结果: 删除队首元素,并返回其值,队发生变化;)
 void delete_LinkQueue(LinkQueue *Q){

    if (Q->front == Q->rear)
    {
    return;
    }
    LinkQueueNode node = Q->front->next;

    Q->front->next = node->next;

    if(Q->rear == node) Q->rear = Q->front;

    free(node);
}
遍历
void LinkQueueTraverse(LinkQueue Q){
    LinkQueueNode node = Q.front->next;
    while(node){
        printf("%d", node->data);
        node = node ->next;
    }
    printf("\n");
}
清空队列
void ClearQueue(LinkQueue *Q){

    LinkQueueNode node = Q->front->next;
    Q->rear = Q->front;
    Q->front->next = NULL;
    while (node) {

        LinkQueueNode temp = node;
        node = node->next;
        free(temp);

    }
}
获取当前队列长度
int getLinkQueueLength(LinkQueue Q){
    int i= 0;
    LinkQueueNode p;
    p = Q.front;
    while (Q.rear != p) {
        i++;
        p = p->next;
    }
    return i;
}