栈和队列应用指南_第1页
栈和队列应用指南_第2页
栈和队列应用指南_第3页
栈和队列应用指南_第4页
栈和队列应用指南_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

栈和队列应用指南一、栈和队列概述

栈和队列是两种重要的数据结构,广泛应用于计算机科学和软件工程中。它们是线性数据结构,具有特定的操作限制,这些限制也带来了它们独特的高效应用场景。

(一)栈的定义和特性

1.定义:栈是一种后进先出(LIFO,LastInFirstOut)的数据结构。

2.特性:

-只有一个操作端,称为栈顶(Top)。

-所有插入和删除操作都在栈顶进行。

-允许的操作包括:压栈(Push,添加元素到栈顶)、弹栈(Pop,移除栈顶元素)。

(二)队列的定义和特性

1.定义:队列是一种先进先出(FIFO,FirstInFirstOut)的数据结构。

2.特性:

-有两个操作端,一个称为队头(Front),另一个称为队尾(Rear)。

-插入操作在队尾进行(称为入队,Enqueue),删除操作在队头进行(称为出队,Dequeue)。

二、栈的应用

栈由于其后进先出的特性,在多种场景下非常有用。

(一)表达式求值

1.中缀表达式转后缀表达式:

-步骤:

1.初始化一个空栈和一个空输出队列。

2.从左到右扫描中缀表达式。

3.遇到操作数时,直接加入输出队列。

4.遇到运算符时,根据优先级压栈或直接入队。

5.处理括号:左括号压栈,右括号弹栈直到遇到左括号。

6.扫描结束后,将栈中剩余元素弹栈并加入输出队列。

2.后缀表达式求值:

-步骤:

1.初始化一个空栈。

2.从左到右扫描后缀表达式。

3.遇到操作数时,压栈。

4.遇到运算符时,弹栈两次获取操作数,执行运算,将结果压栈。

5.扫描结束后,栈顶元素即为结果。

(二)括号匹配

1.方法:使用栈来匹配括号。

2.步骤:

1.初始化一个空栈。

2.从左到右扫描表达式。

3.遇到左括号时,压栈。

4.遇到右括号时,弹栈,检查栈是否为空。

5.如果栈为空或弹栈后栈为空,则括号不匹配。

6.扫描结束后,如果栈为空,则括号匹配。

(三)函数调用栈

1.定义:每次函数调用时,会在栈上创建一个函数帧,存储局部变量和返回地址。

2.作用:

-管理函数调用和返回。

-支持递归调用。

三、队列的应用

队列的先进先出特性使其在多个场景下非常有用。

(一)任务调度

1.方法:使用队列来管理任务。

2.步骤:

1.将所有任务入队。

2.按顺序出队并执行任务。

3.可以实现公平调度,确保任务按提交顺序执行。

(二)缓冲管理

1.方法:使用队列作为缓冲区。

2.应用:

-在生产者-消费者问题中,生产者将产品入队,消费者从队头出队。

-在网络数据包处理中,数据包按到达顺序入队,处理单元按顺序处理。

(三)广度优先搜索(BFS)

1.方法:使用队列实现广度优先搜索。

2.步骤:

1.初始化一个空队列。

2.将起始节点入队。

3.当队列不为空时,出队一个节点,访问该节点。

4.将该节点的所有未访问邻居入队。

5.重复步骤3和4,直到队列为空。

四、栈和队列的比较

(一)操作限制

1.栈:只能在栈顶进行插入和删除操作。

2.队列:可以在队尾插入,在队头删除。

(二)应用场景

1.栈:适用于需要后进先出操作的场景,如表达式求值、括号匹配、函数调用。

2.队列:适用于需要先进先出操作的场景,如任务调度、缓冲管理、广度优先搜索。

(三)实现方式

1.栈:可以使用数组或链表实现。

2.队列:同样可以使用数组或链表实现,但需要处理队头和队尾的操作。

五、总结

栈和队列是两种基础且重要的数据结构,它们通过特定的操作限制提供了高效的应用场景。栈的后进先出特性使其适用于表达式求值、括号匹配和函数调用等场景,而队列的先进先出特性使其适用于任务调度、缓冲管理和广度优先搜索等场景。在实际应用中,选择合适的栈或队列实现方式可以进一步优化性能。

二、栈的应用(续)

(一)表达式求值(续)

1.中缀表达式转后缀表达式(续):

-详细步骤:

1.初始化:创建一个空栈用于存储运算符,创建一个空队列用于存储输出结果。

