數據結構學習筆記6——隊列

1,綜述

隊列(queue)是一種受限制的線性表,其屬性與看電影排隊類似,新加入的必須排在隊尾(enqueue,入隊),先離開的必須從隊首開始(dequeue,出隊)。因此,隊列是按照到達的順序來刪除元素的,即先進先出(First In First Out).

隊列的ADT:(Queue.h)
/********************************************************/
// 用模板實現隊列(Queue)的基類定義
// 隊列,先進先出(FIFO)的線性表
/********************************************************/
#pragma once
#include "Public.h"

template<class E>
class Queue
{
public:
	Queue() {}
	virtual ~Queue() {}

	virtual void clear() = 0;

	// 入隊,只能從隊尾插入
	virtual void enqueue(const E& it) = 0;

	// 出隊,只能從隊首刪除,返回類型不能是引用,因爲返回的隊首值已經不存在了
	virtual E dequeue() = 0;

	// 返回隊首的值,返回類型是const引用,所以你能獲取值,但不能改變它
	virtual const E& frontValue()const = 0;

	// 返回棧中當前存儲的元素個數
	virtual int length() const = 0;
};


隊列的實現方式有兩種:順序隊列(用數組實現)和鏈式隊列(用單向鏈表實現)。

2,順序隊列(array based queue)

用數組模擬隊列,首先想到的,就是把隊首放在a[0]位置,剩下的依次排放,新元素入隊就放在後面排隊,出隊的時候,就必須後面所有的元素都往前移一位,就好像我們排隊的時候,最前面的人走了,後面所有人必須都往前走一步,隊伍才能前進一樣。但是這樣會耗費態度的資源。所以將其改進爲不強制要求隊首必須在a[0],只要能知道隊首所在的數組下標即可。但是這樣一來,一旦發生出隊,a[0]和隊首之間的內存就被浪費了,永遠都沒辦法利用。改進的方案是,當如隊到達數組的最後一個位置a[len-1]後,開始向a[0]填充元素,但不能越過隊首。這樣一來,直線的排列方式實際上已經變成圓環形了。







代碼實現:
ArrayQueue.h:
/********************************************************/
// 用數組實現順序隊列(array based queue)的定義
// 繼承基類棧 Queue<E>
/********************************************************/
#pragma once

#include "Queue.h"
const size_t defaultQSize = 5;//Queue數據的默認長度

template<class E>
class AQueue : public Queue<E>
{
private:
	E* data;   //存儲Queue數據的數組
	int front; //隊首的數組下標
	int rear;  //隊尾的數組下標+1
	int maxSize;//簡單實現,只開闢一次空間,所以有最大大小
	int iNr;   //隊列中的元素個數,隊列長度
public:
	AQueue(size_t size = defaultQSize);
	~AQueue();

	void clear();

	// 入隊,只能從隊尾插入
	void enqueue(const E& it);

	// 出隊,只能從隊首刪除,返回類型不能是引用,因爲返回的隊首值已經不存在了
	E dequeue();

	// 返回類型是const引用,所以你能獲取棧頂的值,但不能改變它
	const E& frontValue()const;

	// 返回棧中當前存儲的元素個數
	int length() const;

};

ArrayQueue_Def.h:
/********************************************************/
// 用數組實現順序隊列(array based queue)的定義
// 繼承基類棧 Queue<E>
// 此處實現AQueue<E>的成員函數
/********************************************************/
#pragma once

#include "ArrayQueue.h"

// 定義構造函數,開闢空間
template<class E>
AQueue<E>::AQueue(size_t size = defaultSize) : front(0), rear(0),iNr(0),maxSize(size)
{
	data = new E[size];
}

template<class E>
AQueue<E>::~AQueue()
{
	delete[] data;
}

// 清空棧,只是修改top下標,不收回內存
template<class E>
void AQueue<E>::clear()
{
	front = rear = iNr = 0;
}

// 入隊,只能從隊尾插入
template<class E>
void AQueue<E> ::enqueue(const E& it)
{
	Assert(iNr < maxSize, "隊列已滿");
	
	// 小於maxSize時就是rear,等於maxSize時變成0
	rear %= maxSize;	
	data[rear++] = it;
	iNr++;
}

