数据结构四: 队列

1. 队列的基本概念

队列是一种先进先出(first in first out ,FIFO)的线性表,只允许在一端进行插入(入队)操作,在另一端进行删除(出队)操作。
在队列中,允许入队操作的一端称为队尾,允许出队操作的一端称为队头。设有队列 q = ( a 1 , a 2 , , a n ) ,则 a 1 称为队头元素, a n 称为队尾元素,队列按 a 1 , a 2 , , a n 的顺序入队,按同样的顺序出队。队列的典型应用是操作系统中的作业排队,同时在离散事件模拟中一般都要使用队列。

2. 链队列

//.hpp文件
#pragma once

//节点类
template<typename elemType>
struct Node
{
    //数据成员
    elemType data;//数据域
    Node<elemType>* next;//指针域

    //构造函数
    Node();//无参构造函数
    Node(elemType item, Node<elemType>* Link = NULL);//已知数据域和指针域建立结构
};

//节点类的实现部分
template<typename elemType>
Node<elemType>::Node()
//操作结果:构造指针域为空的节点
{
    this->next = NULL;
}

template<typename elemType>
Node<elemType>::Node(elemType item, Node<elemType>* Link)
//操作结果:构造数据域为item指针域为Link的节点
{
    this->data = item;
    this->next = Link;
}

//链队列表
template<typename elemType>
class LinkQueue
{
protected:
    //链队列数据成员
    Node<elemType>* front; Node<elemType>*  rear;//队头队尾指针

    //辅助函数
    void Init();//初始化队列

public:
    //抽象数据类型方法声明及重载编译系统默认方法声明
    LinkQueue();//无参构造函数
    virtual ~LinkQueue();//析构函数
    int getLength() const;//获取队列的长度
    bool isEmpty() const;//判断队列是否为空
    void Clear();//将队列清空
    void Traverse(void(*Visit)(elemType &e));//遍历队列
    bool outQueue(elemType &e);//出队操作
    bool getHead(elemType& e) const;//获取队头数据
    bool InQueue(const elemType& e);//入队操作

    LinkQueue(const LinkQueue<elemType>& copy);//复制构造函数
    LinkQueue<elemType>& operator=(const LinkQueue<elemType>& copy);//重载赋值运算符
};

//链队列类的实现
template<typename elemType>
void LinkQueue<elemType>::Init()
{
    rear = front = new Node < elemType > ;//生成头结点,对于头结点不存数据的初始化时,
                                            //要new一个头结点出来,否则直接让头结点指向空指针
}

template<typename elemType>
LinkQueue<elemType>::LinkQueue()
{
    Init();
}

template<typename elemType>
LinkQueue<elemType>::~LinkQueue()
{
    Clear();
}

template<typename elemType>
int LinkQueue<elemType>::getLength() const
{
    int count = 0;
    for (Node<elemType>* curPosition = front->next; curPosition != NULL; curPosition = curPosition->next)
    {
        count++;
    }
    return count;
}

template<typename elemType>
bool LinkQueue<elemType>::isEmpty() const
{
    return front == rear;
}

template<typename elemType>
void LinkQueue<elemType>::Clear()
//要学会这里清空链表的思想
{
    elemType e;
    while(front != NULL)
    {
        outQueue(e);
    }
}

template<typename elemType>
void LinkQueue<elemType>::Traverse(void(*Visit)(elemType &e))
{
    for (Node<elemType>* curPosition = front->next; curPosition != NULL; curPosition = curPosition->next)
    {
        (*Visit)(curPosition->data);
    }
}

template<typename elemType>
bool LinkQueue<elemType>::outQueue(elemType &e)
{
    if (isEmpty())
    {
        return false;
    }
    else
    {
        Node<elemType>* tempPtr = front->next;
        e = tempPtr->data;
        front->next = tempPtr->next;
        if (rear == tempPtr)//要加这个判断,否则为空的时候rear != front 
        {
            rear = front;
        }
        delete tempPtr;
        return true;
    }
}

template<typename elemType>
bool LinkQueue<elemType>::getHead(elemType& e) const
{
    if (isEmpty())
    {
        return false;
    } 
    else
    {
        e = front->next->data;
        return true;
    }

}

