从0开始学习数据结构-队列的学习②

204 阅读4分钟

这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战

顺序队列的基本算法

1. 初始化一个队列

void INITIALQ(int &front, int &rear) // ※
{
    front = -1;
    rear = -1;
}

2. 判断队列是否为空

int EMPTYQ(int front, int rear) // ※
{
    return front === rear;
}

3. 获取当前队头元素 ※

  • 判断队列是否为空
  • 获取当前队头元素赋值给item 该操作不改变队头指针的位置
int GETQ(QElemType QUEUE[], int front, int rear, QElemType &item)
{
    if(EMPTYQ(front, rear))
        return 0; // 队列为空,操作失败,返回0
    else {
        item = QUEUE[font + 1]; // 取出队头元素赋值给变量item
        return 1; // 队列非空,操作成功,返回1
    }
}

4. 队列的插入(进队) ※

在容量为M的队列中插入一个新的元素item

  • 判断上溢条件,如果产生溢出,则返回0,表示插入失败
  • 先将队尾指针rear加1,然后将新的元素item插入到修改之后的rear指出的新的队尾位置上,返回1,表示插入成功
int ADD(QElemType QUEUE[], int &rear, QElemType item)
{
    if (rear == M - 1)
        return 0; // 队列已满,插入失败,返回0
    else {
        QUEUE[++rear] = item;
        return 1; // 队列未满,插入成功,返回1
    }
}

5. 队列的删除(出队) ※

从队列中退出当前队头元素,并保存在变量item中

  • 判断队列是否为空队,若为空,返回0,删除失败
  • 将要删除的队头元素需要用到的话,将其保存在变量item中,队头指针front加1,返回1,删除成功 所谓删除,并不是把队头元素从原存储位置上物理地删除,只是将队头指针向队尾方向移动一个位置,这样,原来的那个队头元素就认为不再包含在队列中了。
int DELQ(QElemType QUEUE[], int &front, int rear, QElemType &item)
{
    if (EMPEYQ(front, rear))
        return 0; // 队列为空,删除失败,返回0
    else {
        item = QUEUE[++front]; // 保存队头元素
        return 1; // 队列非空,删除成功,返回1
    }
}

上面5个算法的实际复杂度均为O(1)

循环队列

在队列的插入算法中,QUEUE[0]~QUEUE[M-1]都被占用时,有rear = M - 1,若次数在进行插入操作会提示产生溢出的信息。 队列出现的问题:假溢出

  • 每次总是删除当前的队头元素,插入操作是在队尾进行
  • 当队尾指针rear = M - 1时,队列的前端可能还有许多由于此前进行了删除操作而产生的空的(可用的)位置 解决假溢出问题的办法之一是:每次删除队头的1个元素后,就把整个队列往前(往左)移动1个位置

image.png 算法修改为:

int DELQ(QElemType QUEUE[], int &rear, QElemType &item)
{
    if (rear == -1)
        return 0; // 队列为空,删除失败,返回0
    else {
        item = QUEUE[0]; // 保存队头元素
        for (i = 0; i < rear; i++) {
            QUEUE[i] = QUEUE[i + 1];
        }
        return 1; // 队列非空,删除成功,返回1
    }
}

这种方法效率极低,不可取:时间复杂度较大分析,出现了循环,为了节省存储空间而白白浪费了大量的时间

另一种办法是:在初始化队列时,令front = rear = 0,并且把队列设想称为头尾相连的循环表,使得空间可以重复使用——这就是循环队列

image.png

  • 进行插入操作时,当队列的第M个位置(数组下标为M-1)被占用以后,只要队列还用可用空间,新的元素加入队列时就可以从第1个位置(数组下标为0)开始。 插入算法中修改队尾指针的语句可修改为:
if (rear == M-1) 
    return 0;
else
    rear ++;

修改后的队尾指针满足rear == front,则产生溢出.

  • 上述算法采用“求模”运算,则可以改为更简单的赋值语句,效果完全相同 rear = (rear + 1) % M

删除算法中有: front = (front + 1) % M

循环队列的插入 ※

int ADDCQ(QElemType QUEUE[], int front, int &rear, QElemType item)
{
    if ((rear + 1) % M == front) // ※
        return 0; // 循环队列已满,插入失败,返回0
    else {
        QUEUE[++rear % M] = item; // ※
        return 1; // 循环队列未满,插入成功,返回1
    }
}

循环队列的删除

int DELCQ(QElemType QUEUE[], int &front, int rear, QElemType &item)
{
    if (front == rear)
        return 0; // 循环队列为空,删除失败,返回0
    else {
        front = (front + 1) % M; // ※
        item = QUEUE[front]; // ※
        return 1; // 循环队列为空,删除成功,返回1
    }
}