实用数据结构基础 第3章 栈.

Slides:



Advertisements
Similar presentations
第五节 函数的微分 一、微分的定义 二、微分的几何意义 三、基本初等函数的微分公式与微分运算 法则 四、微分形式不变性 五、微分在近似计算中的应用 六、小结.
Advertisements

2.8 函数的微分 1 微分的定义 2 微分的几何意义 3 微分公式与微分运算法则 4 微分在近似计算中的应用.
数据结构的引入. 通讯录管理 社团机构管理 校园导航管理 通讯录管理 社团机构管理 校园导航管理.
第五章 二次型. 第五章 二次型 知识点1---二次型及其矩阵表示 二次型的基本概念 1. 线性变换与合同矩阵 2.
问题的提出: 例1 把十进制整数转换为二至十六之间的任一进制数输出。
C++中的声音处理 在传统Turbo C环境中,如果想用C语言控制电脑发声,可以用Sound函数。在VC6.6环境中如果想控制电脑发声则采用Beep函数。原型为: Beep(频率,持续时间) , 单位毫秒 暂停程序执行使用Sleep函数 Sleep(持续时间), 单位毫秒 引用这两个函数时,必须包含头文件
第三章 堆疊與佇列的基本應用 3-1 簡介堆疊(Stack) 3-2 迷宮問題研究 3-3 佇列(queue)的介紹
第3章 栈和队列 3.1 栈 3.2 队列 3.3 应用.
第3章 栈和队列.
第三章 栈和队列 Stack and Queue
第三章栈和队列 栈 队列 递归.
强连通分量 无向图 1、任意两顶点连通称该图为连通图 2、否则将其中的极大连通子图称为连通分量 A D C B E 有向图
Ch.3 栈和队列 黄刘生 中国科学技术大学计算机系 国家高性能计算中心(合肥)
线性表小结 元素之间的线性关系 顺序表 顺序表:元素相邻存储 单链表:后继指针链接 一维数组 给定下标随机存取
本 章 说 明 3.1 栈 3.2 栈的应用举例 3.3 栈与递归的实现 3.4 队 列 本 章 小 结 返回主目录.
第三章 栈和队列 栈和队列是两种重要的数据结构。
走进编程 程序的顺序结构(二).
第3章 栈和队列 丽水学院工学院.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
第3章 堆栈和队列 堆栈 堆栈应用 队列 队列应用 优先级队列 主要知识点.
第3章 栈和队列(一).
第三章 栈和队列.
第五讲 四则运算计算器(一) 精品教程《C#程序设计与应用(第2版)清华大学出版社 谭恒松 主编
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
教 师:曾晓东 电 话: E_mail: 计算机软件技术基础 教 师:曾晓东 电 话: E_mail:
第3章 栈和队列 3.1 栈 本章主题:栈和队列的应用 教学目的:掌握栈和队列的应用方法,理解栈的重要作用
数 据 结 构 刘家芬 Sept 2012.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
第二章 Java语言基础.
动态规划(Dynamic Programming)
第三章 栈和队列.
資料結構 第4章 堆疊.
第七章 操作符重载 胡昊 南京大学计算机系软件所.
顺序表的插入.
数据结构概论 第3章 栈和队列 董黎刚 浙江工商大学信电学院 2019年2月25日.
第四章 栈和队列 4.1栈 4.2栈的应用 4.3队列 4.3队列应用.
计算机软件技术基础 数据结构与算法(3).
实验九 函数嵌套、函数参数 第27讲 C程序设计 Main() { int x,y; X=10; y=x*x+1;
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C语言程序设计 主讲教师:陆幼利.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
顺序表的删除.
线 性 代 数 厦门大学线性代数教学组 2019年4月24日6时8分 / 45.
实验四、TinyOS执行机制实验 一、实验目的 1、了解tinyos执行机制,实现程序异步处理的方法。
C语言程序设计 第一章 数据类型, 运算符与表达式 第二章 顺序程序设计 第三章 选择结构程序设计 第四章 循环控制 第五章 数组.
第 四 讲 线性表(二).
1、递归的概念 2、递归函数的设计 3、递归过程与递归工作栈 4、例 5、递归过程的非递归化
第4章 Excel电子表格制作软件 4.4 函数(一).
实验三 16位算术逻辑运算实验 不带进位控制的算术运算 置AR=1: 设置开关CN 1 不带进位 0 带进位运算;
第九节 赋值运算符和赋值表达式.
iSIGHT 基本培训 使用 Excel的栅栏问题
3.16 枚举算法及其程序实现 ——数组的作用.
第 六 讲 栈和队列(一).
本章的基本内容是: ⑴栈和队列的定义及操作特性; 第3章 特殊线性表—栈、队列和串 ⑵栈和队列的两种存储方法和基本运算的实现;
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C程序设计 实验二 数据类型、运算符和表达式 第6讲
实验目的:掌握数据的顺序存储结构及它们在计算机中的操作。 实验内容:
第七讲 栈和队列(二) 1/.
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第3章 栈和队列 3.1 栈 3.2 队列.
第三节 数量积 向量积 混合积 一、向量的数量积 二、向量的向量积 三、向量的混合积 四、小结 思考题.
第三章 栈和队列.
昆山爱达人信息技术有限公司 QQ: 本节内容 1.栈的链式存储结构及实现 2.栈的应用.
第二次课后作业答案 函数式编程和逻辑式编程
Presentation transcript:

实用数据结构基础 第3章 栈

第 3 章 栈 知 识 点 难 点 栈的定义和特点 栈的基本运算和算法 栈的典型应用 后缀表达式的算法 数制的换算 第 3 章 栈 知 识 点 栈的定义和特点 栈的基本运算和算法 栈的典型应用 难 点 后缀表达式的算法 数制的换算 利用本章的基本知识设计相关的应用问题

要 求 掌握栈的特点 掌握栈的基本运算 熟悉栈的各种实际应用 能设计栈的应用的典型算法 了解栈的运算时间复杂度分析

第3章 目录 3-1 栈的定义与运算 3-2 栈的存储和实现 3-3 栈的应用举例 小 结 实验3 栈子系统 习题3

3-1 栈的定义和运算 3-1-1 栈(Stack)的定义 1. 栈的定义 栈是限制在表尾进行插入和删除的线性表。 a3 a2 a1 进栈 3-1 栈的定义和运算 3-1-1 栈(Stack)的定义 1. 栈的定义     栈是限制在表尾进行插入和删除的线性表。 进栈 出栈 a1 a2 a3 图3-1栈的 示意图

2. 栈的特性 3. 应用实例 (1)栈的主要特点是“后进先出” (2)允许插入、删除的这一端称为栈顶(Top),另一端称为栈底(Bottom)。 3. 应用实例 (1)分币筒; (2)铁路调度站。

3-1-2 栈的运算 1.进栈: Push(&s,x) 初始条件:栈s已存在且非满。 操作结果:在栈顶插入一个元素x,栈中多了一个元素。 2.出栈:Pop(&s) 初始条件:栈s存在且非空。 操作结果:删除栈顶元素,栈中少了一个元素。 3.读栈顶元素:ReadTop(s,&e) 初始条件:栈s已存在且非空。 操作结果:输出栈顶元素,但栈中元素不变。

4. 判栈空:SEmpty(s) 初始条件:栈s已存在。 操作结果:若栈空则返回为0,否则返回为1。 5. 判栈满:SFull(s) 操作结果:若栈满则返回为0,否则返回为1。 6. 显示栈元素:ShowStack (s) 初始条件:栈s已存在 ,且非空。 操作结果:显示栈中所有元素。

3-2 栈的存储和实现 3-2-1 顺序栈 1. 顺序栈的实现 (1) 用一维数组实现顺序栈 设栈中的数据元素的类型是字符型,用一个足够长度的一维数组s来存放元素,数组的最大容量为MAXLEN,栈顶指针为top,则顺序栈可以用C(或C++)语言描述如下: #define MAXLEN 10 // 分配最大的栈空间 char s[MAXLEN]; // 数据类型为字符型 int top; // 定义栈顶指针

(2) 用结构体数组实现顺序栈 顺序栈的结构体描述: #define MAXLEN 10 // 分配最大的栈空间 typedef struct // 定义结构体 { datatype data[MAXLEN]; // datatype可根据用需要定义类型 int top; // 定义栈顶指针 } SeqStack; 再定义一个指向顺序栈的指针: SeqStack *S; // 定义S为结构体类型的指针变量

F E D C B A F E D C B A J I H G F E D C B A A (3)栈操作的示意图如图3-3所示。 top=9 J I H G F E D C B A A top=5 top=3 top=0 top=-1 (a) (b) (c) (d) (e)

当top = -1时,表示栈空,如图3-3(a); 当top=0时,表示栈中有一个元素,如图3-3(b)表示栈中已输入一个元素A; 入栈时,栈顶指针上移,指针top加1,如图3-3(c)是6个元素入栈后的状况; 出栈时,栈顶指针下移,指针top减1, 如图3-3(d)是在F、E相继出栈后的情况。此时栈中还有A、B、C、D 4个元素,top=3,指针已经指向了新的栈顶。但是出栈的元素F、E仍然在原先的存储单元,只是不在栈中了,因为栈是只能在栈顶进行操作的线性表。 当top=9时,即top=MAXLEN–1,表示栈满,如图3-3(e)。

2.顺序栈运算的基本算法 (1)置空栈 首先建立栈空间,然后初始化栈顶指针。 SeqStack *Snull() { SeqStack *s; s=new (SeqStack); // 在C语言中用s=malloc(sizeof(SeqStack)); s->top= –1; // 置栈空 return s; }

int Push (SeqStack *s, datatype x) (2) 进栈 进栈运算是在栈顶位置插入一个新元素x,其算法步骤为: (a)    判栈是否为满,若栈满,作溢出处理,并返回0; (b)    若栈未满,栈顶指针top加1; (c)    将新元素x送入栈顶,并返回1。 int Push (SeqStack *s, datatype x) { if (s->top= =MAXLEN–1) return 0; // 栈满不能入栈,且返回 0 else { s->top++; s->data[s->top]=x; // 栈不满,则压入元素x return 1;} // 进栈成功,返回1 }

(3)出栈 出栈运算是指取出栈顶元素,赋给某一个指定变量x,其算法步骤为: (a) 判栈是否为空,若栈空,作下溢处理,并返回0; (b) 若栈非空,将栈顶元素赋给变量x; (c)  指针top减1,并返回1。 int Pop(SeqStack *s, datatype *x) { if (SEmpty ( s ) ) return 0; // 若栈空不能出栈 ,且返回0 else { *x=s->data[s->top]; // 栈不空则栈顶元素存入*x s->top - -; return 1; } // 出栈成功,返回1 }

(4)读栈顶元素 (5)判栈空 datatype ReadTop(SeqStack *s) { if (SEmpty ( s ) ) return 0; // 若栈空,则返回0 else return (s->data[s->top] ); // 否则,读栈顶元素,但指针未移动 } (5)判栈空 int SEmpty(SeqStack *s) { if (s->top= = –1) return 1; // 若栈空,则返回1 else return 0; // 否则返回0

3-2-2 链栈 (6)判栈满 1.链栈的实现 int SFull(SeqStack *s) { if (s->top= =MAXLEN–1) return 1;// 若栈满,则返回1 else return 0; // 否则返回0 } 3-2-2 链栈 1.链栈的实现 用链式存储结构实现的栈称为链栈。因为链栈的结点结构与单链表的结构相同,通常就用单链表来实现,在此用LinkStack表示,即有: typedef struct stacknode // 栈的存储结构 { datatype data; // 定义数据类型 struct stacknode *next; // 定义一个结构体的链指针 } stacknode;,* Linkstack; Linkstack top; // top为栈顶指针

由于栈中的操作只能在栈顶进行的,所以用链表的头部做栈顶是最合适的。链栈结构如图3-4所示。 top 4 3 2 1 ^ 图3-4 链栈示意图

2.链栈基本操作: (1)入栈 { stacknode *p=new stacknode; // 申请一个新结点 void Push(linkstack *s,int x) { stacknode *p=new stacknode; // 申请一个新结点 p->data=x; // 数据入栈 p->next=s->top; // 修改栈顶指针 s->top=p; }

(2) 出栈 int Pop(linkstack *s) { int x; // 定义一个变量x,用以存放出栈的元素 stacknode *p=s->top; // 栈顶指针指向p x=p->data; // 栈顶元素送x s->top=p->next; // 修改栈顶指针 delete p; // 回收结点p return x; // 返回栈顶元素 }

(3) 显示 void ShowStack (linkstack *s) { stacknode *p=s->top; if (p= =NULL) // 若栈空,作“栈空”显示 printf ("栈为空"); else { printf ("栈元素为:"); while (p!=NULL) //栈非空,则显示栈中所有元素 { printf ("%6d",p->data); // 输出一个元素 p=p->next; } // 修改栈顶指针 printf ("\n"); }

3-3. 栈的应用举例 3-3-1 数制转换 数值进位制的换算是计算机实现计算和处理的基本问题。比如将十进制数N转换为j进制的数,其解决的方法很多,其中一个常用的算法是除j取余法。将十进制数每次除以j,所得的余数依次入栈,然后按“后进先出”的次序出栈便得到转换的结果。 其算法原理是: N =(N / j)* j + N % j 其中: / 为整除,%为求余

【例3-1】将十进制数138转换为二进制数 N N / 2 (整除) N % 2(求余) 138 69 0 69 34 1 34 17 进 0 出 17 8 栈 1 栈 8 4 次 0 次 4 2 序 0 序 2 1 0 1 0 1 ∴ (138)10 =(10001010)2

1.算法思想如下: (1) 若 N<>0,则将N % j 取得的余数压入栈s中 ,执行(2); 若N=0,将栈s的内容依次出栈,算法结束。 (2)  用N / j 代替 N (3) 当N>0,则重复步骤(1)、(2)。

void Conversion(int n) // 将十进制数n转换为二进制数 int x; s.top=NULL; do 2. 算法的实现: void Conversion(int n) // 将十进制数n转换为二进制数 { linkstack s; int x; s.top=NULL; do { x=n%2; // 除2取余 n=n/2; stacknode *p=new stacknode; // 申请一个新结点 p->next=s.top; // 修改栈顶指针 s.top=p; s.top->data=x; } // 入栈

while(n); printf("转换后的二进制数值为:"); while(s.top) // 余数出栈处理 { printf("%d",s.top->data); // 输出栈顶的余数 stacknode* p=s.top; // 修改栈顶指针 s.top=s.top->next; delete p; // 回收一个结点,C语言中用free p }

3-3-2 表达式求值 表达式是由运算对象、运算符、括号等组成的有意义的式子。 1.中缀表达式(Infix Notation) 一般我们所用表达式是将运算符号放在两运算对象的中间,比如:a+b,c/d等等,我们把这样的式子称为中缀表达式。 2.后缀表达式(Postfix Notation) 后缀表达式规定把运算符放在两个运算对象(操作数)的后面。在后缀表达式中,不存在运算符的优先级问题,也不存在任何括号,计算的顺序完全按照运算符出现的先后次次序进行。 3.中缀表达式转换为后缀表达式 其转换方法采用运算符优先算法。转换过程需要两个栈:一个运算符号栈和一个后缀表达式输出符号栈。

(1)读入操作数,直接送输出符号栈。 (2)读入运算符,压入运算符号栈。 (a) 若后进的运算符优先级高于先进的,则继续进栈; (b) 若后进的运算符优先级不高于先进的,则将运算符号栈内高于或等于后进运算符级别的运算符依次弹出后,自己进栈。 (3)括号处理: (a) 遇到开括号“(”,进运算符号栈; (b)遇到闭括号“)”,则把最靠近的开括号“(”,以及其后进栈的运算符依次弹出到输出符号栈(括号均不压入输出符号栈)。 (4)遇到结束符“#”,则把运算符号栈内的所有运算符号依次弹出,并压入输出符号栈。 (5)若输入为+、–单目运算符,改为0与运算对象在前,运算符在后。 例如:–A,转换为:0A–。

【例3-2】A / B ^ C + D * E–A*C A / B ^ /, ^ C + D * +,* E - -,* # 输入符号 运算符栈 输出结果 操作说明 A 输出A / / 进栈 B A,B 输出B ^ /, ^ ^优先级高于/,继续进栈 C A,B,C 输出C + A,B,C, ^,/ ^,/依次弹出 D A,B,C, ^,/,D 输出D * +,* *优先级高于+,继续进栈 E A,B,C, ^,/,D,E 输出E - A,B,C, ^,/,D,E,*,+ *,+ 依次弹出,- 进栈 A,B,C, ^,/,D,E,*,+,A -,* *优先级高于 -,继续进栈 A,B,C, ^,/,D,E,*,+,A,C # A,B,C, ^,/,D,E,*,+,A,C,*, - 遇到结束符#,依次弹出*,-

【例3-3】3 + 4 /(25–(6 + 15))* 8 输出结果 操作说明 3 输出3 + +进栈 4 3,4 输出4 / +,/ 输入符号 运算符栈 输出结果 操作说明 3 输出3 + +进栈 4 3,4 输出4 / +,/ / 继续进栈 ( +,/,( (进栈 25 3,4,25 输出25 - +,/,(,- - 进栈 +,/,(,-,( (再进栈 6 3,4,25,6 输出6 +,/,(,-,(,+ 15 3,4,25,6,15 输出15 ) 3,4,25,6,15,+ 遇),依次弹出第2个(后的符号 3,4,25,6,15,+,- 遇),依次弹出第1个(后的符号 * +,* 3,4,25,6,15,+,-,/ 弹出/,但*高于+,继续进栈 8 +.* 3,4,25,6,15,+,-,/,8 输出8 # 3,4,25,6,15,+,-,/,8,*,+ 遇到结束符#,依次弹出*,+

4.后缀表达式求值 得到后缀表达式为:3 4 25 6 15 + – / 8 * + 得到后缀表达式为:3 4 25 6 15 + – / 8 * + 4.后缀表达式求值 后缀表达式求值的运算要用到一个数栈stack和一个存放后缀表达式的字符型数组exp。其实现过程就是从头至尾扫描数组中的后缀表达式: (1)当遇到运算对象时,就把它插入到数栈stack中; (2)当遇到运算符时,就执行两次出栈的操作,对出栈的数进行该运算符指定的运算,并把计算的结果压入到数栈stack。把它插入到数栈stack中; (3)重复(1)、(2),直至扫描到表达式的终止符“#”,在数栈的栈顶得到表达式的值。

3 4 25 6 15 + - / 8 * + 第1次计算结果为:21 以例【例3-3】的结果看后缀表达式的计算过程: 第2次计算结果为:4 3 4 25 6 15 + - / 8 * + 第1次计算结果为:21 第2次计算结果为:4 第3次计算结果为:1 第4次计算结果为:8 第5次计算结果为:11

3-3-3 子程序调用(Subroutine Call) 在计算机程序设计中,子程序的调用及返回地址就是利用堆栈来完成的。 在C(或C++)语言的主函数对无参子函数的嵌套调用过程中,在调用子程序前,先将返回地址保存到堆栈中,然后才转去执行子程序。当子函数执行到return语句(或函数结束)时,便从栈中弹出返回地址,从该地址处继续执行程序。 例如:主函数调用子函数a()时,则在调用之前先将a函数返回地址压入栈中;在子函数a()中调用子函数b()时,又将b函数返回地址压人栈中;同样,在子函数b()中调用子函数c()时,又将c函数返回地址压人栈中。其调用返回地址进栈示意图如图3-5所示。

c函数返回地址 b函数返回地址 a函数返回地址 返回地址栈: 图3-5 无参函数嵌套调用返回地址进栈示意图 当执行完子函数c()以后,就从栈顶弹出c()函数返回地址,回到子函数b();子函数b()执行完毕返回时,又从栈顶弹出b函数返回地址,回到子函数a();子函数a()返回时,再在栈顶弹出a函数返回地址,回到主函数,继续执行主函数程序。无参函数嵌调用返回示意图如图3-6所示。

图3-6 无参函数嵌套调用返回示意图

3-3-4 递归调用 1.递归 一个直接调用自己,或者通过一系列调用语句间接地调用自己的函数称为递归函数。在程序设计中,有许多实际问题是递归定义的,使用递归的方法编写程序将使许多复杂的问题大大简化。所以,递归是程序设计中的一个强有力的工具。 2.典型例子 (1)2阶斐波那契(Fibonacci)数列 0        n=0 1        n=1 Fib( n–1 )+Fib( n–2 ) 其它情况 Fib(n)=

(2)阶乘函数 n!的定义为: int fac ( int n ) { if (n= =0) return 1 ; Fac(n)==== n*Fac (n-1) n>0 // 递归步骤 根据定义不难写出相应的递归函数: int fac ( int n ) { if (n= =0) return 1 ; else return (n* fac (n–1) ); }

在C++语言中,系统调用是通过中断来进行,中断调用示意图如图3-8所示。 3-3-5 中断处理和现场保护 1.中断处理(Interrupt Processing) 在C++语言中,系统调用是通过中断来进行,中断调用示意图如图3-8所示。 图3-8 中断调用示意图如 如果把中断处理想象成函数调用,则中断处理程序可以看成被调用的函数。

2. 现场保护和恢复 执行中断时,微处理机有时必须对状态寄存器,累加器,以及相关的寄存器对进行现场保护(压栈);中断处理完毕,则必须按后进先出的原则恢复现场(出栈)。下面,我们以汇编语言来说明现场保护和恢复的原理: … ;接受中断处理 PUSH AX ;保护现场 PUSH BX PUSH CX PUSH BP PUSHF ;F状态寄存器进栈 … ;中断处理 POPF ;恢复现场,后进栈的先出栈 POP BP POP CX POP BX POP AX

小 结 (1)栈是一种运算受限制的线性表,它只允许在栈顶进行插入和删除等操作。 小 结 (1)栈是一种运算受限制的线性表,它只允许在栈顶进行插入和删除等操作。 (2)栈的逻辑结构和线性表相同,数据元素之间存在一对一的关系,其主要特点是“后进先出”。 (3)栈的存储结构有顺序栈和链栈之分,要求掌握栈的C语言描述方法。 (4)重点掌握在顺序栈和链栈上实现:进栈、出栈、读栈顶元素、判栈空和判栈满等基本操作。 (5) 熟悉栈在计算机的软件设计中的各种应用,能灵活应用栈的基本原理解决一些综合性的应用问题。

实验3 栈子系统 1.实验目的 2. 实验内容 (1) 掌握栈的特点及其描述方法。 (2) 用链式存储结构实现一个栈。 (1)  掌握栈的特点及其描述方法。 (2) 用链式存储结构实现一个栈。 (3) 掌握建栈的各种等基本操作。 (4)掌握栈的几个典型应用的算法。 2. 实验内容 (1)设计一个字符型的链栈; (2)编写进栈、出栈、显示栈中全部元素的程序; (3)编写一个把十进制整数转换成二进制数的应用程序; (4)编写一个把中缀表达式转换成后缀表达式(逆波兰式)的应用程序; (5)设计一个选择式菜单,以菜单方式选择上述操作。

*教材p64第3行void Stack() ,在作为子系统上机时上机时改为:void main() 栈 子 系 统 ******************************************** * 1---------进 栈 * * 2---------出 栈 * * 3---------显 示 * * 4---------数制转换 * * 5---------逆波兰式 * * 0---------返 回 * 请选择菜单号: *教材p64第3行void Stack() ,在作为子系统上机时上机时改为:void main()

习题3 参考习题解答 返回