-示例:输入中缀表达式`(A+B)C-D`,初始化栈`OpStack=[]`,输出队列`OutputQueue=[]`。

2.扫描表达式:从左到右逐个字符扫描中缀表达式。

-示例:第一个字符是`(`,是左括号,直接压栈。

-`OpStack=['(']`

-`OutputQueue=[]`

3.处理字符:

-操作数(如A,B,C):直接加入输出队列。

-扫描到`A`,加入`OutputQueue`。

-`OpStack=['(']`

-`OutputQueue=['A']`

-运算符(如+,-,,/):

-当栈为空或栈顶为左括号`(`时,直接压栈。

-当当前运算符优先级高于栈顶运算符时,压栈。

-当当前运算符优先级低于或等于栈顶运算符时,将栈顶运算符弹栈并加入输出队列,然后继续比较当前运算符与栈顶运算符的优先级。

-示例:扫描到`+`,栈顶为`(`,直接压栈。

-`OpStack=['(','+']`

-`OutputQueue=['A']`

-左括号`(`:直接压栈。

-示例:扫描到`B`,加入`OutputQueue`。

-`OpStack=['(','+']`

-`OutputQueue=['A','B']`

-右括号`)`:

-弹栈并将弹出的运算符加入输出队列,直到栈顶为左括号`(`,然后将左括号弹栈。

-示例:扫描到`)`,弹出`+`加入`OutputQueue`,然后弹出`(`。

-`OpStack=[]`

-`OutputQueue=['A','B','+']`

3.继续扫描:继续扫描剩余字符。

-示例:扫描到``,栈为空,直接压栈。

-`OpStack=['']`

-`OutputQueue=['A','B','+']`

-扫描到`C`,加入`OutputQueue`。

-`OpStack=['']`

-`OutputQueue=['A','B','+','C']`

-扫描到`-`,当前运算符优先级低于栈顶``,将``弹栈加入`OutputQueue`,然后压栈`-`。

-`OpStack=['','-']`

-`OutputQueue=['A','B','+','C','']`

4.扫描结束:当扫描完所有字符后,将栈中剩余运算符依次弹栈并加入输出队列。

-示例:弹出`-`加入`OutputQueue`。

-`OpStack=[]`

-`OutputQueue=['A','B','+','C','','-']`

5.结果:输出队列`OutputQueue`即为后缀表达式。

-最终后缀表达式:`AB+C-`

-运算符优先级:

-``和`/`优先级最高。

-`+`和`-`优先级次之。

-同优先级的运算符从左到右计算。

2.后缀表达式求值(续):

-详细步骤:

1.初始化:创建一个空栈用于存储操作数。

-示例:输入后缀表达式`AB+C-`,初始化栈`OperandStack=[]`。

2.扫描表达式:从左到右逐个字符扫描后缀表达式。

-示例:第一个字符是`A`,是操作数,压栈。

-`OperandStack=['A']`

3.处理字符:

-操作数(如A,B,C):压栈。

-扫描到`B`,压栈。

-`OperandStack=['A','B']`

-运算符(如+,-,,/):

-弹栈两次,获取两个操作数`op2`和`op1`。

-执行`op1op2`运算(根据运算符类型)。

-将运算结果压栈。

-示例:扫描到`+`,弹栈两次获取`B`和`A`,计算`A+B`,假设`A=1`,`B=2`,结果为`3`,压栈。

-`OperandStack=[3]`

-继续扫描:继续扫描剩余字符。

-示例:扫描到`C`,压栈。

-`OperandStack=[3,'C']`

-示例:扫描到``,弹栈两次获取`C`和`3`,计算`3C`,假设`C=4`,结果为`12`,压栈。

-`OperandStack=[12]`

-示例:扫描到`-`,弹栈两次获取`12`和`3`,计算`3-12`,结果为`-9`,压栈。

-`OperandStack=[-9]`

4.扫描结束:当扫描完所有字符后,栈顶元素即为结果。

-最终结果:`-9`

-注意:在求值过程中,需要确保栈的操作正确,特别是弹栈的顺序和次数。

(二)括号匹配(续)

1.详细方法:使用栈来匹配括号,确保每个左括号`(`都有一个对应的右括号`)`,并且括号的嵌套顺序正确。

2.详细步骤:

1.初始化:创建一个空栈用于存储遇到的左括号。

-示例:输入表达式`((A+B)C-(D/E))`,初始化栈`BracketStack=[]`。

2.扫描表达式:从左到右逐个字符扫描表达式。

