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

简介: 队列(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;               // 返回队头元素的值,队头指针不变}


目录
相关文章
|
23天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
109 9
|
13天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
22 1
|
16天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
19天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
21天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
47 4
|
25天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
数据结构(栈与列队)
数据结构(栈与列队)
20 1
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
33 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
初步认识栈和队列
初步认识栈和队列
61 10
|
1月前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
48 3