1 队列是一种只允许在表的一端插入元素,在另一端删除元素的特殊的线性表。允许插入的一端成为队尾(rear),允许删除的一端成为队头(front)。当队列中没有任何元素时成为空队列。队列是一种先进先出的线性表,简称FIFO表。
2 与栈类似,队列也是一种操作受限的特殊的线性表,同样可以采用顺序存储结构和链式存储结构来表示队列。
3 队列的顺序存储表示——循环队列
队列的顺序存储表示同样是利用一维数组来实现。为了解决“假溢出”问题,可以将队列的数组看成是头尾相接的环状空间,当队尾元素放到数组的最后一个位置时,如需入队就把新元素接着存放在数组的0下表位置,这样的队列成为循环队列。循环队列的基本操作的实现如下:
#ifndef _SQQUEUE_ #define _SQQUEUE_ template<class T> class SqQueue { public: SqQueue(int m = 100); ~SqQueue(); void Clear(); bool Empty() const; int Length() const; T& GetHead() const; T& GetLast() const; void Append(const T&); void Remove(); private: T* m_base; int m_front; int m_rear; int m_size; }; template<class T> SqQueue<T>::SqQueue(int m) { m_front = m_rear = 0; m_size = m; m_base = new T[m]; } template<class T> SqQueue<T>::~SqQueue() { delete [] m_base; } template<class T> void SqQueue<T>::Clear() { m_front = m_rear = 0; } template<class T> bool SqQueue<T>::Empty() const { return m_front == m_rear; } template<class T> int SqQueue<T>::Length() const { return (m_rear - m_front + m_size) % m_size; } template<class T> T& SqQueue<T>::GetHead() const { if (!Empty()) { return m_base[m_front]; } } template<class T> T& SqQueue<T>::GetLast() const { if (!Empty()) { return m_base[(m_rear - 1 + m_size) % m_size]; } } template<class T> void SqQueue<T>::Append(const T& e) { int i,j; if (m_front == (m_rear + 1) % m_size) { T* newbase = new T[m_size + 10]; for (i = m_front,j = 0; i < m_rear; i = (i + 1) % m_size, j++) { newbase[j] = m_base[i]; } delete [] m_base; m_base = newbase; m_front = 0; m_rear = j; m_size += 10; } m_base[m_rear] = e; m_rear = (m_rear + 1) % m_size; } template<class T> void SqQueue<T>::Remove() { if (!Empty()) { m_front = (m_front + 1) % m_size; } } #endif在循环队列中,上述基本操作的时间复杂度均为O(1)。
4 队列的链式存储表示——链队列
#ifndef _LINKQUEUE_ #define _LINKQUEUE_ template<class T> class LinkQueue { public: LinkQueue(); ~LinkQueue(); void Clear(); bool Empty() const; int Length() const; T* GetHead() const; T* GetLast() const; void Append(const T&); void Remove(); private: LinkNode<T>* m_front; LinkNode<T>* m_rear; }; template<class T> LinkQueue<T>::LinkQueue() { m_front = m_rear = new LinkNode<T>; m_front->next = NULL; } template<class T> LinkQueue<T>::~LinkQueue() { Clear(); delete m_front; } template<class T> void LinkQueue<T>::Clear() { LinkNode<T>* p; while (m_front->next != NULL) { p = m_front->next; m_front->next = p->next; delete p; } m_rear = m_front; } template<class T> bool LinkQueue<T>::Empty() const { return m_front == m_rear; } template<class T> int LinkQueue<T>::Length() const { int i = 0; LinkNode<T>* p = m_front; while (p->next != NULL) { i++; p = p->next; } return i; } template<class T> T& LinkQueue<T>::GetHead() const { while(!Empty()) { return m_front->next->data; } } template<class T> T& LinkQueue<T>::GetLast() const { while(!Empty()) { return m_rear->data; } } template<class T> void LinkQueue<T>::Append(const T& e) { LinkNode<T>* p = new LinkNode<T>; p->data = e; p->next = NULL; m_rear->next = p; m_rear = p; } template<class T> void LinkQueue<T>::Remove() { LinkNode<T>* p; if (!Empty()) { p = m_front->next; m_front->next = p->next; if (p == m_rear) { m_rear = m_front; } delete p; } } #endif
链队列的队空条件为front = rear,或者front->next = NULL,而队满只在内不能无可用空间时才发生,所以链队列特别适合于队列元素个数变化比较大的情况。
在链队列的操作中,队列的销毁、清空和求长度操作的时间复杂度为O(n),因为需要对链表的每个结点进行处理。队列的其余操作的时间复杂度为O(1)。
5 队列的应用
5.1 舞伴问题
假设在周末舞会上,男士们和女士们进入舞厅时,各自排成一队。跳舞开始时,依次从男队和女队的队头上各出一人配成舞伴。若两队初始人数不相同,则较长的那一队中未配对者等待下一轮舞曲。现要求写一算法模拟上述舞伴配对问题。
#include <iostream> #include <fstream> #include "SqQueue.h" using namespace std; struct Dancer { char name[20]; char sex; }; void main(int argc, char* argv[]) { ifstream input("input.txt"); SqQueue<Dancer> M(50), F(50); Dancer d; input>>d.name>>d.sex; while (d.sex != '#') { if (d.sex == 'M' || d.sex == 'm') { M.Append(d); } else if (d.sex == 'F' || d.sex == 'f') { F.Append(d); } input>>d.name>>d.sex; } while (!M.Empty() && !F.Empty()) { Dancer d = M.GetHead(); M.Remove(); cout<<d.name<<" "; Dancer d2 = F.GetHead(); F.Remove(); cout<<d2.name<<endl; } if (!M.Empty()) { cout<<"依然还有"<<M.Length()<<"男舞者在等待"<<endl; cout<<M.GetHead().name<<"最先获得舞伴"<<endl; } else if (!F.Empty()) { cout<<"依然还有"<<F.Length()<<"女舞者在等待"<<endl; cout<<F.GetHead().name<<"最先获得舞伴"<<endl; } else { cout<<"没有等待的舞者!"<<endl; } system("pause"); }5.2 火车车厢重排问题
一列货运列车共有 n 节车厢,每节车厢将停放在不同的车站。假定 n 个车站的编号分别为 1 ~ n ,即货运列车按照第 n 站至第 1 站的次序经过这些车站。为了便于从列车上卸掉相应的车厢,车厢的编号应与车站的编号相同,这样,在每个车站只需卸掉最后一节车厢。所以,给定任意次序的车厢,必须重新排列他们。
车厢的重排工作可以通过转轨站完成。在转轨站中有一个入轨、一个出轨和 k 个缓冲轨,缓冲轨位于入轨和出轨之间。假定 缓冲轨按先进先出的方式运作,设计算法解决火车车厢重排问题。
TrackStation.h
#include "SqQueue.h" #pragma once class CTrackStation { public: CTrackStation(int trackCount); ~CTrackStation(void); bool Realign(int* A, int n); private: bool HoldIn(int car); bool HoldOut(int car); SqQueue<int>* m_pTracks; int m_trackCount; };
TrackStation.cpp<pre name="code" class="cpp">#include "TrackStation.h" #include <iostream> CTrackStation::CTrackStation(int trackCount):m_trackCount(trackCount) { m_pTracks = new SqQueue<int>[m_trackCount]; } CTrackStation::~CTrackStation(void) { delete [] m_pTracks; } bool CTrackStation::HoldIn(int car) { int bestTrack = -1; int bestLast = -1; int i; for (i = 0; i < m_trackCount; i++) { if (!m_pTracks[i].Empty()) { int last = m_pTracks[i].GetLast(); if (car > last && last > bestLast) { bestTrack = i; bestLast = last; } } } if (bestTrack == -1) { for (i = 0; i < m_trackCount; i++) { if (m_pTracks[i].Empty()) { bestTrack = i; break; } } } if (bestTrack == -1) { return false; } m_pTracks[bestTrack].Append(car); std::cout<<car<<"号车厢进入"<<bestTrack<<"号缓冲轨"<<std::endl; return true; } bool CTrackStation::HoldOut(int car) { for (int i = 0; i < m_trackCount; i++) { if (!m_pTracks[i].Empty()) { int headCar = m_pTracks[i].GetHead(); if (headCar == car) { headCar =m_pTracks[i].GetHead(); m_pTracks[i].Remove(); std::cout<<headCar<<"号车厢从"<<i<<"号缓冲轨出站"<<std::endl; return true; } } } return false; } bool CTrackStation::Realign(int* A, int n) { int nowOut = 1, i = 0; while (nowOut <= n) { if (HoldOut(nowOut)) { nowOut++; continue; } if (i >= n || !HoldIn(A[i])) { return false; } i++; } return true; }
Main.cpp<pre name="code" class="cpp">#include <iostream> #include <fstream> #include "TrackStation.h" using namespace std; void main(int argc, char* argv[]) { int k; cout<<"请输入缓冲轨数目:"; ifstream input("input.txt"); input>>k; CTrackStation trackStation(k); cout<<"请依次输入入轨的车厢次序,以0结束:"; int car, n = 0, A[100]; input>>car; while(car > 0 && n < 100) { A[n++] = car; input>>car; } if (!trackStation.Realign(A,n)) { cout<<"车厢无法重排!"<<endl; } system("pause"); }