“排队”在我们日常生活中是经常遇到的,“先来先服务”的原则即排队问题的本质,实际上,在操作系统中,作业调度和输入输出管理都遵循“队列”。在数据结构中,队列是这样定义的:它是一种限定存取位置的线性表,只允许在表的一端插入,在另一端删除,插入的一端叫做队尾,删除的一端叫做队头。
下面就来模拟实现一下“队列,这里采用了链表的方式。
源代码如下:
#include<iostream>
#include<assert.h>
using namespace std;
template<class T>
struct QueueNode
{
T _data;
QueueNode<T>*_next;
QueueNode(const T&x)
:_data(x)
, _next(NULL)
{}
};
template<class T>
class Queue
{
typedef QueueNode<T>Node;
public:
Queue()
:_head(NULL)
, _tail(NULL)
{}
~Queue()
{
delete _head;
_head = NULL;
}
//插入操作
void Push(const T&x)
{
//链表为空,则直接把申请的新节点链上去,即使头指针指向该节点
if (_head == NULL)
{
_head = new Node(x);
_tail = _head;
}
//链表不为空,则头指针指向位置不变,把新节点链上去,然后注意要改变尾指针的指向位置(移动尾指针)
else
{
_tail->_next = new Node(x);
_tail = _tail->_next;
_tail->_next = NULL;
}
}
//删除操作
void Pop()
{
//空链表或链表中只有一个节点。(这里由于都符合_head==_tail的条件,所以可以直接归并为一种情况)则直接delete掉,然后头尾指针都置空
if (_head == _tail)
{
delete _head;
_head = _tail=NULL;
}
//链表中有多个节点,则需要改变头指针的指向位置,然后delete掉首节点。(注意这里一个易错点:在改变头指针指向的位置前,必须先把它保存起来,否则_head一旦移动,则后边delete第一个节点时就找不到它的位置了)
else
{
Node*tmp = _head;
_head = _head->_next;
delete tmp;
}
}
//返回队头元素的值
T&Front()
{
assert(_head != NULL);
return _head->_data;
}
//返回队尾元素的值
T&Back()
{
assert(_head != NULL);
return _tail->_data;
}
//判断队列是否为空
bool Empty()
{
if (_head == NULL)
{
return true;
}
else
return false;
}
//检测队列中的元素个数(注:此处最好用一个指针把_head指向的位置保存起来,不要用_head去移动来计数,否则调用size()函数时会出问题,因为_head已经走到最后为空了,导致输出队列元素那里出错
size_t size()
{
size_t _size = 0;
Node*tmp = _head;
while (tmp)
{
tmp= tmp->_next;
_size++;
}
return _size;
}
protected:
Node*_head;
Node*_tail;
};
//测试函数部分
void TestQueue()
{
Queue<int> q;
q.Push(1);
q.Push(2);
q.Push(3);
q.Push(4);
q.size();
q.Pop();
q.Pop();
while (!q.Empty())
{
cout << q.Front() << " ";
q.Pop();
}
}
int main()
{
TestQueue();
system("pause");
return 0;
}