考试前复习下数据结构,把一些知识点整理在这!主要参考了殷人昆主编的《数据结构(用面向对象方法与C++语言描述)》这本书,以及中山大学刘聪老师的课件内容!
概念与性质
一些概念
队列是一种操作受限制的线性表,它只允许在表的一端插入,在另一端删除。
- 队尾:允许插入的一端
- 队头:允许删除的一端
队列的性质
由于队列是一种运算受限的线性表,只允许在表的一端进行插入,另一端进行删除,所以队列具有一些特殊的性质。给定队列Q=(a1, a2, …, an),则最先入队的元素a1为队头,最后入队的元素an为队尾,按a1, a2, …, an的顺序入队,出队的顺序与入队的一致,即a1先出队,an最后出队,就如同在车站排队买票一样。这样的性质可以概括为先进先出(FIFO)。
队列的操作
队列有几个主要的操作:
- 建立一个空队列(构造函数Queue());
- 在队尾添加一个新元素,如果队列未满(append() );
- 在队头删除一个元素,如果队列不空(serve() );
- 检查队列是否空(isEmpty());
- 取得队头元素,如果队列不空(retrieve())。
队列的抽象数据类型主要有两种存储表示:基于数组的存储表示(顺序队列)和基于链表的存储表示(链式队列)。
顺序队列
利用一个一维数组作为队列元素的存储结构,并设置两个指针front和rear,分别指示队列的队头和队尾的位置。
其中,front指向队头元素所在位置,rear指向队尾位置的后一位置。有元素入队时,更新rear指针;有元素出队时,更新front指针。
那么,问题来了。当rear == maxSize时,队列满,如果再加入新元素,就会产生“溢出”。但是,这种“溢出”可能是假溢出,因为数组的前端可能还有空位置。那么,要怎么解决这种浪费空间的问题呢?
我们可以采用循环队列的存储方式,即把数组的前端和后端连接起来,形成一个环形的表。
此时,每次更新两个指针时,不再是简单的+1了,而需要用到取余运算。
front = (front+1) % maxSize;
rear = (rear+1) % maxSize;
当队头指针追上队尾指针,即front == rear时,队列就变为空队列。为了区别队空和队满的状态,用(rear+1) % maxSize == front来判断是否队已满,也就是说让rear指到front的前一位置就认为队已满,所以在队满时实际空了一个元素的位置。注意:循环队列中最多只能存放maxSize-1个元素。(当然,这只是循环队列的一种实现方式,也可以使用其他方法判断队空队满)。
循环队列数组实现
// 定义两个异常处理类
class EmptyQueueException : public runtime_error {
public:
EmptyQueueException() : runtime_error("The queue is empty.") {}
};
class FullQueueException : public runtime_error {
public:
FullQueueException() : runtime_error("The queue is full.") {}
};
template <typename T>
class Queue {
public:
Queue(int size = 10) : rear(0), front(0), maxSize(size) {
elements = new T[maxSize];
}
~Queue() {
delete []elements;
}
bool isEmpty() const {
return front == rear;
}
bool isFull() const {
return (rear+1) % maxSize == front;
}
int getSize() const {
return (rear-front+maxSize) % maxSize;
}
T retrieve() {
if (isEmpty()) {
throw EmptyQueueException();
}
return elements[front];
}
void append(const T& x) {
if (isFull()) {
throw FullQueueException();
}
elements[rear] = x;
rear = (rear+1) % maxSize;
}
T serve() {
if (isEmpty()) {
throw EmptyQueueException();
}
T x = elements[front];
front = (front + 1) % maxSize;
return x;
}
private:
int rear; // 队尾指针
int front; // 队头指针
T * elements; // 存放队列元素的数组
int maxSize; // 队列可容纳的最大元素个数
};
链式队列
链式队列是基于单链表的一种存储方式。
队列的队头指针指向单链表的第一个结点,队尾指针指向单链表的最后一个结点。
用单链表表示的链式队列适用于数据元素变动比较大的情形,而且不存在队列满而溢出的情况。
// 定义异常处理类
class EmptyQueueException : public runtime_error {
public:
EmptyQueueException() : runtime_error("The queue is empty.") {}
};
template <typename T>
class Queue {
public:
Queue() : rear(NULL), front(NULL) {}
~Queue() {
Node* p;
while (front != NULL) {
p = front;
front = front->next;
delete p;
}
}
bool isEmpty() const {
return front == NULL;
}
int getSize() const {
Node* p = front;
int count = 0;
while (p != NULL) {
p = p->next;
count++;
}
return count;
}
T retrieve() {
if (isEmpty()) {
throw EmptyQueueException();
}
return front->data;
}
void append(const T& x) {
if (front == NULL) {
front = rear = new Node;
front->data = x;
front->next = NULL;
return;
}
Node *newNode = new Node;
newNode->next = NULL;
newNode->data = x;
rear->next = newNode;
rear = rear->next;
}
T serve() {
if (isEmpty()) {
throw EmptyQueueException();
}
Node* temp = front;
T firstData = front->data;
front = front->next;
delete temp;
return firstData;
}
private:
struct Node {
T data;
Node * next;
};
Node* front; // 队头指针
Node* rear; // 队尾指针
};