- 整理数据结构学习笔记
- 基于C++
文章目录
- 一、队列的相关定义
- 二、队列的表示和实现
- 1、链队列
- (1)定义
- (2)数据结构
- (3)基本操作
- 1、初始化队列 bool InitQueue(LinkQueue &Q)
- 2、销毁队列 bool DestroyQueue(LinkQueue &Q)
- 3、置空 bool ClearQueue(LinkQueue &Q)
- 4、判空 bool QueueEmpty(LinkQueue Q)
- 5、返回队长度 int QueueLength(LinkQueue Q)
- 6、队头元素 bool GetHead(LinkQueue Q,int &e)
- 7、入队 bool EnQueue(LinkQueue &Q,int e)
- 8、出队 bool DeQueue(LinkQueue &Q,int &e)
- 9、遍历队列 bool QueueTraverse(LinkQueue Q,bool (*visit)(QNode a))
- 2、顺序队列(循环队列)
一、队列的相关定义
队列
(queue):是限定仅在队尾进行插入,在队头进行删除的线性表。队头
(front):线性表中允许删除的一端队尾
(rear):线性表中允许插入的一端空队
:不含元素的空表入队
(Enqueue):在线性表尾部(队尾)后新增一个元素出队
(Dequeue):把线性表头部(队头)的元素取出并从表中删除- 可见,最先入队的元素最先出队,这种特性称为
先进先出
(FIFO)
二、队列的表示和实现
队列通常用链式存储结构实现
1、链队列
(1)定义
- 链队列是使用单链表结构实现的队列
- 设置两个指针分别指示队头(头指针)和队尾(尾指针)
- 为方便起见,在真正队头元素的前一个添加头结点,令头指针指向它。
(2)数据结构
//队列(链表)中的一个节点
typedef struct QNode
{
int data;
struct QNode *next;
}QNode,*QueuePtr;
//队列
typedef struct
{
QueuePtr front; //头指针
QueuePtr rear; //尾指针
}LinkQueue;
(3)基本操作
1、初始化队列 bool InitQueue(LinkQueue &Q)
- 初始化队列为空队,申请一个结点作为头结点,并让头尾指针指向它即可
- 注意这时Q.front==Q.rear,这也是空队的判据
bool InitQueue(LinkQueue &Q)
{
Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));
if(!Q.front)
exit(OVERFLOW);
Q.front->next=NULL;
return OK;
}
2、销毁队列 bool DestroyQueue(LinkQueue &Q)
- 可以利用头尾指针遍历队列,依次释放所有结点空间
bool DestroyQueue(LinkQueue &Q)
{
while(Q.front)
{
Q.rear=Q.front->next;
free(Q.front);
Q.front=Q.rear;
}
return OK;
}
3、置空 bool ClearQueue(LinkQueue &Q)
- 注意和栈的区别,链表结点空间难以重复利用,置空时必须把除头结点外的所有队列结点释放空间
- 设两个指针遍历头结点后的队列(链表),依次释放空间
- 重设尾指针,令Q.rear=Q.front
- 特别注意不要忘了Q.rear->next=NULL
bool ClearQueue(LinkQueue &Q)
{
QueuePtr p,q;
p=Q.front->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
Q.rear=Q.front;
Q.rear->next=NULL;
return OK;
}
4、判空 bool QueueEmpty(LinkQueue Q)
- 判断top和base是否相等
bool QueueEmpty(LinkQueue Q)
{
if(Q.front==Q.rear)
return true;
else
return false;
}
5、返回队长度 int QueueLength(LinkQueue Q)
- 设一个指针遍历队列计数
int QueueLength(LinkQueue Q)
{
int l=0;
QueuePtr p;
p=Q.front->next;
while(p)
{
l++;
p=p->next;
}
return l;
}
6、队头元素 bool GetHead(LinkQueue Q,int &e)
- 先判断队列非空
- 取头结点后第一个元素
bool GetHead(LinkQueue Q,int &e)
{
if(QueueEmpty(Q))
return ERROR;
QueuePtr p;
p=Q.front->next;
e=p->data;
return OK;
}
7、入队 bool EnQueue(LinkQueue &Q,int e)
- 大体和链表在尾部插入一个结点的操作相同,只是要注意一下尾指针
- 申请结点,调整指针顺序,图示为在空表中添加元素x和y
bool EnQueue(LinkQueue &Q,int e)
{
QueuePtr p;
p=(QueuePtr)malloc(sizeof(QNode));
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p; //这个别忘了
return OK;
}
8、出队 bool DeQueue(LinkQueue &Q,int &e)
- 先判断队列非空
- 取头结点后第一个元素,移动指针 Q.front->next=Q.front->next
- 特别注意,如果队中只有一个元素,移动头指针后front指向NULL正确,但尾指针还指向着出队的那个元素,如果现在直接释放结点空间,尾指针就会丢失,所以这里要单独判断一下尾指针的情况,图示为x元素出队的情形
- 处理好指针问题后,释放出队结点的空间
bool DeQueue(LinkQueue &Q,int &e)
{
if(QueueEmpty(Q))
return ERROR;
QueuePtr p;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
//防止只剩一个元素时队尾指针没了
if(Q.rear==p)
Q.rear=Q.front;
free(p);
}
9、遍历队列 bool QueueTraverse(LinkQueue Q,bool (*visit)(QNode a))
bool QueueTraverse(LinkQueue Q,bool (*visit)(QNode a))
{
QNode *p;
p=Q.front->next;
while(p)
{
if(visit(*p)==ERROR)
return ERROR;
p=p->next;
}
return OK;
}
bool Visit(QNode a)
{
cout<<a.data<<" ";
return OK;
}
2、顺序队列(循环队列)
(1)定义
- 顺序队列是使用一组连续的存储单元实现的队列
- 设置两个指针分别指示队头(头指针)和队尾(尾指针)。为方便实现,我们规定初始化空队时令front=rear=0,每当尾部有元素入队,rear加一;每当头部有元素出队,front加一
- 根据以上约定,非空队列中,front指针始终指向头部元素,rear始终指向真正尾部元素再下一个,如图所示
- 很容易发现队列头部的空间无法重复利用,为了解决这一问题,我们通常把顺序队列臆造为环状结构,称之循环队列
- 如上图所示,循环队列中,front==rear时可能为空队也可能为满队,不能再用它作为判空依据了。通常的做法有两个:一是和上图一样牺牲一个存储单元,判空依据为“rear指针的下一个位置为front指向位置”;二是另设一个判空标志,同时判断指针相等和判空标志。
- 这样处理后,由于指针的问题,无法使用动态申请空间的方法延长队列了,所以一定要事先确定循环队列长度,如果难以确定最好还是使用链式队列。
(2)数据结构
typedef struct
{
int rear; //尾指针
int front; //头指针
int *base; //队列空间基地址
int size; //队列尺寸(包括用于判空那个元素在内)
}CQueue;
(3)基本操作
1、初始化队列 bool InitQueue(CQueue &Q,int size)
- 传入队最大长度,初始化队列为空队
- 注意多申请一个空间用于区分空队满队
- 指针front=rear=0
bool InitQueue(CQueue &Q,int size)
{
Q.size=size+1; //有一个空间浪费在判空
Q.base=(int *)malloc(Q.size*sizeof(int));
if(!Q.base)
return ERROR;
Q.front=Q.rear=0;
return OK;
}
2、置空 void ClearQueue(CQueue &Q)
- 循环队列用数组实现,类似栈。清空时只要移动指针即可,不需要释放空间
- 直接设置指针front=rear=0
void ClearQueue(CQueue &Q)
{
Q.front=Q.rear=0;
}
3、判空 bool QueueEmpty(CQueue Q)
- 判断top和base是否相等
bool QueueEmpty(CQueue Q)
{
if(Q.rear==Q.front)
return true;
else
return false;
}
4、判满 bool QueueFull(CQueue Q)
- 判断rear后移一位后是否和front相等
- 循环队列中指针p后移一位:p=(p+1)%size
bool QueueFull(CQueue Q)
{
if((Q.rear+1)%Q.size==Q.front)
return true;
else
return false;
}
5、返回队长度 int QueueLength(CQueue Q)
- 由于数组特性,可以用指针进行加减运算来求长度
int QueueLength(CQueue Q)
{
int l=Q.rear-Q.front;
if(l>0)
return l;
else
return Q.size+l;
}
6、入队 bool EnQueue(CQueue &Q,int e)
- 先判断队列未满
- 在尾指针处赋值,后移尾指针Q.rear=(Q.rear+1)%Q.size
bool EnQueue(CQueue &Q,int e)
{
if(QueueFull(Q))
return ERROR;
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%Q.size;
return OK;
}
7、出队 bool DeQueue(CQueue &Q,int &e)
- 先判断队列非空
- 取指针处值,后移头指针Q.front=(Q.front+1)%Q.size
bool DeQueue(CQueue &Q,int &e)
{
if(QueueEmpty(Q))
return ERROR;
e=Q.base[Q.front];
Q.front=(Q.front+1)%Q.size;
return OK;
}