-示例:第一个字符是`(`,是左括号,压栈。

-`BracketStack=['(']`

-示例:第二个字符是`(`,是左括号,压栈。

-`BracketStack=['(','(']`

-示例:扫描到`A`,是操作符,不操作。

-`BracketStack=['(','(']`

-示例:扫描到`+`,是操作符,不操作。

-`BracketStack=['(','(']`

-示例:扫描到`)`,是右括号,弹栈。

-`BracketStack=['(']`

-示例:继续扫描到``,是操作符,不操作。

-`BracketStack=['(']`

-示例:扫描到`C`,是操作符,不操作。

-`BracketStack=['(']`

-示例:扫描到`-`,是操作符,不操作。

-`BracketStack=['(']`

-示例:扫描到`(`,是左括号,压栈。

-`BracketStack=['(','(']`

-示例:扫描到`D`,是操作符,不操作。

-`BracketStack=['(','(']`

-示例:扫描到`/`,是操作符,不操作。

-`BracketStack=['(','(']`

-示例:扫描到`)`,是右括号,弹栈。

-`BracketStack=['(']`

-示例:扫描到`)`,是右括号,弹栈。

-`BracketStack=[]`

3.检查栈:

-如果扫描结束后栈为空,则括号匹配成功。

-如果栈不为空,则括号匹配失败。

-示例:扫描结束后`BracketStack=[]`,匹配成功。

4.结果:根据栈的状态判断括号是否匹配。

-最终结果:括号匹配成功。

3.应用场景:

-编译器中的语法分析。

-表达式解析。

-网络协议中的括号匹配。

(三)函数调用栈(续)

1.详细定义:每次函数调用时,系统会在栈上创建一个函数帧(或称为栈帧、活动记录),用于存储该函数的局部变量、参数、返回地址等信息。

2.详细作用:

-管理函数调用和返回:

-当函数被调用时,当前函数的执行状态(包括指令指针、寄存器值等)被保存,然后创建一个新的函数帧压栈,新函数帧成为当前栈帧。

-当函数执行完毕时,当前函数帧被弹栈,之前保存的执行状态被恢复,继续执行被调用的函数。

-支持递归调用:

-即使是递归调用,每次递归都会创建一个新的函数帧,并正确保存和恢复之前的函数帧,确保递归的正确性。

-示例:递归计算阶乘`factorial(n)`。

-`factorial(3)`调用`factorial(2)`,然后`factorial(2)`调用`factorial(1)`,然后`factorial(1)`调用`factorial(0)`,最后逐层返回。

-每次调用都会创建新的函数帧,存储当前函数的参数和返回地址。

3.示例:

-函数`A`调用函数`B`,调用过程:

1.保存`A`的执行状态。

2.创建`B`的函数帧,压栈。

3.执行`B`。

4.`B`执行完毕,弹栈,恢复`A`的执行状态,继续执行`A`。

-如果`B`再调用`C`,则`C`的函数帧会压在`B`的函数帧之上。

4.应用场景:

-大多数编程语言中的函数调用。

-递归函数的实现。

-异常处理和中断处理。

三、队列的应用(续)

(一)任务调度(续)

1.详细方法:使用队列来管理任务,确保任务按到达顺序执行,实现公平调度。

2.详细步骤:

1.初始化:创建一个空队列用于存储任务。

-示例:有任务`Task1`、`Task2`、`Task3`,初始化队列`TaskQueue=[]`。

2.任务入队:将所有任务按到达顺序入队。

-示例:`Task1`到达,入队。

-`TaskQueue=[Task1]`

-示例:`Task2`到达,入队。

-`TaskQueue=[Task1,Task2]`

-示例:`Task3`到达,入队。

-`TaskQueue=[Task1,Task2,Task3]`

3.任务出队:按顺序出队并执行任务。

-示例:出队`Task1`并执行。

-`TaskQueue=[Task2,Task3]`

-示例:出队`Task2`并执行。

-`TaskQueue=[Task3]`

-示例:出队`Task3`并执行。

-`TaskQueue=[]`

4.结果:所有任务按到达顺序执行。

-最终执行顺序:`Task1`->`Task2`->`Task3`

3.应用场景:

-操作系统中的任务调度。

-网络服务器中的请求处理。

-生产者-消费者问题中的任务管理。

4.优缺点:

-优点:

-公平性:确保任务按到达顺序执行。

-简单性:实现简单,易于理解。

-缺点:

-长时间等待:如果前面有长时间执行的任务,后面的任务需要等待较长时间。

