《大话数据结构》—— 队列的顺序存储结构 (循环队列)—— C++、C# 代码实现

 


 队列 


 队列的概念:

  队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作。

队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头。队列的插入操作通常称作入队列,队列的删除操作通常称作出队列。

下图是一个依次向队列中插入数据元素a0,a1,...,an-1后的示意图:

《大话数据结构》—— 队列的顺序存储结构 (循环队列)—— C++、C# 代码实现_第1张图片

上图中,a0是当前 队头数据元素,an-1是当前 队尾数据元素。

为了避免当只有一个元素时,对头和队尾重合使得处理变得麻烦,所以引入两个指针:front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样的话,当front指针等于rear时,此队列不是还剩一个元素,而是空队列。

《大话数据结构》—— 队列的顺序存储结构 (循环队列)—— C++、C# 代码实现_第2张图片


顺序队列


 顺序队列 队列的顺序存储结构称为顺序队列,顺序队列实际上是运算受限的顺序表,和顺序表一样,顺序队列也是必须用一个数组来存放当前队列中的元素。由于队列的队头和队尾的位置是变化的,因而要设两个指针和分别指示队头和队尾元素在队列中的位置。 

 顺序队列中的溢出现象:

《大话数据结构》—— 队列的顺序存储结构 (循环队列)—— C++、C# 代码实现_第3张图片

上图中,front指针指向队头元素,rear指针指向队尾元素的下一个位置。

图(d)中b、c、d出队后,front指针指向元素e,rear指针在数组外面。假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经被占用,再向后加就会产生数组越界的错误,可实际上队列在下标为0、1、2、3、4的地方还是空闲的,我们把这种现象叫做“假溢出”——也叫假上溢。

下溢:队列为空时,做出队操作产生的溢出现象。

真上溢:当队列满时,做入队操作产生的空间溢出现象。

顺序队列的基本操作

入队时:将新元素插入rear所指的位置,并将rear+1。

出队时:删除front所指的元素,并将front+1。

 


循环队列


  所以解决假溢出的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种逻辑上首尾相连的顺序存储结构称为循环队列。

如何判断循环队列究竟是空的还是满的:

  现在问题又来了,我们之前说,空队列时,front指针等于rear指针,那么现在循环队列满的时候,也是front等于rear,那么如何判断循环队列究竟是空的还是满的?有如下办法:

办法1:设置一个标志位flag。初始时置flag=0;每当入队列操作成功就置flag=1;每当出队列操作成功就置flag=0。则队列空的判断条件为:rear == front && flag==0;队列满的判断条件为:rear = = front && flag= =1。

办法2:保留一个元素的存储空间。此时,队列满时的判断条件为  (rear + 1) % maxSize == front;队列空的判断条件还是front == rear。

办法3:设计一个计数器count,统计队列中的元素个数。此时,队列满的判断条件为:count > 0 && rear == front ;队列空的判断条件为count == 0。

我们在接下来的代码中采用方法2来实现。

CirQueue.h 头文件

#include
#include
using namespace std;
#ifndef TT_CIR_QUEUE_H
#define TT_CIR_QUEUE_H
namespace tt
{
	class CirQueue
	{
	public:
		using  ElemType = int;
		using Status = void;

		enum State
		{
			TT_ERROR = 0,
			TT_OK = 1
		};
	public:
		CirQueue(ElemType INI_SIZE);  //初始化队列
		~CirQueue();
		ElemType isEmpty()const;  //判断队列是否为空。
		ElemType isFull()const;  //确定队列是否满了

		ElemType clear(); //将队列清空
		ElemType getHead(ElemType &elemOut); //若队列存在且非空,用elem返回队列的队头元素
		ElemType insert(ElemType elem); //若队列存在,插入新元素elem到队列中并成为队尾元素。
		ElemType remove(ElemType &elemOut);// 删除队列中的队头元素,并用elem返回。

