目录
链队列
实现队列的最好的方式就是使用单链表来实现,队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已——称为链队列。
那为了操作方便,头指针指向头结点,队尾指针指向终端节点,即最后一个结点元素。
设队首、队尾指针front和rear,front指向头结点,rear指向队尾
队列为空时,front 和 real 都指向头结点:
这种实现出队和入队就十分方便,出队只需要将头结点指向的位置进行改变,入队只需要将rear的指向进行改变。
下面看我写的代码:
LinkQueue.h 头文件
#include<iostream>
#include<cassert>
using namespace std;
#ifndef TT_LINK_QUEUE_H
#define TT_LINK_QUEUE_H
namespace tt
{
class LinkQueue //队列的链表结构
{
public:
using ElemType = int;
using Status = void;
public:
struct QNode //队列的结点结构
{
ElemType m_data; //链队列的数据域
QNode *m_next; //链队列的指针域
};
enum State
{
TT_ERROR = 0,
TT_OK = 1
};
public:
LinkQueue();
~LinkQueue();
ElemType isEmpty()const; //判断队列是否为空
ElemType clear(); //清空队列
ElemType insert(ElemType elem); //插入元素致链队列尾部
ElemType remove(ElemType &elemOut); //删除链队列的对头元素
ElemType destroy(); //销毁队列
ElemType getHead(ElemType &eleOut); //获取对头元素
Status createTail(ElemType *datas, size_t length);//创建长度为length的链队列,数据通过数组指定,这里采用尾插法
Status createHead(ElemType *datas, size_t length); //头插法
Status getlength()const; //链队列的当前个数
Status traverseElem(); //遍历显示链队列中的所有元素
private:
QNode *m_front; //链队列的对头指针,队头不保存元素,只起头结点作用,当front==rear时,队列为空
QNode *m_rear; //链队列的队尾指针
ElemType m_queLength; //链队列的当前长度
};
inline LinkQueue::ElemType LinkQueue::isEmpty()const
{
return (m_front == m_rear);
}
inline LinkQueue::Status LinkQueue::getlength()const
{
cout << "链队列的当前的元素个数为:" << m_queLength << "\n" << endl;
}
}
#endif //TT_LINK_QUEUE_H
testLinkQueue.cpp 源文件
#include"LinkQueue.h"
namespace tt
{
LinkQueue::LinkQueue()
{
m_front = new QNode;
assert(m_front != nullptr);
m_rear = m_front; //链队列为空时,对头就是队尾
m_front->m_next = nullptr; //队头指针指向空
m_queLength = 0;
cout << "*******************链队列初始化成功************************" << endl;
}
LinkQueue::~LinkQueue()
{
this->destroy();
}
LinkQueue::Status LinkQueue::createTail(ElemType *datas, size_t length)//创建长度为length的链队列,数据通过数组指定,这里采用尾插法
{
for (size_t i = 0; i < length; ++i)
{
this->insert(datas[i]);
}
}
LinkQueue::Status LinkQueue::createHead(ElemType *datas, size_t length) //头插法
{
if (m_rear == m_front)
{
this->insert(datas[0]); //创建第一个元素
}
QNode *s = m_front->m_next; //指针s指向第一个元素
for (int i = 1; i < length; ++i)
{
QNode *p = new QNode;
p->m_data = datas[i];
p->m_next = s; // 新插入的结点的后继是s,该结点暂时成为第一个元素
m_front->m_next = p; //头结点后继是p
s = p; // 指针s也指向p指针指向的位置
++m_queLength;
}
}
LinkQueue::ElemType LinkQueue::insert(ElemType elem)
{
QNode *p = new QNode; //给一个新节点分配内存
assert(p != nullptr);
p->m_data = elem; //首先把要把新元素添进数据域
p->m_next = nullptr; //新结点的后继指向空
m_rear->m_next = p; //然后在把p变成原队尾指针指向的后继结点
m_rear = p; //最后再把p设置为队尾结点,此时m_rear指针指向最后一个元素
++m_queLength; //队列的长度加1
return TT_OK;
}
LinkQueue::ElemType LinkQueue::remove(ElemType &elemOut)
{
if (m_front == m_rear) //先判断链队列是否为空,空就错误,
{
return TT_ERROR;
}
QNode *p = m_front->m_next; //声明一个新结点,该结点等于要删除的第一个元素
elemOut = p->m_data; //在把要删除的元素用eleOut返回
m_front->m_next = p->m_next; //在把要队头结点的后继结点,即第二个元素,m_front指针指向它,就变成头结点的后继
if (m_rear == p) //如果要删除的元素刚好就是最后一个元素, 此时就一个元素
{
m_rear = m_front; //删除后,m_rear指针指向头结点
}
delete p; //释放要删除的结点的内存
--m_queLength; //链队列的的长度减一
return TT_OK;
}
LinkQueue::ElemType LinkQueue::getHead(ElemType &eleOut)
{
if (m_front == m_rear) //先判断链队列是否为空,空就错误,
{
return TT_ERROR;
}
//QNode *q=m_front->m_next; eleOut=q->m_data;
eleOut = m_front->m_next->m_data; //把m_front指针指向的后继结点的指针域中的数据用eleOut返回
return TT_OK;
}
LinkQueue::ElemType LinkQueue::clear() //清空链队列,不包括头结点
{
QNode *m = m_front->m_next; //首先声明一个临时结点m,变成队头元素
//m_front->m_next = nullptr; //然后m_front指针指向nullptr
m_rear = m_front; //m_rear指针指向头结点
while (m) //当m不为空时,循环继续
{
QNode *s = m->m_next; //先把m结点的后继结点变成s,即第二个元素
delete m; //释放m结点
m = s; //然后在把s 结点变成m结点,m结点永远都是第一个元素
}
m_queLength = 0;
return TT_OK;
}
LinkQueue::ElemType LinkQueue::destroy() //销毁队列,包括头结点
{
this->clear();
delete m_front;
m_front = m_rear = nullptr;
return TT_OK;
}
LinkQueue::Status LinkQueue::traverseElem() //遍历显示链队列中的所有元素
{
if (m_front == m_rear) //先判断链队列是否为空,空就错误,
{
cout << "此队列中没有数据或者队列没有建立,无法显示!" << "\n" << endl;
}
else
{
cout << "队列从队头至队尾内容依次为:";
QNode *q = m_front->m_next; //首先把第一个元素变成临时变量q
while (q) //只要q不为NULL,就继续
{
cout << q->m_data << " "; //显示出q指针指向的节点的元素
q = q->m_next; //q指针每循环一次往后移动一次
}
cout << endl;
}
}
}
//测试链式队列
void testLinkQueue()
{
tt::LinkQueue mylinkQueue; //初始化一个队列
int myLength(0); //链队列的整表创建
cout << "想创建多少数据的链表?";
cin >> myLength;
int *myDatas = new int[myLength];
cout << "请依次输入这" << myLength << "个数据,中间以回车符隔开:" << endl;
for (int i = 0; i < myLength; ++i)
{
cin >> myDatas[i]; //输入要存储的数据的值
}
mylinkQueue.createTail(myDatas, myLength); //调用createTail函数 建立单链表
mylinkQueue.traverseElem();
while (true)
{
{
cout << ("\n*********************************************************") << endl
<< "*************** 链队列的基本功能展示 ******************" << endl
<< "*******************************************************" << endl
<< "************** 选择1—— 数据进队列尾. ************" << endl
<< "************** 选择2—— 删除队列头元素. ************" << endl
<< "*************** 选择3—— 显示队列头元素. ************" << endl
<< "*************** 选择4—— 判断队列是否为空. ************" << endl
<< "***************************************************************" << endl
<< "*************** 选择5—— 显示队列的元素个数. *************" << endl
<< "*************** 选择6—— 清空队列. *************" << endl
<< "**************** 选择7—— 销毁队列. *************" << endl
<< "**************** 选择8—— 显示队列中的所有元素. ***********" << endl
<< "**************** 选择9—— 清屏. *************" << endl
<< "**************** 选择0—— 退出程序! *************" << endl
<< "***************************************************************" << endl
<< "***************************************************************" << endl;
}
cout << "\n*************请输入你想要使用的链队列功能的序号***************" << endl;
cout << "请输入相应的序号:";
int userChoice(0);
cin >> userChoice;
if (userChoice == 0)
{
cout << "程序已退出,感谢您的使用!" << "\n" << endl;
break;
}
switch (userChoice)
{
case 1:
{
cout << "请输入你想添加的数据:";
int pushDatas(0);
cin >> pushDatas;
if (mylinkQueue.insert(pushDatas)) //添加数据进队尾
{
cout << "数据" << pushDatas << "进队列成功!" << endl;
mylinkQueue.getlength();
mylinkQueue.traverseElem();
}
else
cout << "内存分配失败,数据" << pushDatas << "进队列失败!" << "\n" << endl;
break;
}
case 2:
{
int popHead(0);
if (mylinkQueue.remove(popHead)) //删除队头元素
{
cout << "数据" << popHead << "删除成功!" << "\n" << endl;
mylinkQueue.getlength();
mylinkQueue.traverseElem();
}
else
{
cout << "目前队列为空,数据" << popHead << "删除失败!" << "\n" << endl;
mylinkQueue.getlength();
}
break;
}
case 3:
{
int showHead(0);
if (mylinkQueue.getHead(showHead)) //显示队头元素
{
cout << "链队列的头元素为:" << showHead << "\n" << endl;
mylinkQueue.getlength();
mylinkQueue.traverseElem();
}
else
{
cout << "目前队列为空,无法显示元素!" << "\n" << endl;
mylinkQueue.getlength();
}
break;
}
case 4:
if (mylinkQueue.isEmpty()) //判断链队列是否为空
{
cout << "此队列为空!" << "\n" << endl;
mylinkQueue.getlength();
}
else
{
cout << "队列不为空" << "\n" << endl;
mylinkQueue.getlength();
mylinkQueue.traverseElem();
}
break;
case 5: //显示链队列当前的元素个数
mylinkQueue.getlength();
mylinkQueue.traverseElem();
break;
case 6:
if (mylinkQueue.clear()) //清空队列
{
cout << "队列已被清空!" << "\n" << endl;
mylinkQueue.getlength();
}
else
{
cout << "队列清空失败!" << "\n" << endl;
mylinkQueue.getlength();
mylinkQueue.traverseElem();
}
break;
case 7:
{
cout << "你确定要销毁此队列吗?如若销毁,就无法恢复,请谨慎操作.(若销毁请输入Y或y,表示确定)";
char yesOrNo;
cin >> yesOrNo;
if ((yesOrNo == 'y') || (yesOrNo == 'y'))
{
if (mylinkQueue.destroy())
{
cout << "队列已被销毁!" << "\n" << endl;
}
else
cout << "队列销毁失败!" << "\n" << endl;
}
break;
}
case 8: //显示队列中的所有元素
mylinkQueue.getlength();
mylinkQueue.traverseElem();
break;
case 9:
system("cls");
cout << "屏幕已经清屏,可以重新输入!" << "\n" << endl;
break;
default:
cout << "输入的序号不正确,请重新输入!" << "\n" << endl;
}
}
delete[]myDatas;
myDatas = nullptr;
}
int main()
{
testLinkQueue();
system("pause");
return 0;
}
C# 代码实现的链队列:
namespace DateStructure
{
interface IQueue
{
void CreateLinkQueueTail(params int[] arrElem);
void clear();
bool isEmpty();
void EnQueue(int insertElem);
int DeQueue();
int getLength();
void show();
int getHeadElem();
}
class Node
{
public int m_Data { get; set; } = 0;
public Node m_Next { get; set; } = null;
}
class LinkQueue : IQueue
{
private Node m_queueHead;
private Node m_queueRear;
private int m_data;
public LinkQueue()
{
m_queueHead = new Node();
Debug.Assert(m_queueHead != null, "头结点创建失败!");
m_queueRear = m_queueHead;
}
public void CreateLinkQueueTail(params int[] arrElem) // 尾插法
{
if (arrElem.Length != 0 && arrElem != null)
{
for (int i = 0; i != arrElem.Length; ++i)
{
EnQueue(arrElem[i]);
}
}
else
{
throw new ArgumentException("链队列整表创建失败!");
}
}
public int getHeadElem()
{
if (m_queueHead.m_Data == 0)
{
return -1;
}
return m_queueHead.m_Next.m_Data;
}
public int DeQueue()
{
if (m_queueHead.m_Data == 0)
{
return -1;
}
int removeElem = m_queueHead.m_Next.m_Data;
if(m_queueHead.m_Next == m_queueRear)
{
m_queueRear = m_queueHead;
}
else
{
m_queueHead.m_Next = m_queueHead.m_Next.m_Next;
}
--m_queueHead.m_Data;
return removeElem;
}
public void EnQueue(int insertElem)
{
Node newNode = new Node();
Debug.Assert(newNode != null, "添加元素时,创建新结点失败!");
newNode.m_Data = insertElem;
m_queueRear.m_Next = newNode;
m_queueRear = newNode;
++m_queueHead.m_Data;
}
public void clear()
{
m_queueHead.m_Data = 0;
m_queueRear = m_queueHead;
}
public int getLength()
{
return m_queueHead.m_Data;
}
public bool isEmpty()
{
return (m_queueHead.m_Data == 0 && m_queueRear == m_queueHead);
}
public void show()
{
if (m_queueHead.m_Data == 0)
{
WriteLine("该链队列中没有数据,无法显示!");
return;
}
else
{
Node temp = m_queueHead;
Write("输出此时链队列中所有的元素:");
for (int i = 0; i != m_queueHead.m_Data; ++i)
{
temp = temp.m_Next;
Write($"{temp.m_Data},");
}
WriteLine();
}
}
}
class Program
{
static void Main(string[] args)
{
LinkQueue myQueue = new LinkQueue();
const int arraySize = 1;
int[] myIntArray = new int[arraySize] { 55 };
myQueue.CreateLinkQueueTail(myIntArray);
myQueue.show();
int removeElem = myQueue.DeQueue();
if (removeElem >= 0)
{
WriteLine($"被删除的元素为:{removeElem}");
}
else
{
WriteLine("当前链队列是空的,没有元素可以删除!");
}
WriteLine("请输入你想插入的元素值:");
int pushElem = Convert.ToInt32(Console.ReadLine());
myQueue.EnQueue(pushElem);
myQueue.show();
int getHeadElem = myQueue.getHeadElem();
if(0<=getHeadElem)
{
WriteLine($"获取队头元素为:{getHeadElem}");
}
else
{
WriteLine("当前链队列是空的,没有元素可以获取!");
}
if (myQueue.isEmpty())
{
WriteLine("当前的链队列为空!");
}
else
WriteLine("当前的链队列非空!");
WriteLine($"\n获取当前链队列的总个数:{myQueue.getLength()}");
myQueue.clear();
WriteLine($"获取当前链队列的总个数:{myQueue.getLength()}");
}
}
}
循环队列和链式队列的比较
(1)从时间上看,它们的基本操作都是常数时间,即O(1)的。不过循环队列是事先申请好空间,使用期间不释放;而链式队列,每次申请和释放结点也会存在一定的时间开销,如果入队和出队比较频繁,则两者还是有细微的差别。
(2)从空间上看,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链式队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链式队列更加灵活。
总结:总的来说,在可以确定队列长度的最大值的情况下,建议用循环队列,如果你无法估计队列的长度,那就用链式队列。