// 彈出棧頂元素
template<class E>
E AQueue<E> ::dequeue()
{
	Assert(iNr > 0, "空隊列");

	E it = data[front++]; //front下移一位
	front %= maxSize;     // 小於maxSize時就是front,等於maxSize時變成0
	iNr--;
	return it;
}

// 獲取棧頂元素
template<class E>
const E& AQueue<E> ::frontValue() const
{
	Assert(iNr > 0, "空棧");
	return data[front];
}

// 返回棧中元素個數
template<class E>
int AQueue<E>::length() const
{
	return iNr;
}


3,鏈式隊列

鏈式隊列是對單向鏈表的簡化。需要注意的是,這裏front不再是指向隊首,front裏面不存儲隊列中任意一個元素的數值,front->next存儲的纔是隊首元素。

LinkedQueue.h:
/********************************************************/
// 用模板實現鏈式隊列(Linked Queue)的定義
// 是單向鏈表的簡化
// 繼承基類 Queue<E>
/********************************************************/
#pragma once

#include "Queue.h"

// 1,定義節點類模板
template<class E>
class QNode
{
public:
	E element; //本結點存儲的元素值
	QNode* next;//指向下一結點的指針
	QNode(const E& elemval, QNode* nextval = NULL) :element(elemval), next(nextval) {}
	QNode(QNode* nextval = NULL) :next(nextval) {}
};

// 2,定義鏈表類模板
template<class E>
class LQueue : public Queue<E>
{
private:
	QNode<E>* front; //隊首
	QNode<E>* rear;  //隊尾
	int cnt;         //鏈表中當前存儲的元素個數
	void init();     //初始化
public:
	LQueue();
	~LQueue();

	void print() const;
	void clear();

	// 入隊,只能從隊尾插入
	void enqueue(const E& it);

	// 出隊,只能從隊首刪除,返回類型不能是引用,因爲返回的隊首值已經不存在了
	E dequeue();

	// 返回棧中當前存儲的元素個數
	int length() const;

	// 返回隊首的值,返回類型是const引用,所以你能獲取值,但不能改變它
	const E& frontValue()const;
};

LinkedQueue_Def.h:
/********************************************************/
// 用模板實現鏈式隊列(Linked Queue)的定義
// 是單向鏈表的簡化
// 繼承基類 Queue<E>
// 本文件實現成員函數的定義
/********************************************************/
#pragma once
#include "LinkedQueue.h"


template<class E>
void LQueue<E>::init()
{
	//所有指針指向新開的空間
	front = rear = new QNode<E>();
	cnt = 0;
}

//構造函數
template<class E>
LQueue<E>::LQueue()
{
	//構造時只開闢一個節點的空間
	init();
}

//析構函數
template<class E>
LQueue<E>::~LQueue()
{
	clear();
	delete front; //空的首節點也不能留
}

//打印所有元素
template<class E>
void LQueue<E>::print() const
{
	Node<E>* temp = front->next; //head中不存儲數據,head->next中才有數據
	while (NULL != temp)
	{
		//此處暗示類型E必須定義了“<<”操作,否則報錯
		cout << temp->element << endl;
		temp = temp->next;
	}
}

//清空鏈表
template<class E>
void LQueue<E>::clear()
{
	QNode<E>* temp = front->next;
	//刪除除front之外的所有節點
	while(NULL != temp)
	{
		front->next = temp->next;
		delete temp;
		temp = front->next;		
	}
	rear = front;
	cnt = 0;
}

//入隊
template<class E>
void LQueue<E>::enqueue(const E& it)
{
	rear = rear->next = new QNode<E>(it);
	cnt++;
}

//出隊
template<class E>
E LQueue<E>::dequeue()
{
	// 由於front節點不存儲真實的內容,要刪除的不是front,而是front->next
	Assert(cnt != 0, "隊列爲空");
	QNode<E>* temp = front->next;
	E it = front->next->element;

	// front節點的位置不變,但是front->next指向下一個
	front->next = temp->next;

	// 要刪除的是最後一個元素
	if (temp == rear)
	{
		rear = front;
	}

	delete temp;
	cnt--;
	return it;
}

// 返回個數
template<class E>
int LQueue<E>::length() const
{
	return cnt;
}

template<class E>
const E& LQueue<E>::frontValue()const
{
	return front->next->element;
}


發佈了99 篇原創文章 · 獲贊 13 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章