《大话数据结构5》—— 队列的链式存储结构 —— C++代码实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34536551/article/details/83786389

目录

链队列

循环队列和链式队列的比较


链队列


●  实现队列的最好的方式就是使用单链表来实现,队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已——称为链队列。

● 那为了操作方便,头指针指向头结点,队尾指针指向终端节点,即最后一个结点元素。

设队首、队尾指针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;
}

● 注意: 把以上代码复制黏贴到 Visual Studio 2017 上 或者 Visual Studio 2015 上,如果是其他的编译器,可能会出错, 因为该程序用了一点 C++11 的语法, 如果你的编译器不遵循C++11的语法的法,一般来说会出错。

把该代码分别放进编译器中的源文件和头文件就能编译运行成功。  如果要合成到一块的源文件的话, 需要修改一下地方。你们自己修改吧。

如果有错误的话,欢迎指教。


如果转载的话, 请指明出处。谢谢。
 


循环队列和链式队列的比较


(1)从时间上看,它们的基本操作都是常数时间,即O(1)的。不过循环队列是事先申请好空间,使用期间不释放;而链式队列,每次申请和释放结点也会存在一定的时间开销,如果入队和出队比较频繁,则两者还是有细微的差别。

(2)从空间上看,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链式队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链式队列更加灵活。

总结:总的来说,在可以确定队列长度的最大值的情况下,建议用循环队列,如果你无法估计队列的长度,那就用链式队列。

猜你喜欢

转载自blog.csdn.net/qq_34536551/article/details/83786389