template<typename elemType>
bool LinkQueue<elemType>::InQueue(const elemType& e)
{
    //这里要判断队列是否已满
    Node<elemType>* tempPtr = new Node<elemType>(e);
    if (tempPtr == NULL)
    {
        return false;
    }
    rear->next = tempPtr;
    rear = tempPtr;
    return true;
}

template<typename elemType>
LinkQueue<elemType>::LinkQueue(const LinkQueue<elemType>& copy)
{
    if (copy.isEmpty())
    {
        Init();
    }
    else
    {
        Init();
        for (Node<elemType>* tempPtr = copy.front->next; tempPtr != NULL; tempPtr = tempPtr->next)
        {
            InQueue(tempPtr->data);//注意这个技巧
        }
    }
}

template<typename elemType>
LinkQueue<elemType>& LinkQueue<elemType>::operator=(const LinkQueue<elemType>& copy)
{
    if (&copy != this)
    {
        Clear();
        Init();
        for (Node<elemType>* tempPtr = copy.front->next; tempPtr != NULL; tempPtr = tempPtr->next)
        {
            InQueue(tempPtr->data);//注意这个技巧
        }
    }
    return *this;
}
//测试文件
#include <iostream>
#include "LinkQueue.h"

using namespace std;

template<typename elemType>
void Visit(elemType &e)
{
    cout << e << endl;
    e = e + 1;

}
int main(int argc, char* argv[])
{
    LinkQueue<int> myLinkQueue;
    myLinkQueue.InQueue(1);
    myLinkQueue.InQueue(2);
    myLinkQueue.InQueue(3);
    myLinkQueue.Traverse(Visit);

    int e;
    myLinkQueue.outQueue(e);
    cout << e << endl;

    getchar();
    return 0;
}

3 循环队列——队列的顺序存储结构

顺序存储结构的队列是存储在一个一维数组中的,因此它会出现假溢出的问题。下面先简单说明一下什么是假溢出:
这里写图片描述
在上图的(d)中,将不可以再继续插入新元素了否则会因数组越界而出错,显然此时的实际可以空间还没有用完,这种情形之下在插入一个新的元素产生的溢出称为“假溢出”。
那么这个问题该如何解决呢?为了解决这个问题,我们将顺序队从逻辑上看成一个环,成为循环队列。当队头(front)和队尾(rear)进入到 maxSize - 1时,再进一位就自动的移动到0,可用取余运算符(%)简单实现。
队头进1:front = (front + 1)%maxSize
队尾进1:rear = (rear + 1)%maxSize
这里写图片描述
上图是循环队列的逻辑结构,从图可以知道,在对空和队满时都有:
front == rear
因此从front == rear无法判断是队空和队满的状态,因此我们通过下面的方式来判断:

(1) 另设一个标识符区别队列是对空还是队满
(2) 少用一个元素空间,约定队头在队尾指针的下一位置(指环状的下一个位置)时最为队满的标志。接下来的实现采用这种方式

//.hpp文件
#pragma  once
#define DEFAULT_SIZE 10
//循环队列类
template<typename elemType>
class Squeue
{
protected:
    //成员变量
    int front, rear;//队头队尾
    int maxSize;//队列的最大元素的个数
    elemType* elems;//元素存储结构

    //辅助函数
    bool Full() const;//判断队列是否为空
    void Init();//初始化队列

public:
    //方法
    Squeue(int size = DEFAULT_SIZE);//构造函数
    virtual ~Squeue();//析构函数
    int getLength() const;//获取队列长度
    bool isEmpty() const;//判断队列是否为空
    void Clear();//清空队列
    void Traverse(void(*Visit)(elemType& e));//遍历队列
    bool OutQueue(elemType& e);//出队操作
    bool getHead(elemType& e) const;//取队头操作
    bool InQueue(const elemType& e);//入队操作

    Squeue(const Squeue<elemType>& copy);//复制构造函数
    Squeue<elemType>& operator=(const Squeue<elemType>& copy);//重载复制运算符

};

//循环队列类的实现
template<typename elemType>
bool Squeue<elemType>::Full() const
{
    return getLength() == maxSize - 1;
}

template<typename elemType>
void Squeue<elemType>::Init()
{
    rear = front = 0;
}

