数据结构——队列

简介: 1 队列是一种只允许在表的一端插入元素,在另一端删除元素的特殊的线性表。允许插入的一端成为队尾(rear),允许删除的一端成为队头(front)。当队列中没有任何元素时成为空队列。队列是一种先进先出的线性表,简称FIFO表。 2 与栈类似,队列也是一种操作受限的特殊的线性表,同样可以采用顺序存储结构和链式存储结构来表示队列。 3 队列的顺序存储表示——循环队列 队列的顺序存储表示同样

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");
}
 

目录
相关文章
数据结构 | 队列的实现
数据结构 | 队列的实现
|
4月前
|
存储
【数据结构】队列
【数据结构】队列
数据结构之——队列详解
数据结构之——队列详解
|
7月前
数据结构(队列)
数据结构(队列)
31 0
|
7月前
|
存储
数据结构:7、队列
数据结构:7、队列
35 0
|
7月前
|
存储
【数据结构】什么是队列?
【数据结构】什么是队列?
56 0
|
7月前
数据结构-队列
数据结构-队列
46 0
|
7月前
|
存储 前端开发
【排排站:探索数据结构中的队列奇象】(下)
【排排站:探索数据结构中的队列奇象】
|
7月前
|
存储 Java
数据结构之队列
数据结构之队列
57 0
|
7月前
|
存储
【数据结构】深入了解队列
【数据结构】深入了解队列