-非抢占式:无法抢占正在执行的任务。

(二)缓冲管理(续)

1.详细方法:使用队列作为缓冲区,管理生产者和消费者之间的任务或数据交换。

2.生产者-消费者问题:

-定义:多个生产者生成数据,多个消费者消费数据,使用队列作为缓冲区。

-详细步骤:

1.初始化:创建一个队列用于存储数据。

-示例:初始化队列`BufferQueue=[]`。

2.生产者:

-生产数据。

-检查队列是否已满。

-如果队列未满,将数据入队。

-如果队列已满,生产者阻塞(等待或放弃)。

3.消费者:

-检查队列是否为空。

-如果队列不为空,将数据出队并消费。

-如果队列为空,消费者阻塞(等待或放弃)。

-示例:

-生产者`P1`生产数据`Data1`,队列`BufferQueue=[Data1]`。

-消费者`C1`消费`Data1`,队列`BufferQueue=[]`。

-生产者`P2`生产数据`Data2`,队列`BufferQueue=[Data2]`。

-消费者`C2`消费`Data2`,队列`BufferQueue=[]`。

3.应用场景:

-操作系统中的进程同步。

-多线程编程中的线程同步。

-网络数据包处理。

4.优缺点:

-优点:

-解耦:生产者和消费者相互独立。

-并发:允许多个生产者和消费者并发工作。

-缺点:

-阻塞:生产者或消费者可能需要等待。

-内存管理:需要管理队列的内存。

(三)广度优先搜索(BFS)(续)

1.详细方法:使用队列实现广度优先搜索,按层次遍历图或树。

2.详细步骤:

1.初始化:

-创建一个空队列用于存储待访问节点。

-创建一个集合或数组用于存储已访问节点。

-选择一个起始节点,将其加入队列和已访问集合。

-示例:起始节点为`A`,`Visited={A}`,`Queue=[A]`。

2.遍历:

-当队列不为空时,执行以下操作:

1.从队列中出队一个节点,记为当前节点。

2.访问当前节点(可以记录或处理)。

3.获取当前节点的所有未访问邻居节点。

4.将这些邻居节点加入队列和已访问集合。

-示例:

-出队`A`,访问`A`。

-`A`的邻居节点为`B`和`C`,`Visited={A,B,C}`,`Queue=[B,C]`。

-出队`B`,访问`B`。

-`B`的邻居节点为`D`,`D`已访问,忽略。

-`C`的邻居节点为`E`和`F`,`Visited={A,B,C,E,F}`,`Queue=[E,F]`。

-出队`E`,访问`E`。

-`E`的邻居节点为空,忽略。

-出队`F`,访问`F`。

-`F`的邻居节点为`G`,`G`已访问,忽略。

-队列为空,遍历结束。

3.应用场景:

-图的最短路径问题(无权图)。

-搜索算法(如搜索引擎)。

-无向图的连通性判断。

4.优缺点:

-优点:

-找到最短路径(无权图)。

-实现简单。

-缺点:

-时间复杂度较高:可能需要遍历所有节点。

-空间复杂度较高:需要存储所有节点和边的信息。

四、栈和队列的比较(续)

(一)操作限制(续)

1.栈:

-操作:只能在一端(栈顶)进行插入和删除操作。

-特性:后进先出(LIFO)。

-实现:可以使用数组或链表实现。

-示例:

-压栈(Push):在栈顶添加元素。

-弹栈(Pop):移除栈顶元素。

2.队列:

-操作:在一端(队尾)插入,另一端(队头)删除。

-特性:先进先出(FIFO)。

-实现:可以使用数组或链表实现,但需要处理队头和队尾的操作。

-示例:

-入队(Enqueue):在队尾添加元素。

-出队(Dequeue):移除队头元素。

(二)应用场景(续)

1.栈:

-表达式求值:中缀转后缀、后缀求值。

-括号匹配:检查表达式中括号的匹配情况。

-函数调用栈:管理函数调用和返回。

-文本编辑:撤销和重做操作。

-递归:支持递归函数的实现。

2.队列:

-任务调度:按顺序执行任务。

-缓冲管理:生产者-消费者问题。

-广度优先搜索:按层次遍历图或树。

-消息队列:异步消息处理。

-打印队列:管理打印任务。

(三)实现方式(续)

1.栈的实现:

-数组实现:

-使用一个数组和一个整数(栈顶指针)来表示栈。

-压栈:将元素添加到数组末尾,栈顶指针加一。

