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

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


目录
相关文章
|
5天前
|
算法 C语言 C++
【practise】栈的压入和弹出序列
【practise】栈的压入和弹出序列
|
3天前
栈的几个经典应用,真的绝了
文章总结了栈的几个经典应用场景,包括使用两个栈来实现队列的功能以及利用栈进行对称匹配,并通过LeetCode上的题目示例展示了栈在实际问题中的应用。
栈的几个经典应用,真的绝了
|
4天前
|
存储 网络协议 Linux
用户态协议栈06-TCP三次握手
用户态协议栈06-TCP三次握手
|
3天前
|
算法
【数据结构与算法】优先级队列
【数据结构与算法】优先级队列
6 0
|
3天前
|
存储 算法
【数据结构与算法】队列(顺序存储)
【数据结构与算法】队列(顺序存储)
5 0
|
4天前
|
存储
全局变量和局部变量在堆和栈的区别
全局变量和局部变量在堆和栈的区别
11 0
|
5天前
|
存储 人工智能 运维
高质量存储力发展问题之浪潮信息发布的大模型智算软件栈的定义如何解决
高质量存储力发展问题之浪潮信息发布的大模型智算软件栈的定义如何解决
8 0
|
5天前
|
设计模式 算法 C语言
【CPP】栈、双端队列、队列、优先级队列与反向迭代器
【CPP】栈、双端队列、队列、优先级队列与反向迭代器
【数据结构】栈和队列
【数据结构】栈和队列
|
6天前
|
C语言
用栈实现将一个十进制数值转换成八进制数值。即用该十进制数值除以8,并保留其余数;重复此操作,直到该十进制数值为0为止。最后将所有的余数反向输出就是所对应的八进制数值
这篇文章展示了如何使用栈(包括顺序栈和链栈)实现将十进制数值转换成八进制数值的方法,通过C语言编程演示了两种栈的实现方式和使用场景。
用栈实现将一个十进制数值转换成八进制数值。即用该十进制数值除以8,并保留其余数;重复此操作,直到该十进制数值为0为止。最后将所有的余数反向输出就是所对应的八进制数值