		ElemType destroy(); //若队列存在,则销毁它
		Status getLength()const; //返回队列中当前元素的个数。
		Status show();  //显示队列中的所有元素
	private:
		ElemType *m_data;  //数据域
		ElemType m_front;  //指向队头元素
		ElemType m_rear;  //指向队尾元素的下一个位置
		ElemType m_queueSize;   //队列的最大容量
	};
	inline CirQueue::ElemType CirQueue::isEmpty()const  //判断队列是否为空
	{
		return (m_front == m_rear);
	}
	inline CirQueue::Status CirQueue::getLength()const //返回队列中当前元素的个数
	{
		cout<< "当前队列中的元素个数为:" << (m_rear - m_front + m_queueSize) % m_queueSize << endl;
	}
	inline CirQueue::ElemType CirQueue::clear() //将队列清空
	{
		m_front = m_rear = 0;
		return TT_OK;
	}
	inline CirQueue::ElemType CirQueue::isFull()const  //确定队列是否满了
	{
		return (m_rear + 1) % m_queueSize == m_front;
	}
}
#endif  //TT_CIR_QUEUE_H

testCirQueue.cpp 源文件

#include"CirQueue.h"

namespace tt
{
	CirQueue::CirQueue(ElemType INI_SIZE)
	{
		assert(INI_SIZE != 0);
		m_data = new int[INI_SIZE];
		assert(m_data != nullptr);
		m_queueSize = INI_SIZE;  //把队列的初始化的最大容量赋值给成员数据
		m_front = m_rear = 0;  //m_front等于m_rear 就是空队列
		cout << "***********     循环队列初始化成功!   **************" << endl;
	}
	CirQueue::~CirQueue()
	{
		this->destroy();
	}
	CirQueue::ElemType CirQueue::insert(ElemType elem)  //插入元素致队列的尾部
	{
		if (((m_rear + 1) % m_queueSize) == m_front)//判断队列满的情况
		{
			return TT_ERROR;
		}
		m_data[m_rear] = elem;    //将元素elem  添加到队列的末尾
		m_rear = (m_rear + 1) % m_queueSize;  //尾指针应以此种方式加1,才会实现循环队列, 若到末尾转到数组的头部
		return TT_OK;
	}
	CirQueue::ElemType CirQueue::remove(ElemType &elemOut)  //删除队列的队头元素
	{
		if (m_front == m_rear)   //判断循环队列是否为空
		{
			return TT_ERROR;
		}
		elemOut = m_data[m_front];  //将对头元素赋给elem返回
		m_front = (m_front + 1) % m_queueSize;  //m_front指针向后移动一位,若到最后则转到数组头部
		return TT_OK;
	}
	CirQueue::ElemType CirQueue::getHead(ElemType &elemOut) //若队列存在且非空,用elem返回队列的队头元素
	{
		if (m_front == m_rear)   //判断循环队列是否为空
		{
			return TT_ERROR;
		}
		elemOut = m_data[m_front];  //把队头元素用elem返回
		return TT_OK;
	}

	CirQueue::ElemType CirQueue::destroy() //若队列存在,则销毁它
	{
		delete[] m_data;
		m_data = nullptr;
		m_front = m_rear = m_queueSize = 0;
		return ((!m_data) && (m_front == m_rear == m_queueSize == 0));  //多一个判断看队列是否被销毁
	}
	CirQueue::Status CirQueue::show()  //显示队列的所有元素
	{
		if (m_front == m_rear)
		{
			cout << "错误,此队列中没有数据或者队列没有建立,无法显示!" << endl;
		}
		else
		{
			 auto count = (m_rear - m_front + m_queueSize) % m_queueSize;  //一个临时变量存储该队列的元素个数
			cout << "队列从队头至队尾内容依次为:";
			for (size_t i = m_front; i < m_front + count; ++i)
			{
				cout << m_data[i] << ' ';
			}
			cout << endl;
		}
	}
}