template<typename elemType>
Squeue<elemType>::Squeue(int size)
{
    maxSize = size;
    elems = new elemType[maxSize];
    Init();
}

template<typename elemType>
Squeue<elemType>::~Squeue()
{
    delete[] elems;
}

template<typename elemType>
int Squeue<elemType>::getLength() const
{
    return (rear - front + maxSize) % maxSize;//这个要注意
}

template<typename elemType>
bool Squeue<elemType>::isEmpty() const
{
    return rear == front;
}

template<typename elemType>
void Squeue<elemType>::Clear()
{
    rear = front = 0;
}

template<typename elemType>
void Squeue<elemType>::Traverse(void(*Visit)(elemType& e))
//注意点:如何判断队列结束,以及指针的更新
{
    for (int curPosition = front; curPosition != rear; curPosition = (curPosition + 1) % maxSize)//这里求长度以及加运算后都要求余
    {
        (*Visit)(elems[curPosition]);
    }

}

template<typename elemType>
bool Squeue<elemType>::OutQueue(elemType& e)
{
    if (isEmpty())
    {
        return false;
    }
    else
    {
        e = elems[front];
        front = (front + 1) % maxSize;
        return true;
    }
}

template<typename elemType>
bool Squeue<elemType>::getHead(elemType& e) const
{
    if (isEmpty())
    {
        return false
    }
    else
    {
        e = elems[front];
        return true;
    }
}

template<typename elemType>
bool Squeue<elemType>::InQueue(const elemType& e)
{
    if (!Full())
    {
        elems[rear] = e;
        rear = (rear + 1) % maxSize;
        return true;
    }
    else
    {
        return false;
    }
}

template<typename elemType>
Squeue<elemType>::Squeue(const Squeue<elemType>& copy)
{
    elems = new elemType[copy.maxSize];
    maxSize = copy.maxSize;
    front = copy.front;
    rear = copy.rear;
    for (int curposition = front; curposition != rear; curposition = (curposition + 1) % maxSize)
    {
        elems[curposition] = copy.elems[curposition];
    }
}

template<typename elemType>
Squeue<elemType>& Squeue<elemType>::operator=(const Squeue<elemType>& copy)
{
    if (&copy != this)
    {
        delete[] elems;
        elems = new elemType[copy.maxSize];
        maxSize = copy.maxSize;
        front = copy.front;
        rear = copy.rear;
        for (int curposition = front; curposition != rear; curposition = (curposition + 1) % maxSize)
        {
            elems[curposition] = copy.elems[curposition];
        }
    }
    return *this;
}
//测试文件
#include <iostream>
#include "Squeue.hpp"

using namespace std;

template<typename elemType>
void Visit(elemType &e)
{
    cout << e << endl;
    e = e + 1;

}

int main(int argc, char* argv[])
{
    Squeue<int> myqueue;
    myqueue.InQueue(1);
    myqueue.InQueue(2);
    myqueue.InQueue(3);
    myqueue.InQueue(4);
    myqueue.Traverse(Visit);
    int e;
    myqueue.OutQueue(e);
    cout << e << endl;

    Squeue<int> myqueue1;
    myqueue1 = myqueue;
    myqueue1.Traverse(Visit);
    myqueue1.Clear();
    myqueue1.Traverse(Visit);
    getchar();
    return 0;
}

4. 优先队列

上面介绍的队列过于死板,对于一些特殊的情况我们不能使用先进先出的策略。比如,在医院中,对于病危的病人,可能他们是后来的,但是应该优先给他们救治。因此,优先队列就产生了。
优先队列是一种数据结构,优先队列有两种数据类型:最小优先队列和最大优先队列。最小优先队列的出队操作OutQueue()将删除最下的数据元素。最大优先队列是删除最大的。
优先队列有多种实现方式。一种简单的方式是在入队时就对队列中的数据元素进行排列,使其顺序合理化。当然我们也可以在出队时选择合适的数据元素将其删除。这里选择第一中方式。

4.1 最小优先链队列

//最小优先队列类
template<typename elemType>
class MinPriorityLinkQueue :public LinkQueue<elemType>
{
public:
    //重载入队操作说明
    bool InQueue(const elemType& e);
};