-弹栈:移除数组末尾元素,栈顶指针减一。

-优点:实现简单,访问速度快。

-缺点:可能需要动态扩展数组大小。

-链表实现:

-使用一个链表来表示栈,链表的头部作为栈顶。

-压栈:在链表头部添加新节点。

-弹栈:移除链表头部节点。

-优点:动态扩展,无需预分配大小。

-缺点:需要额外的内存开销(指针)。

2.队列的实现:

-数组实现:

-使用一个数组和一个整数(队头指针)来表示队列。

-入队:将元素添加到数组末尾,队尾指针加一。

-出队:移除数组头部元素,队头指针加一。

-优点:实现简单,访问速度快。

-缺点:需要处理数组循环(CircularBuffer)。

-链表实现:

-使用一个链表来表示队列,链表的头部作为队头,尾部作为队尾。

-入队:在链表尾部添加新节点。

-出队:移除链表头部节点。

-优点:动态扩展,无需预分配大小。

-缺点:需要额外的内存开销(指针)。

-循环队列:

-使用数组实现队列,通过循环使用数组空间来提高空间利用率。

-优点:减少空间浪费。

-缺点:实现稍复杂。

五、总结(续)

栈和队列是两种基础且重要的数据结构,它们通过特定的操作限制提供了高效的应用场景。

-栈:后进先出(LIFO)特性使其适用于表达式求值、括号匹配、函数调用等场景。栈可以通过数组或链表实现,具有不同的优缺点。

-队列:先进先出(FIFO)特性使其适用于任务调度、缓冲管理、广度优先搜索等场景。队列同样可以通过数组(循环队列)或链表实现,具有不同的优缺点。

在实际应用中,选择合适的栈或队列实现方式可以进一步优化性能。理解栈和队列的工作原理和应用场景,有助于更好地设计和实现各种算法和系统。

-栈的应用示例:

-表达式求值:将中缀表达式转换为后缀表达式,然后求值。

-括号匹配:检查表达式中括号的匹配情况。

-函数调用栈:管理函数调用和返回。

-队列的应用示例:

-任务调度:按顺序执行任务。

-缓冲管理:生产者-消费者问题。

-广度优先搜索:按层次遍历图或树。

通过合理使用栈和队列,可以简化问题的解决过程,提高程序的效率和可读性。

一、栈和队列概述

栈和队列是两种重要的数据结构,广泛应用于计算机科学和软件工程中。它们是线性数据结构,具有特定的操作限制,这些限制也带来了它们独特的高效应用场景。

(一)栈的定义和特性

1.定义:栈是一种后进先出(LIFO,LastInFirstOut)的数据结构。

2.特性:

-只有一个操作端,称为栈顶(Top)。

-所有插入和删除操作都在栈顶进行。

-允许的操作包括:压栈(Push,添加元素到栈顶)、弹栈(Pop,移除栈顶元素)。

(二)队列的定义和特性

1.定义:队列是一种先进先出(FIFO,FirstInFirstOut)的数据结构。

2.特性:

-有两个操作端,一个称为队头(Front),另一个称为队尾(Rear)。

-插入操作在队尾进行(称为入队,Enqueue),删除操作在队头进行(称为出队,Dequeue)。

二、栈的应用

栈由于其后进先出的特性,在多种场景下非常有用。

(一)表达式求值

1.中缀表达式转后缀表达式:

-步骤:

1.初始化一个空栈和一个空输出队列。

2.从左到右扫描中缀表达式。

3.遇到操作数时,直接加入输出队列。

4.遇到运算符时,根据优先级压栈或直接入队。

5.处理括号:左括号压栈,右括号弹栈直到遇到左括号。

6.扫描结束后,将栈中剩余元素弹栈并加入输出队列。

2.后缀表达式求值:

-步骤:

1.初始化一个空栈。

2.从左到右扫描后缀表达式。

3.遇到操作数时,压栈。

4.遇到运算符时,弹栈两次获取操作数,执行运算,将结果压栈。

5.扫描结束后,栈顶元素即为结果。

(二)括号匹配

1.方法:使用栈来匹配括号。

2.步骤:

1.初始化一个空栈。

2.从左到右扫描表达式。

3.遇到左括号时,压栈。

4.遇到右括号时,弹栈,检查栈是否为空。

5.如果栈为空或弹栈后栈为空,则括号不匹配。

6.扫描结束后,如果栈为空,则括号匹配。

(三)函数调用栈

1.定义:每次函数调用时,会在栈上创建一个函数帧,存储局部变量和返回地址。