//测试循环队列的功能
void testCirQueue()
{
	int allocMemory(0);
	cout << "请输入队列初始化的最大容量:";
	cin >> allocMemory;
	tt::CirQueue myCirQueue(allocMemory);  //初始化一个队列
	while (true)
	{
		{
			cout << ("\n***************************************************") << endl
				<< "***************     循环队列的基本功能展示    **************" << endl
				<< "*******************************************************" << endl
				<< "**************      选择1—— 数据进队列尾.       ************" << endl
				<< "**************      选择2—— 删除队列头元素.     ************" << endl
				<< "***************     选择3—— 显示队列头元素.     ************" << endl
				<< "***************     选择4—— 判断队列是否为空.   ************" << endl
				<< "***************     选择5—— 判断队列是否满了.   ************" << endl
				<< "***************************************************************" << endl
				<< "***************     选择6—— 显示队列的元素个数. *************" << endl
				<< "***************     选择7—— 清空队列.           *************" << endl
				<< "****************     选择8—— 销毁队列.           *************" << endl
				<< "****************     选择9—— 显示队列中的所有元素. ***********" << endl
				<< "****************     选择10—— 清屏.              *************" << endl
				<< "****************     选择0—— 退出程序!         *************" << endl
				<< "***************************************************************" << endl
				<< "***************************************************************" << endl;
		}
		cout << "\n*****************     请输入你想要使用的队列功能的序号   ***************" << endl;
		cout << "请输入你的选择:";
		int userChoice(0);
		cin >> userChoice;
		if (!userChoice)
		{
			cout << "程序已退出,感谢您的使用!" << "\n" << endl;
			break;
		}

		switch (userChoice)
		{
		case 1:
		{
			int pushDatas(0);
			cout << "请输入想要添加的数据:";
			cin >> pushDatas;
			if (myCirQueue.insert(pushDatas))   //进队列
			{
				cout << "数据" << pushDatas << "成功进入队列中!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();  //显示所有元素
			}
			else
				cout << "目前队列已满, 数据" << pushDatas << "进入失败!" << endl;
			break;
		}
		case 2:
		{
			int popDatas(0);
			if (myCirQueue.remove(popDatas))   //删除队列头元素
			{
				cout << "数据" << popDatas << "从队列中成功删除!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			else
			{
				cout << "目前队列为空, 数据" << popDatas << "删除失败!" << endl;
				myCirQueue.getLength();
			}
			break;
		}
		case 3:
		{
			int disHead(0);
			if (myCirQueue.getHead(disHead))   //获取队头元素
			{
				cout << "队列头元素为:" << disHead << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			else
			{
				cout << "目前队列为空, 数据" << disHead << "获取失败!" << endl;
				myCirQueue.getLength();
			}
			break;
		}
		case 4:
			if (myCirQueue.isEmpty())  //判断队列是否空
			{
				cout << "队列为空,或者队列尚未建立!" << endl;
				myCirQueue.getLength();
			}
			else
			{
				cout << "队列非空!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			break;
		case 5:
			if (myCirQueue.isFull())  //判断队列是否满
			{
				cout << "目前队列已满,不能再添加数据了!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			else
			{
				cout << "目前队列不满,还可以继续输入数据进栈!" << "\n" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			break;
		case 6:
			myCirQueue.getLength();  //显示队列的元素个数
			myCirQueue.show();
			break;

		case 7:
			if (myCirQueue.clear())
			{
				cout << "队列已清空!" << endl;
				myCirQueue.getLength();
			}
			else
			{
				cout << "队列清空失败!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			break;
		case 8:
		{
			cout << "你确定要销毁一个队列吗?(若销毁请输入输入(Y/y))";
			char yesOrNo;
			cin >> yesOrNo;
			if ((yesOrNo == 'Y') || (yesOrNo == 'y'))
			{
				if (myCirQueue.destroy())
				{
					cout << "队列已被销毁!" << endl;
				}
				else
					cout << "队列销毁失败!" << endl;
			}
			break;
		}
		case 9:
			myCirQueue.getLength();
			myCirQueue.show();
			break;
		case 10:
			system("cls");
			cout << "屏幕已经清屏,可以重新输入!" << "\n" << endl;
			break;
		default:
			cout << "输入的序号不正确,请重新输入!" << "\n" << endl;
		}
	}
}
int main()
{
	testCirQueue();
	system("pause");
	return 0;
}

C# 代码实现的循环队列:

namespace DateStructure
{
    interface IQueue
    {
        void CreateQueue(params int[] arrElem);
        void clear();
        bool isEmpty();
        void EnQueue(int insertElem);
        int DeQueue();
        int getLength();
        void show();
    }

    class CirQueue : IQueue
    {
        private int[] m_data;
        private int m_front; // 循环队列队头指针
        private int m_rear; // 循环队列尾指针
        private int m_queueMaxLength;
        private int m_queueCurrentLength;
        public CirQueue(int maxSize)
        {
            Debug.Assert(maxSize > 0, "不能分配小于1的数组!");
            m_data = new int[maxSize];
            Debug.Assert(m_data != null, "初始化时,内存分配失败!");
            m_queueMaxLength = maxSize;
            m_front = m_rear = m_queueCurrentLength = 0;
        }

        public void CreateQueue(params int[] arrElem)
        {
            // arrElem.Length 不能等于 m_queueMaxLength,否则但添加元素时,会出现bug
            if ((arrElem != null) && (arrElem.Length != 0) && arrElem.Length < m_queueMaxLength)
            {
                m_rear = m_queueCurrentLength = arrElem.Length;
                for (int i = 0; i != m_queueCurrentLength; ++i)
                {
                    m_data[i] = arrElem[i];
                }
            }
            else
            {
                throw new ArgumentException("循环队列整表创建失败!");
            }
        }
        public void clear()
        {
            m_front = m_rear = m_queueCurrentLength = 0;
        }



        public int DeQueue()
        {
            if (isEmpty() == true)
            {
                return -1;
            }
            int removeElem = m_data[m_front];
            m_front = (m_front + 1) % m_queueMaxLength;
            --m_queueCurrentLength;
            return removeElem;
        }

        public void EnQueue(int insertElem)
        {
            if ((m_rear + 1) % m_queueMaxLength == m_front)
            {             
                throw new ArgumentException("当前循环队列已满,无法添加元素!");           
            }
            m_data[m_rear] = insertElem;
            m_rear = (m_rear + 1) % m_queueMaxLength;
            ++m_queueCurrentLength;
        }

        public int getLength()
        {
            return m_queueCurrentLength;
        }

        public bool isEmpty()
        {
            return ((m_queueCurrentLength == 0) && (m_rear == m_front));
        }
       public void show()
        {
            if(m_queueCurrentLength == 0)
            {
                WriteLine("该循环队列中没有元素,无法输出!");
                return;
            }
            int index = m_front;
            WriteLine("输出链队列中的所有元素:");
            do
            {
                Write($"{m_data[index]},");
                index = (index + 1) % m_queueMaxLength;
            } while (index != m_rear);
            WriteLine();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("输入循环队列的最大容量:");
            int sizeCapacity = Convert.ToInt32(Console.ReadLine());
            var myQueue = new CirQueue(sizeCapacity);

            const int arraySize = 5;
            int[] myIntArray = new int[arraySize] { 141, 11, 24, 347, 45 };
            myQueue.CreateQueue(myIntArray);
            myQueue.show();

            WriteLine("请输入你想插入的元素值:");
            int pushElem = Convert.ToInt32(Console.ReadLine());
            myQueue.EnQueue(pushElem);
            myQueue.show();

            int removeElem = myQueue.DeQueue();
            if (removeElem >= 0)
            {
                WriteLine($"被删除的元素为:{removeElem}");
                myQueue.show();

            }
            else
            {
                WriteLine("当前循环队列是空的,没有元素可以删除!");
            }

            if (myQueue.isEmpty())
            {
                WriteLine("当前的循环队列为空!");
            }
            else
                WriteLine("当前的循环队列非空!");
            WriteLine($"\n获取当前循环队列的总个数:{myQueue.getLength()}");
            myQueue.clear();
            WriteLine($"获取当前循环队列的总个数:{myQueue.getLength()}");

        }

    }
}


 

你可能感兴趣的:(数据结构)