数据结构(严蔚敏版)第三章——栈和队列(三)【队列的表示和操作的实现】

简介: 队列(Queue)是仅在表尾进行插入操作,在表头进行删除操作的线性表表尾即an端,称为队尾;表头即a1端,称为队头。它是一种先进先出(FIFO)的线性表;插入元素称为入队;删除元素称为出队、队列的存储结构为链队或顺序3.4、栈与递归3.4.1、采用递归算法解决的问题3.5、队列的表示和操作的实现3.5.1、相关术语3.5.2、队列的相关概念3.5.3、队列的类型定义3.5.4、队列的顺序表示和实现3.5.5、队列的链式表示和实现

3.4、栈与递归

3.4.1、采用递归算法解决的问题

1、定义是递归的:

  • 若一个对象部分地包含自己,或用它自己给自己定义,则称这个对象是递归的;
  • 若一个过程直接或者间接的调用自己,则称这个过程是递归的过程。

递归问题——用分治法求解

分治法:对于一个较为复杂的问题,能够分解成几个相对简单且解法相同或类似的子问题来求解

必备的三个条件

  • 1、能将一个问题转变成一个新问题,而且新问题与原问题的解法相同或类同,不同的仅是处理的对象,且这些处理对象是变化有规律的
  • 2、可以通过上述转化而使问题简化
  • 3、必须有一个明确的递归出口,或者递归的边界

”‘分治法“求解递归问题算法的一般形式为:

voidp(参数) {
if (递归结束条件成立) 可直接求解;      // 递归终止的条件elsep(较小的参数);                 // 递归步骤}
// 例如longFact (longn) {
if (n==0) return1;             // 基本项elsereturnn*Fact(n-1);      // 归纳项}

函数调用过程:

调用前,系统完成:

  1. 将实参,返回地址等传递给被调用函数
  2. 为被调用函数的局部变量分配存储区
  3. 将控制转移到被调用函数的入口

调用后,系统完成:

  1. 保存被调用函数的计算结果
  2. 释放被调用的函数的数据区
  3. 依照被调用函数保存的返回地址将控制转移到调用函数

多个函数构成嵌套调用时:

第四节记录不全:详细内容请观看第05周12--3.5队列的表示和实现1--3.5.1队列的类型定义哔哩哔哩

3.5、队列的表示和操作的实现

为了防止大家忘记队列的相关概念

3.5.1、相关术语:

  • 队列(Queue)是仅在表尾进行插入操作,在表头进行删除操作的线性表
  • 表尾即an端,称为队尾;表头即a1端,称为队头。
  • 它是一种先进先出(FIFO)的线性表
  • 插入元素称为入队;删除元素称为出队
  • 队列的存储结构为链队或顺序队(常用循环顺序队)

3.5.2、队列的相关概念

  1. 定义:只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表
  2. 逻辑结构: 与线性表相同,仍为一对一关系
  3. 存储结构:顺序队或链队,以循环顺序队列更常见。
  4. 运算规则:只能在队首或队尾运算,且访问结点时依照先进先出(FIFO)的原则
  5. 实现方式:关键是掌握入队和出队操作,具体实现依顺序队或链队的不同而不同

3.5.3、队列的类型定义

ADTQueue {
数据对象:D= {ai|ai属于ElemSet, i=1, 2, ... , n, n>=0}
数据关系:R= { <ai-1, ai>|ai-1, ai属于D, i=2, ... , n} // 约定其中a1端为队列头,an端为队列尾基本操作:InitQueue(&Q);
操作结果:构造空队列QDestroyQueue(&Q);
条件:队列Q已存在操作结果:队列Q被销毁ClearQueue(&Q);
条件:队列Q已存在操作结果:将Q清空QueueLength(&Q);
条件:队列Q已存在操作结果:返回Q的元素个数,即队长GetHead(Q, &e);
条件:Q为非空队列操作结果:用e返回Q的队头元素EnQueue(&Q, e);
条件:队列Q已存在操作结果:插入元素e为Q的队尾元素DeQueue(&Q, &e);
条件:Q为非空队列操作结果:删除Q的队头元素,用e返回值QueueTraverse(Q);
条件:Q存在且非空操作结果:从队头到队尾,依次对Q的每个数据元素访问}
  • 队列的物理存储可以分为顺序存储结构,也可用链式存储结构即:顺序队列链式队列

3.5.4、队列的顺序表示和实现

1、顺序存储结构
  • 队列的顺序表示—用一维数组base[MAXQSIZE]
#define MAXQSIZE 100      // 最大队列长度Typedefstruct {
QElemType*base;        // 初始化的动态分配存储空间intfront;              // 头指针intrear;               // 尾指针}SqQueue;

 

2、解决假上溢的方法——引入循环队列

base[0] 接在base[MAXQSIZE - 1]之后,若rear + 1 == M,则令rear = 0;

实现方法:利用模(mod, C语言中:%)运算

插入元素:Q.base[Q.rear] = x;

Q.rear = (Q.rear + 1) % MAXQSIZE;

删除元素:x = Q.base[s.front]

Q.front = (Q.front + 1) % MAXQSIZE;

两个重要的判断条件:

队空的条件:Q.front == Q.rear

队满的条件:(Q.rear+1) % MAXQSIZE == Q.front

3、队列的初始化

【算法步骤】

  • 为队列分配一个最大容量为MAXQSIZE的数组空间,base指向数组空间的首地址。
  • 头指针和尾指针置为0,表示队列为空

【算法描述】

StatusInitQueue(SqQueue&Q) {
Q.base=newQElemType[MAXQSIZE];           // 分配数组空间// Q.base = (QElemType*)malloc(MAXQSIZE * sizeof(QElemType));if (!Q.base) exit(OVERFLOW);                // 存储分配失败Q.front=Q.rear=0;                       // 头指针尾指针置为0,队列为空returnOK;
}
4、求队列的长度
  • 对于非循环队列,尾指针和头指针的差值就是队列的长度,而循环队列,差值可能为负数,所以需要加上MAXQSIZE,再与MAXQSIZE求余

【算法描述】

intQueueLength(SqQueueQ) {
return (Q.rear-Q.front+MAXQSIZE) %MAXQSIZE;
}
5、循环队列入队

【算法步骤】

  • 判断队列是否满,若满则返回ERROR
  • 将新元素插入队尾
  • 队尾指针加1

【算法描述】

StatusEnQueue(SqQueue&Q, QElemTypee) {
// 插入元素e为Q的新的队尾元素if ((Q.rear+1) %MAXQSIZE==Q.front)         // 尾指针在循环意义上加1后等于头指针,表明队满returnERROR;
Q.base[Q.rear] =e;                             // 新元素插入队尾Q.rear= (Q.rear+1) %MAXQSIZE;               // 队尾指针加1returnOK;
}
6、循环队列的出队

【算法步骤】

  • 判断队列是否为空,若空则返回ERROR
  • 保存队头元素
  • 队头指针加1

【算法描述】

StatusDeQueue(SqQueue&Q, QElemType&e) {
// 删除Q的队头元素if (Q.front==Q.rear) returnERROR;            // 队空e=Q.base[Q.front];                            // 保存队头元素Q.front= (Q.front+1) %MAXQSIZE;             // 队头指针加1returnOK;
}
7、循环队列取队头元素
  • 当队列非空时,此操作返回当前队头元素的值,队头指针保持不变

【算法描述】

SElemTypeGetHead(SqQueueQ) {
// 返回Q的队头元素,不修改队头指针if (Q.front!=Q.rear)            // 队列非空returnQ.base[Q.front];         // 返回队头元素的值,队头指针不变}

3.5.5、队列的链式表示和实现

链队列运算指针变化状况

1、链式队列的定义
#define MAXQSIZE 100        // 最大队列长度typedefstructQnode {
QElemTypedata;
structQNode*next;
}QNode, *QueuePtr;
typedefstruct {
QueuePtrfront;           // 队头指针QueuePtrrear;            // 队尾指针}LinkQueue;
2、链队列初始化

【算法步骤】

  • 生成新结点作为头结点,队头和队尾指针指向此结点
  • 头结点的指针域为空

【算法描述】

StatusInitQueue(LinkQueue&Q) {
// 构造一个空队列Q.front=Q.rear=newQNode;             // 生成新结点作为头结点,队头和队尾指针指向该结点Q.front->next=NULL;                     // 头结点的指针域为空returnOK;
}
3、链队的销毁

【算法步骤】

  • 从头结点开始,依次释放所有结点

【算法描述】

StatusDestroyQueue (LinkQueue&Q) {
while(Q.front) {
Q.rear=Q.front->next;
free(Q.front);
Q.front=Q.rear;
  }+returnOK;
}
4、链队列的入队

【算法描述】

  • 为入队元素分配结点空间,用指针p指向
  • 将新结点数据域置为e
  • 将新结点插入到队尾
  • 修改队尾指针为p

【算法描述】

StatusEnQueue(LinkQueue&Q, QElemTypee) {
// 插入元素e为Q的新的队尾元素p=newQNode;                        // 为入队元素分配结点空间,用指针p指向p->data=e;                        // 将新结点数据域置为ep->next=NULL; Q.rear->next=p; // 将新结点插入到队尾Q.rear=p;                           // 修改队尾指针returnOK;
}
5、链队列的出队

【算法步骤】

  • 判断队列是否为空,若为空则返回ERROR
  • 临时保存队头元素的空间,以备释放
  • 修改头结点的指针域, 指向下一个结点
  • 判断出队元素是否为最后一个元素,若是,则将队尾指针重新赋值,指向头结点
  • 释放原队头元素的空间

【算法描述】

StatusDeQueue(LinkQueue&Q, QElemType&e){
// 删除Q的队头元素,用e返回其值if (Q.front==Q.rear) returnERROR;          // 若队列为空,则返回ERRORp=Q.front->next;                          // p指向队头元素e=p->data;                                // e保存队头元素的值Q.front->next=p->next;                  // 修改头结点的指针域if (Q.rear==p) Q.rear=Q.front;            // 最后一个元素被删,队尾指针指向头结点deletep;                                     // 释放原队头元素的空间returnOK;
}
6、链队中求队头元素

【算法描述】

SElemTypeGetHead(LinkQueueQ) {
// 返回Q的队头元素,不修改队头指针if (Q.front!=Q.rear)                          // 队列非空returnQ.front->next->data;               // 返回队头元素的值,队头指针不变}


目录
相关文章
|
4天前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
1月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
142 77
|
1月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
38 7
|
1月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
44 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
1月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
46 9
|
3月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
99 5
|
3月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
72 0
|
3月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
54 1
|
3月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
116 21
|
3月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。

热门文章

最新文章