2.作用:

-管理函数调用和返回。

-支持递归调用。

三、队列的应用

队列的先进先出特性使其在多个场景下非常有用。

(一)任务调度

1.方法:使用队列来管理任务。

2.步骤:

1.将所有任务入队。

2.按顺序出队并执行任务。

3.可以实现公平调度,确保任务按提交顺序执行。

(二)缓冲管理

1.方法:使用队列作为缓冲区。

2.应用:

-在生产者-消费者问题中,生产者将产品入队,消费者从队头出队。

-在网络数据包处理中,数据包按到达顺序入队,处理单元按顺序处理。

(三)广度优先搜索(BFS)

1.方法:使用队列实现广度优先搜索。

2.步骤:

1.初始化一个空队列。

2.将起始节点入队。

3.当队列不为空时,出队一个节点,访问该节点。

4.将该节点的所有未访问邻居入队。

5.重复步骤3和4,直到队列为空。

四、栈和队列的比较

(一)操作限制

1.栈:只能在栈顶进行插入和删除操作。

2.队列:可以在队尾插入,在队头删除。

(二)应用场景

1.栈:适用于需要后进先出操作的场景,如表达式求值、括号匹配、函数调用。

2.队列:适用于需要先进先出操作的场景,如任务调度、缓冲管理、广度优先搜索。

(三)实现方式

1.栈:可以使用数组或链表实现。

2.队列:同样可以使用数组或链表实现,但需要处理队头和队尾的操作。

五、总结

栈和队列是两种基础且重要的数据结构,它们通过特定的操作限制提供了高效的应用场景。栈的后进先出特性使其适用于表达式求值、括号匹配和函数调用等场景,而队列的先进先出特性使其适用于任务调度、缓冲管理和广度优先搜索等场景。在实际应用中,选择合适的栈或队列实现方式可以进一步优化性能。

二、栈的应用(续)

(一)表达式求值(续)

1.中缀表达式转后缀表达式(续):

-详细步骤:

1.初始化:创建一个空栈用于存储运算符,创建一个空队列用于存储输出结果。

-示例:输入中缀表达式`(A+B)C-D`,初始化栈`OpStack=[]`,输出队列`OutputQueue=[]`。

2.扫描表达式:从左到右逐个字符扫描中缀表达式。

-示例:第一个字符是`(`,是左括号,直接压栈。

-`OpStack=['(']`

-`OutputQueue=[]`

3.处理字符:

-操作数(如A,B,C):直接加入输出队列。

-扫描到`A`,加入`OutputQueue`。

-`OpStack=['(']`

-`OutputQueue=['A']`

-运算符(如+,-,,/):

-当栈为空或栈顶为左括号`(`时,直接压栈。

-当当前运算符优先级高于栈顶运算符时,压栈。

-当当前运算符优先级低于或等于栈顶运算符时,将栈顶运算符弹栈并加入输出队列,然后继续比较当前运算符与栈顶运算符的优先级。

-示例:扫描到`+`,栈顶为`(`,直接压栈。

-`OpStack=['(','+']`

-`OutputQueue=['A']`

-左括号`(`:直接压栈。

-示例:扫描到`B`,加入`OutputQueue`。

-`OpStack=['(','+']`

-`OutputQueue=['A','B']`

-右括号`)`:

-弹栈并将弹出的运算符加入输出队列,直到栈顶为左括号`(`,然后将左括号弹栈。

-示例:扫描到`)`,弹出`+`加入`OutputQueue`,然后弹出`(`。

-`OpStack=[]`

-`OutputQueue=['A','B','+']`

3.继续扫描:继续扫描剩余字符。

-示例:扫描到``,栈为空,直接压栈。

-`OpStack=['']`

-`OutputQueue=['A','B','+']`

-扫描到`C`,加入`OutputQueue`。

-`OpStack=['']`

-`OutputQueue=['A','B','+','C']`

-扫描到`-`,当前运算符优先级低于栈顶``,将``弹栈加入`OutputQueue`,然后压栈`-`。

-`OpStack=['','-']`

-`OutputQueue=['A','B','+','C','']`

4.扫描结束:当扫描完所有字符后,将栈中剩余运算符依次弹栈并加入输出队列。

-示例:弹出`-`加入`OutputQueue`。

-`OpStack=[]`

-`OutputQueue=['A','B','+','C','','-']`

5.结果:输出队列`OutputQueue`即为后缀表达式。

-最终后缀表达式:`AB+C-`

