目录
先放上ADT的解释和预定义常量。
抽象数据类型(ADT)是指一个数学模型以及定义在该模型上的一组操作。抽象数据类型的定义仅取决于它的一组逻辑特性,而与其在计算机内部如何表示和实现无关,即不论其内部结构如何变化,只要它的数学特性不变,都不影响其外部的使用。
//预定义常量和类型:
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1 //INFEASIBLE意为不可行
#define OVERFLOW -2
typedef int Status;
typedef int QElemType;
以下是队列的抽象数据类型定义
ADT Queue{
//此处省略了数据对象和数据关系的定义
基本操作:
InitQueue(&Q)//构造队列
DestoryQueue(&Q)//销毁队列
ClearQueue(&Q)//清空队列
QueueEmpty(Q)//判断队列为空
QueueLength(Q)//队列长度
GetHead(Q,&e)//队首元素
EnQueue(&Q,e)//入队操作
DeQueue(&Q,&e)//退队操作
QueueTraverse(Q,visit())//遍历队列
}ADT Queue
单链队列的表示与实现
首先是链队列的数据成员,既然是和链有关那自然少不了指针。此处用的是C语言中的结构来表示。
对于队列先进先出的特点,允许删除的一端称为队头(front),允许插入的一端称为队尾(rear)。
typedef struct QNode{
QEleType data;
struct QNode *next;
}QNode,* QueuePtr;
typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
接下来开始逐步实现链队列的各个函数。
Status InitQueue(LinkQueue &Q)
//构造一个空队列
分配内存这块其实就是QueuePtr Q.front = new QNode();
Status InitQueue(LinkQueue &Q){
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if(!Q.front)
exit(OVERFLOW);//分配失败代表溢出
Q.front->next=NULL;
return ok;
}
Status DestroyQueue(LinkQueue& Q)
//销毁队列
销毁意味着将队列从内存中释放,直接free即可
Status DestroyQueue(LinkQueue& Q)
{
while (Q.front) {
Q.rear = Q.front->next;
free(Q.front);
Q.front = Q.rear;
}
return OK;
}
Status ClearQueue(LinkQueue& Q)
//清空队列
和销毁不同的时清空队列时可能出现队列为空的情况,所以要先判断是否为空。同时清空队列时要保留了队列的头指针,相当于pop出了队列的所有元素但仍保留队列本身,以后还可以对队列进行插入元素的操作;而销毁队列就不能再插入元素了。
Status ClearQueue(LinkQueue& Q)
{
if (Q.front == Q.rear)return ERROR;
Q.rear = Q.front->next;
while (Q.front->next)
{
Q.rear = Q.rear->next;
free(Q.front->next);
Q.front->next = Q.rear;
}
Q.rear = Q.front;
return OK;
}
Status QueueEmpty(LinkQueue& Q)
//判断队列为空
头尾指针相等时就为空了,比较简单
Status QueueEmpty(LinkQueue& Q)
{
//判断是否为空
if (Q.front == Q.rear)
{
return TRUE;//为空
}
return FALSE;
}
Status QueueLength(LinkQueue& Q)
//求队列长度
相当于queue.length(),遍历队列最后返回长度即可
Status QueueLength(LinkQueue& Q)
{
int len = 0;
if (Q.front == Q.rear)return 0;
Q.rear = Q.front->next;
while (Q.rear)
{
Q.rear = Q.rear->next;
len++;
}
return len;
}
Status GetHead(LinkQueue& Q, QElemType& e)
//获取队列的队列开头的元素,并保存到e中
相当于queue.front(),记住头指针不保存数据,头指针的下一个即front->next才保存着队首的数据
Status GetHead(LinkQueue& Q, QElemType& e)
{
if (Q.front == Q.rear) return ERROR;
e = Q.front->next->data;
return OK;
}
Status QueueTraverse(LinkQueue& Q/*,visit()*/)
//遍历队列并对每个成员通过visit函数进行访问
这里因为队列元素都是int类型所以省略了visit函数,但如果是结构,类或者STL之类的就要好好写visit函数了。
遍历方式参照链表的遍历,比STL里队列的优点就是遍历的时候不用pop出队列元素。
Status QueueTraverse(LinkQueue& Q/*,visit()*/)
{
if (Q.front == Q.rear)
{
cout << "Queue is Empty." << endl;
return ERROR;
}
QueuePtr p = Q.front->next;
while (p)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
return OK;
}
Status EnQueue(LinkQueue &Q, QElemType e)
//队队列进行入队操作,e进入队尾
Status DeQueue(LinkQueue &Q, QElemType& e)
//对队列进行退队操作,退出队首元素,并保存到e中
这两个函数一起讲。入队的时候需要新建一个节点p(p->next=NULL)来接到队列尾部,由于rear就是队列尾部所以直接Q.rear->next=p,并把新的队尾更新为p(即Q.rear=p)
退队时记得看看队列是不是为空先。要注意的是如果队列只剩一个元素再退队时,此时队列为空,需要更新头尾指针使头尾指针相等。
Status EnQueue(LinkQueue &Q, QElemType e)
{
QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
if (!p)
exit(OVERFLOW);
p->data = e;p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
Status DeQueue(LinkQueue &Q, QElemType& e)
{
if (Q.front == Q.rear)return ERROR;
QueuePtr p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if (Q.rear == p)
Q.rear = Q.front;
free(p);
return OK;
}
以下是完整代码实现
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int QElemType;
using namespace std;
typedef struct QNode {
QElemType data;
struct QNode* next;
}QNode, * QueuePtr;
typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
Status InitQueue(LinkQueue& Q) {
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if (!Q.front)
exit(OVERFLOW);
Q.front->next = NULL;
return OK;
}
Status DestroyQueue(LinkQueue& Q)
{
while (Q.front) {
Q.rear = Q.front->next;
free(Q.front);
Q.front = Q.rear;
}
return OK;
}
Status ClearQueue(LinkQueue& Q)
{
if (Q.front == Q.rear)return ERROR;
Q.rear = Q.front->next;
while (Q.front->next)
{
Q.rear = Q.rear->next;
free(Q.front->next);
Q.front->next = Q.rear;
}
Q.rear = Q.front;
return OK;
}
Status QueueEmpty(LinkQueue& Q)
{
//判断是否为空
if (Q.front == Q.rear)
{
return TRUE;//为空
}
return FALSE;
}
Status QueueLength(LinkQueue& Q)
{
int len = 0;
if (Q.front == Q.rear)return 0;
Q.rear = Q.front->next;
while (Q.rear)
{
Q.rear = Q.rear->next;
len++;
}
return len;
}
Status GetHead(LinkQueue& Q, QElemType& e)
{
if (Q.front == Q.rear) return ERROR;
e = Q.front->next->data;
return OK;
}
Status QueueTraverse(LinkQueue& Q/*,visit()*/)
{
if (Q.front == Q.rear)
{
cout << "Queue is Empty." << endl;
return ERROR;
}
QueuePtr p = Q.front->next;
while (p)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
return OK;
}
Status EnQueue(LinkQueue &Q, QElemType e)
{
QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
if (!p)
exit(OVERFLOW);
p->data = e;p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
Status DeQueue(LinkQueue &Q, QElemType& e)
{
if (Q.front == Q.rear)return ERROR;
QueuePtr p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if (Q.rear == p)
Q.rear = Q.front;
free(p);
return OK;
}
为了方便测试这里写了主函数,各位可以根据需要自行改进。(Show函数为遍历队列+打印输出,和上面的QueueTraverse是同样的功能)
Status Show(LinkQueue& Q)
{
if (Q.front == Q.rear)
{
cout << "Queue is Empty." << endl;
return ERROR;
}
QueuePtr p = Q.front->next;
while (p)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
return OK;
}
int main()
{
LinkQueue Q;
InitQueue(Q);
int n;
cin >> n;
for (int i = 0;i < n;i++)
{
EnQueue(Q, i);
}
int tmp = 0;
cout << "len: " << QueueLength(Q) << endl;
Show(Q);
GetHead(Q, tmp);
cout << "head is " << tmp << endl;
DeQueue(Q, tmp);
Show(Q);
ClearQueue(Q);
Show(Q);
EnQueue(Q,827);
Show(Q);
if (QueueEmpty(Q) == TRUE)
{
cout << "Is empty." << endl;
}
else
{
cout << "Not empty." << endl;
}
EnQueue(Q, 106);
Show(Q);
DestroyQueue(Q);
Show(Q);
}