//最小优先链队列的实现部分
template<typename elemType>
bool MinPriorityLinkQueue<elemType>::InQueue(const elemType& e)
{
    Node<elemType>* curPtr = LinkQueue<elemType>::front->next;//指向当前节点
    Node<elemType>* curPrePtr = LinkQueue<elemType>::front;//指向当前节点的前驱节点

    while (curPtr!= NULL && curPtr->data <= e)
    {
        curPrePtr = curPtr;
        curPtr = curPtr->next;
    }

    Node<elemType>* tempPtr = new Node<elemType>(e, curPtr);
    if (tempPtr == NULL)
    {
        return false;
    }
    curPrePtr->next = tempPtr;

    if (curPrePtr == LinkQueue<elemType>::rear)
    {
        LinkQueue<elemType>::rear = tempPtr;//指向新队尾
    }

    return true;
}

4.2 最大优先链队列

//最大优先链队列
template<typename elemType>
class MaxPriorityLinkQueue :public LinkQueue < elemType >
{
public:
    //重载入队操作
    bool InQueue(const elemType& e);
};


//最大优先链队列实现部分
template<typename elemType>
bool MaxPriorityLinkQueue<elemType>::InQueue(const elemType& e)
{
    Node<elemType>* curPtr = LinkQueue<elemType>::front->next;//指向当前节点
    Node<elemType>* curPrePtr = LinkQueue<elemType>::front;//指向当前节点的前驱节点

    while (curPtr != NULL && curPtr->data >= e)
    {
        curPrePtr = curPtr;
        curPtr = curPtr->next;
    }

    Node<elemType>* tempPtr = new Node<elemType>(e, curPtr);
    if (tempPtr == NULL)
    {
        return false;
    }
    curPrePtr->next = tempPtr;

    if (curPrePtr == LinkQueue<elemType>::rear)
    {
        LinkQueue<elemType>::rear = tempPtr;//指向新队尾
    }

    return true;
}

4.3 最小优先循环队列

//最小优先循环队列类
template<typename elemType>
class MinPrioritySquue : public Squeue < elemType >
{
public:
    //重载入队操作声明
    bool InQueue(const elemType& e);
};


//最小优先循环队列实现部分
template<typename elemType>
bool MinPrioritySquue<elemType>::InQueue(const elemType& e)
{
    if (Squeue<elemType>::Full())
    {
        return false;
    } 
    else
    {
        int curPosition = Squeue<elemType>::front;
        while(curPosition != rear && Squeue<elemType>::elems[curPosition] <= e)
        {//将所有大于e的数据元素向前一个
            Squeue<elemType>::elems[(curPosition - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize] =
                Squeue<elemType>::elems[curPosition];
            curPosition = (curPosition + 1) % Squeue<elemType>::maxSize;
        }
        //curPosition的前一个位置为e的插入位置
        Squeue<elemType>::elems[(curPosition - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize] = e;
        //将front前移一个位置
        Squeue<elemType>::front = (Squeue<elemType>::front - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize;
        return true;
    }
}

4.4 最大优先循环队列


//最大优先循环队列类
template<typename elemType>
class MaxPrioritySquue : public Squeue < elemType >
{
public:
    //重载入队操作声明
    bool InQueue(const elemType& e);
};


//最大优先循环队列实现部分
template<typename elemType>
bool MaxPrioritySquue<elemType>::InQueue(const elemType& e)
{
    if (Squeue<elemType>::Full())
    {
        return false;
    }
    else
    {
        int curPosition = Squeue<elemType>::front;
        while (curPosition != rear && Squeue<elemType>::elems[curPosition] >= e)
        {//将所有大于e的数据元素向前一个
            Squeue<elemType>::elems[(curPosition - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize] =
                Squeue<elemType>::elems[curPosition];
            curPosition = (curPosition + 1) % Squeue<elemType>::maxSize;
        }
        //curPosition的前一个位置为e的插入位置
        Squeue<elemType>::elems[(curPosition - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize] = e;
        //将front前移一个位置
        Squeue<elemType>::front = (Squeue<elemType>::front - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize;
        return true;
    }
}

猜你喜欢

转载自blog.csdn.net/wuye999/article/details/79488279