-运算符优先级:

-``和`/`优先级最高。

-`+`和`-`优先级次之。

-同优先级的运算符从左到右计算。

2.后缀表达式求值(续):

-详细步骤:

1.初始化:创建一个空栈用于存储操作数。

-示例:输入后缀表达式`AB+C-`,初始化栈`OperandStack=[]`。

2.扫描表达式:从左到右逐个字符扫描后缀表达式。

-示例:第一个字符是`A`,是操作数,压栈。

-`OperandStack=['A']`

3.处理字符:

-操作数(如A,B,C):压栈。

-扫描到`B`,压栈。

-`OperandStack=['A','B']`

-运算符(如+,-,,/):

-弹栈两次,获取两个操作数`op2`和`op1`。

-执行`op1op2`运算(根据运算符类型)。

-将运算结果压栈。

-示例:扫描到`+`,弹栈两次获取`B`和`A`,计算`A+B`,假设`A=1`,`B=2`,结果为`3`,压栈。

-`OperandStack=[3]`

-继续扫描:继续扫描剩余字符。

-示例:扫描到`C`,压栈。

-`OperandStack=[3,'C']`

-示例:扫描到``,弹栈两次获取`C`和`3`,计算`3C`,假设`C=4`,结果为`12`,压栈。

-`OperandStack=[12]`

-示例:扫描到`-`,弹栈两次获取`12`和`3`,计算`3-12`,结果为`-9`,压栈。

-`OperandStack=[-9]`

4.扫描结束:当扫描完所有字符后,栈顶元素即为结果。

-最终结果:`-9`

-注意:在求值过程中,需要确保栈的操作正确,特别是弹栈的顺序和次数。

(二)括号匹配(续)

1.详细方法:使用栈来匹配括号,确保每个左括号`(`都有一个对应的右括号`)`,并且括号的嵌套顺序正确。

2.详细步骤:

1.初始化:创建一个空栈用于存储遇到的左括号。

-示例:输入表达式`((A+B)C-(D/E))`,初始化栈`BracketStack=[]`。

2.扫描表达式:从左到右逐个字符扫描表达式。

-示例:第一个字符是`(`,是左括号,压栈。

-`BracketStack=['(']`

-示例:第二个字符是`(`,是左括号,压栈。

-`BracketStack=['(','(']`

-示例:扫描到`A`,是操作符,不操作。

-`BracketStack=['(','(']`

-示例:扫描到`+`,是操作符,不操作。

-`BracketStack=['(','(']`

-示例:扫描到`)`,是右括号,弹栈。

-`BracketStack=['(']`

-示例:继续扫描到``,是操作符,不操作。

-`BracketStack=['(']`

-示例:扫描到`C`,是操作符,不操作。

-`BracketStack=['(']`

-示例:扫描到`-`,是操作符,不操作。

-`BracketStack=['(']`

-示例:扫描到`(`,是左括号,压栈。

-`BracketStack=['(','(']`

-示例:扫描到`D`,是操作符,不操作。

-`BracketStack=['(','(']`

-示例:扫描到`/`,是操作符,不操作。

-`BracketStack=['(','(']`

-示例:扫描到`)`,是右括号,弹栈。

-`BracketStack=['(']`

-示例:扫描到`)`,是右括号,弹栈。

-`BracketStack=[]`

3.检查栈:

-如果扫描结束后栈为空,则括号匹配成功。

-如果栈不为空,则括号匹配失败。

-示例:扫描结束后`BracketStack=[]`,匹配成功。

4.结果:根据栈的状态判断括号是否匹配。

-最终结果:括号匹配成功。

3.应用场景:

-编译器中的语法分析。

-表达式解析。

-网络协议中的括号匹配。

(三)函数调用栈(续)

1.详细定义:每次函数调用时,系统会在栈上创建一个函数帧(或称为栈帧、活动记录),用于存储该函数的局部变量、参数、返回地址等信息。

2.详细作用:

-管理函数调用和返回:

-当函数被调用时,当前函数的执行状态(包括指令指针、寄存器值等)被保存,然后创建一个新的函数帧压栈,新函数帧成为当前栈帧。

-当函数执行完毕时,当前函数帧被弹栈,之前保存的执行状态被恢复,继续执行被调用的函数。

-支持递归调用:

-即使是递归调用,每次递归都会创建一个新的函数帧,并正确保存和恢复之前的函数帧,确保递归的正确性。

-示例:递归计算阶乘`factorial(n)`。

