数据结构之队列

简介: 数据结构之队列

队列的定义:

只允许在一端进行插入(入队),在另一端删除(出队)的线性表。

举例:

在队末进行入队,在对头进行出队。


队列的特点:

先进入队列的元素先出队(先进先出)

空队列:

对头和队尾元素:

队列的基本操作:


定义队列:

#define Maxsize 10
typedef struct
{
  ElemType data[Maxsize];//用静态数组存放队列元素
  int front, rear;//队头和队尾指针
}SqQueue;

InitQueue(&Q):初始化队列,构造一个空队列Q。

void InitQueue(SqQueue&Q)
{
  Q.rear = Q.front = 0;//初始化时,队头,队尾指针指向0
}

DestoryQueue(&Q):销毁队列,销毁并释放队列Q所占用的内存空间。

EnQuquq(&Q,x):入队,若队列Q未满,将x加入,使之成为新的队尾。

bool EnQueue(SqQueue& Q, ElemType x)
{
  if (队列已满)//判断队列是否存满
    return false;
  Q.data[Q.rear] = x;//将x插入队尾
  Q.rear = (Q.rear + 1)%Maxsize;//队尾指针加1取模
  return true;
}

那么队列已满的条件是不是rear==Maxsize?

那当然不是!

举例:

如果是上述这种情况,虽然此时rear==Maxsize,但是队列中的前几个元素已经出队列了,此时的队列并没有存满。

正确的判断条件是:

方案一:

(Q.rear + 1)%Maxsize//(9+1)%10=0使rear指向0

相当于将线性存储在逻辑结构上变成了环状:

队列已满的条件:

队尾指针的再下一个位置是对头,即(Q.rear+1)%Maxsize==Q.front

注意:这里无法将所有的空间都使用,因为如果当rear和front指向同一个位置,那么这不就恰好成为了空队列的条件吗?因此我们必须牺牲一个存储单元。

方案二:

方案三:


当rear指向的是队尾元素时的基本操作:

队列元素个数:

(rear+Maxsize-front)%Maxsize

DeQueue(&Q,&x):出队,若队列Q非空,删除对头元素,并用x返回。

bool DeQueue(SqQueue& Q, ElemType& x)
{
  if (Q.rear == Q.front)//空栈则报错
  {
    return false;
  }
  x = Q > data[Q.front];
  Q.front = (Q > front + 1) % Maxsize;//从队头元素开始,使元素依次出队
  return true;
}

GetHead(Q,&x):读对头元素,若队列Q非空,则将对头元素赋值给x。

bool GetHead(SqQueue Q, ElemType& x)
{
  if (Q.rear == Q.front)//队空则报错
    return false;
  x = Q.data[Q.front];
  return true;
}

QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回false.

bool QueueEmpty(SqQueue Q)
{
  if (Q.rear == Q.front)
    return true;
  else
    return false;
}

队列的链式实现:

typedef struct LinkNode//链式队列结点
{
  ElemType data;
  struct LinkNode* next;
}LinkNode;
typedef struct//链式队列
{
  LinkNode* front, * rear;//队列的对头和队尾指针
}LinkNode;

链队列:链式存储实现的队列

链式队列的初始化:

//带头结点
void InitQueue(LinkQueue& Q)
{
  Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));//使指针都指向头结点
  Q.front->next = NULL;
}
//不带头结点
void InitQueue(LinkQueue& Q)
{
  Q.front = NULL;
  Q.rear = NULL;
}

链式队列的判空操作:

//带头结点
bool IsEmpty(LinkQueue Q)
{
  if (Q.front == Q > rear)
    return true;
  else
    return false;
}
//不带头结点
bool IsEmpty(LinkQueue Q)
{
  if (Q.front == NULL)
    return true;
  else
    return false;
}

链式队列的入队:

