数据结构与算法之如何基于顺序存储和链式存储设计一个队列

前言

  上一篇,我们学习了栈的结构,以及基于顺序存储链式存储两个不同角度如何设计一个栈,以及一些对的常规操作。
那么栈本篇来看一下队列的结构,以及如何基于顺序存储链式存储两个不同角度设计一个队列

1.  队列的结构

  队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。

  和一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

队列的示意图:

队列以顺序存储的方式存储时:

假如长度为 5 的队列,初始状态,空队列如所示,frontrear指针均指向下标为 0 的位置。然后依次入队C1C2C3front指针依然指向下标为 0 位置,而rear指针指向下标为 3 的位置。然后依次C1C2出队,移动front到 2 的位置,然后C4C5C6入队,C3出队,就会形成下面图的形式


这时,rear后面没有可以入队的空间,由于队列的规则,只能从一段入队,导致front前面的位置无法存储数据,而形成队列已满的形式,我们称这种情况为 假溢出

为了防止 假溢出,我们可以设计一个循环队列,如下图:

我们可以牺牲一个存储单元,来判断是否满对。

判断空对:Q.front = Q.rear

判断满对:Q.front = (Q.rear + 1) % MaxSize

2.  顺序存储队列的设计

2.1  顺序存储队列的设计
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXSIZE 20  // 存储空间初始化分配量

typedef int Status;
typedef int QElemType;

// 循环队列的顺序存储结构
typedef struct {
    QElemType data[MAXSIZE];
    int front;  // 头指针
    int rear;   // 尾指针,若队列不空,指向队列尾元素的下一个位置
}SqQueue;

判断空对:Q.front = Q.rear

判断满对:Q.front = (Q.rear + 1) % MaxSize

2.2  顺序存储队列的基本操作
  • 初始化一个空队列
// 初始化一个空队列
Status InitQueue(SqQueue *Q) {
    Q->front = 0;
    Q->rear  = 0;
    return OK;
}
  • 清空队列
// 清空队列
Status ClearQueue(SqQueue *Q) {
    Q->front = Q->rear = 0;
    return OK;
}
  • 判断队列是否为空
Status QueueEmpty(SqQueue Q) {
    if (Q.front == Q.rear) { // 空队列标记
        return TRUE;
    } else {
        return FALSE;
    }
}
  • 返回元素个数,即:队列的当前长度
int QueueLength(SqQueue Q) {
    // + MAXSIZE,循环队列,防止出现负数
    return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
  • 返回队列Q的队头元素
Status GetHead(SqQueue Q, QElemType *e) {
    // 判断队列是否为空
    if (Q.front == Q.rear) {
        return ERROR;
    }
    *e = Q.data[(Q.front)];
    return OK;
}
  • 入队
Status EnQueue(SqQueue *Q, QElemType e) {
    // 判断是否队列已满
    if ((Q->rear + 1) % MAXSIZE == Q->front) {
        printf("队列已满\n");
        return ERROR;
    }
    // 将元素赋值给队尾
    Q->data[Q->rear] = e;
    // rear指针后移,若到最后则转到数组头部
    Q->rear = (Q->rear + 1)%MAXSIZE;
    return OK;
}
  • 出队
Status DeQueue(SqQueue *Q, QElemType *e) {
    // 判断是否为空
    if (Q->rear == Q->front) {
        return ERROR;
    }
    // 队头元素赋值给e
    *e = Q->data[Q->front];
    
    //front 指针向后移动一位,若到最后则转到数组头部
    Q->front = (Q->front+1)%MAXSIZE;
    return OK;
}
  • 遍历队列元素
Status QueueTraverse(SqQueue Q) {
    int i;
    i = Q.front;
    while (i != Q.rear) {
        printf("%d  ", Q.data[i]);
        i = (i + 1)%MAXSIZE;
    }
    printf("\n");
//    int e;
//    for (i = Q.front; i<Q.rear; i++){
//        e = Q.data[i];
//        printf(" %d  ", e);
//    }
//    printf("\n");

    return OK;
}

3.  链式存储队列的设计

3.1  链式存储队列的设计

链式存储的队列,需要设计一个节点

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;
typedef int QElemType;

// 节点结构
typedef struct QNode {
    struct QNode *next;
    QElemType     data;
}QNode, *QueuePtr;

// 队列的链表结构
typedef struct {
    QueuePtr front;  // 头指针
    QueuePtr rear;   // 尾指针
}LinkQueue;
3.2  链式存储队列的基本操作
  • 初始化队列
Status InitQueue(LinkQueue *Q) {
    
    // 头尾指针指向新节点
    Q->front = Q->rear = (QueuePtr )malloc(sizeof(QNode));
    // 判断是否创建成功
    if (!Q->front) {
        return ERROR;
    }
    // 头结点的指针域置空
    Q->front->next = NULL;

    return OK;
}

  • 销毁队列
Status DestoryQueue(LinkQueue *Q) {
    // 遍历节点,释放
    while (Q->front) {
        // 尾指针已经失去意义,作为临时遍历使用
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    return OK;
}
  • 队列置空
Status ClearQueue(LinkQueue *Q) {
    QueuePtr p,q;
    // 头尾指针相等
    Q->rear = Q->front;
    p = Q->front->next;
    Q->front->next = NULL;
    
    while (p) {
        q = p;
        p = p->next;
        free(q);
    }
    return OK;
}
  • 判断是否为空,即判断头尾节点是否相等
Status QueueEmpty(LinkQueue Q){
    if (Q.front == Q.rear)
        return TRUE;
    else
        return FALSE;
}
  • 获取长度
int QueueLength(LinkQueue Q) {
    int i = 0;
    QueuePtr p = Q.front;
    while (p != Q.rear) {
        i ++;
        p = p->next;
    }
    return i;
}

也可以在设计队列的时候,加一个变量count入队出队+1-1,来计数。

  • 入队 不用判断是否满队
Status EnQueue(LinkQueue *Q, QElemType e) {
    // 创建新节点
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
    if (!s) {
        return ERROR;
    }
    
    s->data = e;
    s->next = NULL;
    
    // 出入队尾
    Q->rear->next = s;
    
    //修改队尾指针
    Q->rear = s;
    
    return OK;
}

  • 出队 需要判断是否为空
Status DeQueue(LinkQueue *Q, QElemType *e) {

    QueuePtr p;
    // 判断空
    if (Q->front == Q->rear) {
        return ERROR;
    }
    
    p = Q->front->next;
    *e= p->data;
    
    Q->front->next = p->next;
    
    //若队头就是队尾,则删除后将rear指向头结点.
    if(Q->rear == p) Q->rear = Q->front;
    
    free(p);
    return OK;
}

  • 获取头元素
Status GetHead(LinkQueue Q,QElemType *e) {
    //队列非空
    if (Q.front != Q.rear) {
        //返回队头元素的值,队头指针不变
        *e =  Q.front->next->data;
        return TRUE;
    }
    
    return  FALSE;
}
  • 遍历元素
Status QueueTraverse(LinkQueue Q){
    
    QueuePtr p;
    p = Q.front->next;
    while (p) {
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}
···
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值