-`factorial(3)`调用`factorial(2)`,然后`factorial(2)`调用`factorial(1)`,然后`factorial(1)`调用`factorial(0)`,最后逐层返回。

-每次调用都会创建新的函数帧,存储当前函数的参数和返回地址。

3.示例:

-函数`A`调用函数`B`,调用过程:

1.保存`A`的执行状态。

2.创建`B`的函数帧,压栈。

3.执行`B`。

4.`B`执行完毕,弹栈,恢复`A`的执行状态,继续执行`A`。

-如果`B`再调用`C`,则`C`的函数帧会压在`B`的函数帧之上。

4.应用场景:

-大多数编程语言中的函数调用。

-递归函数的实现。

-异常处理和中断处理。

三、队列的应用(续)

(一)任务调度(续)

1.详细方法:使用队列来管理任务,确保任务按到达顺序执行,实现公平调度。

2.详细步骤:

1.初始化:创建一个空队列用于存储任务。

-示例:有任务`Task1`、`Task2`、`Task3`,初始化队列`TaskQueue=[]`。

2.任务入队:将所有任务按到达顺序入队。

-示例:`Task1`到达,入队。

-`TaskQueue=[Task1]`

-示例:`Task2`到达,入队。

-`TaskQueue=[Task1,Task2]`

-示例:`Task3`到达,入队。

-`TaskQueue=[Task1,Task2,Task3]`

3.任务出队:按顺序出队并执行任务。

-示例:出队`Task1`并执行。

-`TaskQueue=[Task2,Task3]`

-示例:出队`Task2`并执行。

-`TaskQueue=[Task3]`

-示例:出队`Task3`并执行。

-`TaskQueue=[]`

4.结果:所有任务按到达顺序执行。

-最终执行顺序:`Task1`->`Task2`->`Task3`

3.应用场景:

-操作系统中的任务调度。

-网络服务器中的请求处理。

-生产者-消费者问题中的任务管理。

4.优缺点:

-优点:

-公平性:确保任务按到达顺序执行。

-简单性:实现简单,易于理解。

-缺点:

-长时间等待:如果前面有长时间执行的任务,后面的任务需要等待较长时间。

-非抢占式:无法抢占正在执行的任务。

(二)缓冲管理(续)

1.详细方法:使用队列作为缓冲区,管理生产者和消费者之间的任务或数据交换。

2.生产者-消费者问题:

-定义:多个生产者生成数据,多个消费者消费数据,使用队列作为缓冲区。

-详细步骤:

1.初始化:创建一个队列用于存储数据。

-示例:初始化队列`BufferQueue=[]`。

2.生产者:

-生产数据。

-检查队列是否已满。

-如果队列未满,将数据入队。

-如果队列已满,生产者阻塞(等待或放弃)。

3.消费者:

-检查队列是否为空。

-如果队列不为空,将数据出队并消费。

-如果队列为空,消费者阻塞(等待或放弃)。

-示例:

-生产者`P1`生产数据`Data1`,队列`BufferQueue=[Data1]`。

-消费者`C1`消费`Data1`,队列`BufferQueue=[]`。

-生产者`P2`生产数据`Data2`,队列`BufferQueue=[Data2]`。

-消费者`C2`消费`Data2`,队列`BufferQueue=[]`。

3.应用场景:

-操作系统中的进程同步。

-多线程编程中的线程同步。

-网络数据包处理。

4.优缺点:

-优点:

-解耦:生产者和消费者相互独立。

-并发:允许多个生产者和消费者并发工作。

-缺点:

-阻塞:生产者或消费者可能需要等待。

-内存管理:需要管理队列的内存。

(三)广度优先搜索(BFS)(续)

1.详细方法:使用队列实现广度优先搜索,按层次遍历图或树。

2.详细步骤:

1.初始化:

-创建一个空队列用于存储待访问节点。

-创建一个集合或数组用于存储已访问节点。

-选择一个起始节点,将其加入队列和已访问集合。

-示例:起始节点为`A`,`Visited={A}`,`Queue=[A]`。

2.遍历:

-当队列不为空时,执行以下操作:

1.从队列中出队一个节点,记为当前节点。

2.访问当前节点(可以记录或处理)。

3.获取当前节点的所有未访问邻居节点。

4.将这些邻居节点加入队列和已访问集合。

-示例:

-出队`A`,访问`A`。

-`A`的邻居节点为`B`和`C`,`Visited={A,B,C}`,`Queue=[B,C]`。

-出队`B`,访问`B`。

-

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论