//带头结点
void EnQueue(LinkQueue& Q, ElemType x)
{
  LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
  s->data = x;
  s->next = NULL;
  Q.rear->next=s;//新节点插入rear之后
  Q.rear = s;//修改表尾指针
}
//不带头结点
void EnQueue(LinkQueue& Q, ElemType x)
{
  LInkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
  s->data = x;
  s->next = NULL;
  if (Q.front == NULL)//在空队列中插入第一个元素
  {
    //修改队头队尾指针
    Q.front = s;
    Q.rear = s;
  }
  else
  {
    Q.rear->next = s;//新节点插入到rear节点之后
    Q.rear = s;//修改rear指针
  }
}

链式队列的出队操作:

//带头结点
bool DeQueue(LinkQueue& Q, ELemType& x)
{
  if (Q.front == Q.rear)//空队
    return false;
  LinkNode* p = Q.front->next;
  x = p->data;//用变量x返回队头元素
  Q.front->next = p->next;//修改头结点的next指针
  if (Q > rear == p)//最后一个节点出队
  {
    Q.rear = Q.front;//修改rear指针
  }
  free(p);
  return true;
}
//不带头结点
bool DeQueue(LinkQueue& Q, ElemType& x)
{
  if (Q.front == NULL)//空队列
    return false;
  LinkNode* p = Q.front;//p指向此次出队列的节点
  x = p->data;//用变量x返回队头元素
  Q.front = p->next;//修改front指针
  if (Q.rear == p)//最后一个节点出队
  {
    Q.front = NULL;
    Q.rear = NULL;
  }
  free(p);
  return true;
}

上面我们提到如果是顺序存储,那么当预分配的空间耗尽时,就代表此时队列已满,而对于链式存储来说,一般情况下,队列是不会满的,除非内存不足。

双端队列:

前面我们学习过,栈是只允许从一端插入和删除的线性表,队列是只允许从一端插入,另一端删除的线性表,而双端队列是只允许从两端插入,两端删除的线性表。

判断输出序列的合法性:

若数据元素输入序列为1,2,3,4,则那些输出序列是合法的?那些是非法的?

对数据元素1,2,3,4的输出共有24种:

在栈中:

红色字体代表非法输出,绿色字体代表合法输出!

举例分析:


当为1,2,3,4的顺序时:


数据1,输入再输出,数据2输入再输出,直到数据四完成输出。


当为2,4,1,3的顺序时:


数据2作为第一个元素输出,那么,输入1,2,输出2,接下来要输出数据4,那么,输入3,4,输出4,此时栈中剩余1,3,1是在3之前输入的,要想输出1,就必须先输出3,因此无法完成这组的输出。


其他结果也可按此进行分析。


注:在栈中合法的输出序列,在双端序列中一定合法。


对于计算合法输出序列,我们可以使用卡特兰数:


在输入受限的双端队列中:

红色字体代表非法输出,绿色字体代表合法输出!

在输出受限的双端队列中:

红色字体代表非法输出,绿色字体代表合法输出!

输出/输入受限的双端队列中判断序列的输出是合法还是非法,和在栈中判断方法基本相同,只需要注意栈只能在一端,而输出/输入受限的双端队列虽然可以在两端,但会有不同的限制!

相关文章
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
1月前
|
缓存 算法 调度
数据结构之 - 双端队列数据结构详解: 从基础到实现
数据结构之 - 双端队列数据结构详解: 从基础到实现
61 5
|
1月前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
99 64
|
17天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
初步认识栈和队列
初步认识栈和队列
58 10
|
1月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
20 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
1月前
|
存储 安全 Java
【用Java学习数据结构系列】探索栈和队列的无尽秘密
【用Java学习数据结构系列】探索栈和队列的无尽秘密
30 2
【数据结构】--- 栈和队列
【数据结构】--- 栈和队列
|
1月前
|
消息中间件 存储 Java
数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
29 4
|
1月前
【初阶数据结构】深入解析队列:探索底层逻辑
【初阶数据结构】深入解析队列:探索底层逻辑

热门文章

最新文章