资源预览内容
第1页 / 共539页
第2页 / 共539页
第3页 / 共539页
第4页 / 共539页
第5页 / 共539页
第6页 / 共539页
第7页 / 共539页
第8页 / 共539页
第9页 / 共539页
第10页 / 共539页
亲,该文档总共539页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
数据结构Data Structure河南大学计算机与信息工程学院河南大学计算机与信息工程学院河南大学计算机与信息工程学院河南大学计算机与信息工程学院8 8 8 83 3 3 31 学学 分分: 5 教教 材:材:严蔚敏严蔚敏等,数据结构(等,数据结构(C语言版),清华大语言版),清华大学出版社,学出版社,1997年年4月月 参考书:参考书:1 殷人昆殷人昆等等,数据结构(用面向对象方法与数据结构(用面向对象方法与C+描述),清华大学出版社,描述),清华大学出版社,1999年年7月。¥月。¥26 2 殷人昆殷人昆等等,数据结构习题解析,清华大学出版数据结构习题解析,清华大学出版社,社,2002年年4月。¥月。¥263 李春葆李春葆,数据结构习题与解析(数据结构习题与解析(C语言篇),清语言篇),清华大学出版社,华大学出版社,2001年年1月。¥月。¥284 严蔚敏严蔚敏等等,数据结构题集数据结构题集(C语言版语言版),清华大学,清华大学出版社,出版社, 1997年年4月。¥月。¥162内内 容容 安安 排排章章内内 容容 学时学时 章章 内内 容容 学时学时 1序序 论论27图图122线性表线性表88动态存储管理动态存储管理略略3栈和队列栈和队列89查找查找124串串610内部排序内部排序125数组和广义表数组和广义表 811外部排序外部排序略略6树和二叉树树和二叉树 212文件文件略略注:本学期共注:本学期共85学时,机动学时,机动5学时。学时。3第第1章章 序序 论论1.1 什么是数据结构什么是数据结构1.2 基本概念和术语基本概念和术语1.3 抽象数据类型的表示和实现抽象数据类型的表示和实现1.4 算法和算法分析算法和算法分析作业作业41.1 什么是数据结构什么是数据结构Q1 Q1 如何采用计算机解决问题?如何采用计算机解决问题?Q2 Q2 数据结构解决什么样的问题?数据结构解决什么样的问题?Q3 Q3 数据结构课程介绍数据结构课程介绍5Q1:如何采用计算机解决问题?如何采用计算机解决问题?答:答:寻求数学模型的实质:寻求数学模型的实质: 分析问题,从中提取分析问题,从中提取操作的对象操作的对象,并找出这些,并找出这些操作对象之间含有的操作对象之间含有的关系关系,然后用,然后用数学的语言数学的语言加以加以描述。描述。(2) 设计解此设计解此数学模型数学模型的算法;的算法;(1) 从具体问题抽象出一个适当的数学模型;从具体问题抽象出一个适当的数学模型;(3) 编程,测试、调整直至得到最终解答。编程,测试、调整直至得到最终解答。6Q2:数据结构解决什么样的问题?数据结构解决什么样的问题?答:答: 数据结构研究数据结构研究非数值非数值计算的程序设计问计算的程序设计问题中计算机的操作对象以及它们之间的关系题中计算机的操作对象以及它们之间的关系和操作等的学科。和操作等的学科。7 介于介于数学、计算机硬件和计算机软件数学、计算机硬件和计算机软件三者之间的一门核心课程三者之间的一门核心课程关系对象关系操作数学软件硬件对象关系操作Q3:数据结构课程介绍数据结构课程介绍81.2 基本概念和术语基本概念和术语Q1 什么是数据结构?什么是数据结构?Q2 学习数据结构有什么用?学习数据结构有什么用? Q3 数据结构涵盖的主要内容?数据结构涵盖的主要内容? 讨论:讨论:9Q1:什么是数据结构?什么是数据结构?答答: (见见教教材材P5) 是相相互互之之间间存存在在一一种种或或多多种种特特定定关系关系的的数据元素数据元素的集合,表示为:的集合,表示为:(数值或非数值数值或非数值)Data_Structure=(D, S)或:是指同一数据元素类中各元素之间存在的关系。或:是指同一数据元素类中各元素之间存在的关系。亦可表示为:亦可表示为:S(D, R) 或或 B=(K, R)元素有限集元素有限集关系有限集关系有限集10术语:术语:数据、数据元素和数据项数据、数据元素和数据项(见教材见教材P4定义定义):):数数据据(data)所所有有能能被被计计算算机机识识别别、存存储储和和处处理理的的符符号号的的集集合(合(包括数字、字符、声音、图像等信息包括数字、字符、声音、图像等信息 )。)。数数据据元元素素(data element)是是数数据据的的基基本本单单位位,具具有有完完整整确确定的实际意义定的实际意义(又称元素、结点,顶点、记录等又称元素、结点,顶点、记录等)。)。数数据据项项(Data item)构构成成数数据据元元素素的的项项目目。是是具具有有独独立立含含义义的的最小最小标识单位(标识单位(又称字段、域、属性又称字段、域、属性 等等)。)。三者之间的关系:数据三者之间的关系:数据 数据元素数据元素 数据项数据项例:例:班级通讯录班级通讯录 个人记录个人记录 姓名、年龄姓名、年龄11Q2:学习数据结构有什么用?学习数据结构有什么用?答答:计计算算机机内内的的数数值值运运算算依依靠靠方方程程式式,而而非非数数值值运运算算(如表、树、图等)则要依靠数据结构。(如表、树、图等)则要依靠数据结构。 这这是是一一门门研研究究非非数数值值计计算算的的程程序序设设计计问问题题中中计计算算机机的的操操作对象作对象以及它们之间的以及它们之间的关系和操作关系和操作等等的学科。等等的学科。程序设计实质好算法好结构程序设计实质好算法好结构同样的数据对象,用不同的数据结构来表示,同样的数据对象,用不同的数据结构来表示,运算效率可能有明显的差异。运算效率可能有明显的差异。12Q3:数据结构涵盖的内容?数据结构涵盖的内容?13解释解释1: 什么叫数据的逻辑结构?什么叫数据的逻辑结构?答答:指指数数据据元元素素之之间间的的逻逻辑辑关关系系。即即从从逻逻辑辑关关系系上上描描述述数数据据,它它与与数数据据的的存存储储无无关关,是是独独立立于于计计算机算机的。逻辑结构可细分为的。逻辑结构可细分为4类:类:集合结构:集合结构: 仅同属一个集合仅同属一个集合线性结构线性结构: 一对一(一对一(1:1) 树树 结结 构构: 一对多(一对多(1:n) 图图 结结 构构: 多对多多对多 (m:n)非线性非线性线线 性性14例:例:用图形表示下列数据结构,并指出它用图形表示下列数据结构,并指出它 们是属于线性结构还是非线性结构。们是属于线性结构还是非线性结构。(1) S=(D, R) D= a, b, c, d, e, f R=(a,e), (b,c), (c,a), (e,f), (f,d)解:解: 上述表达式可用图形表示为:上述表达式可用图形表示为:b c a e f d此结构为此结构为线性线性的。的。15(2) S=(D, R) D=di | 1i5 R=(di , dj ), ij d1 d5 d2 d4 d3该结构该结构是非线性的是非线性的。解:解:上述表达式可用图形表示为:上述表达式可用图形表示为:16解释解释2:什么叫数据的物理结构?什么叫数据的物理结构?答答:物物理理结结构构亦亦称称存存储储结结构构,是是数数据据的的逻逻辑辑结结构构在在计计算算机机存存储储器器内内的的表表示示(或或映映像)。它像)。它依赖于计算机依赖于计算机。存储结构可分为存储结构可分为4大类:大类:例:例:(见见教材教材P6)复数)复数3.02.3i 的两种存储方式:的两种存储方式:顺序、链式、索引、散列顺序、链式、索引、散列2.303023.00300041503023.0030004152.3法法1:地址地址 内容内容法法2:地址地址 内容内容2字节字节17解释解释3:什么是数据的运算?什么是数据的运算?答:在数据的逻辑结构上答:在数据的逻辑结构上定义定义的操作算法。的操作算法。它它在数据的存储结构上实现在数据的存储结构上实现。最常用的数据运算有最常用的数据运算有5种:种:插入、删除、修改、查找、排序插入、删除、修改、查找、排序181.3 抽象数据类型的表示和实现抽象数据类型的表示和实现Q1 数据类型与抽象数据类型的区别?数据类型与抽象数据类型的区别?Q2 抽象数据类型如何定义?抽象数据类型如何定义?Q3 抽象数据类型如何表示和实现?抽象数据类型如何表示和实现? 讨论:讨论:提示:教材中例提示:教材中例1-6和例和例1-7分别给出了抽象数据类分别给出了抽象数据类型型“三元组三元组”的定义、表示和实现,请试阅读。的定义、表示和实现,请试阅读。19Q1 数据类型与抽象数据类型的区别?数据类型与抽象数据类型的区别?数据类型:数据类型:是一个值的集合和定义在该值上 的一组操作的总称。抽象数据类型:抽象数据类型:由用户定义,用以表示应用问题的数据模型。它由基本的数据类型构成,并包括一组相关的服务(或称操作)它与数据类型实质上是一个概念,但其特它与数据类型实质上是一个概念,但其特征是征是使用与实现分离使用与实现分离,实行,实行封装封装和和信息隐信息隐蔽蔽(独立于计算机)。(独立于计算机)。20Q2 抽象数据类型如何抽象数据类型如何定义定义?抽象数据类型抽象数据类型可以用以下的三元组来表示:可以用以下的三元组来表示: ADT = (D,S,P) 数据对象数据对象 D上的关系集上的关系集 D上的操作集上的操作集 ADTADT抽象数据类型名抽象数据类型名 数据数据对象对象: 数据数据关系关系: 基本基本操作操作 : ADT ADT抽象数据类型抽象数据类型名名ADT常用常用定义定义格式格式21例:例:给出自然数给出自然数(Natural Number )的抽象数据类型定义的抽象数据类型定义。ADT Natural_Number isobjects: 一个整数的有序子集合,它开始于0,结束于机器能表示的最大整数 (MAX INT)functions: 对于所有的 x, y Natural_Number; TRUE, FALSE Boolean; +, -, , = = ,=等都是可用的服务。Zero ( )Zero ( ): Natural Number Natural Number 返回返回 0 0IsZero(x)IsZero(x): Boolean if (x=0) Boolean if (x=0) 返回返回TRUETRUE else else 返回返回 FALSEFALSEAdd(x, y)Add(x, y): Natural Number Natural Number if (x+y = MAX INT)if (x+y = MAX INT)返回返回 x+yx+y else else 返回返回MAX INTMAX INTSubtract(x,y): Natural NumberNatural Number if (xy)返回返回0 else 返回返回x-yEqual(x,y): Boolean Equal(x,y): Boolean if (x= y) if (x= y)返回返回TRUE else TRUE else 返回返回FALSEFALSESuccessor(x) : Natural NumberNatural Number if (x = MAX INT)返回返回x else 返回返回x+1end Natural_Number 22Q3 抽象数据类型如何抽象数据类型如何表示和实现表示和实现? 抽象数据类型可以通过固有的数据类型(如整型、实型、字符型等)来表示和实现。 即利用处理器中已存在的数据类型来说明新的结构,用已经实现的操作来组合新的操作。注注 :教材中用的是教材中用的是类类C语言(介于伪码和语言(介于伪码和C语言之间)语言之间) 作为描述工具。其描述语法见作为描述工具。其描述语法见P10-11。但上机时要用具体语言实现,如但上机时要用具体语言实现,如C C或或C+C+等等231.4 算法和算法分析算法和算法分析Q1. 什么是算法?什么是算法?Q2. 算法设计的要求算法设计的要求?Q3. 时间复杂度如何表示?时间复杂度如何表示?Q4. 空间复杂度如何表示?空间复杂度如何表示?讨论:讨论:24答:算法是解决某一特定类型问题的答:算法是解决某一特定类型问题的有限运算有限运算 序列序列。是一系列输入转换为输出的。是一系列输入转换为输出的计算步计算步 骤骤。Q1. 什么是算法?什么是算法?算法有算法有5个基本特性:个基本特性:有穷性、确定性、可行性、输入和输出有穷性、确定性、可行性、输入和输出25Q2. 算法设计的要求?算法设计的要求?答:答:(1) (1) 正确性正确性(2) (2) 可读性可读性(3) (3) 健壮性健壮性(4) (4) 效率与低存储需求效率与低存储需求26时间复杂度时间复杂度T(n)按数量级递增顺序为:按数量级递增顺序为: 注注1 O()为渐近符号()为渐近符号。注注2 空间复杂度空间复杂度S(n)按数量级递增顺序也与按数量级递增顺序也与上表类同。上表类同。复杂度高复杂度高复杂度低复杂度低Q3. 时间复杂度如何表示?时间复杂度如何表示?27渐进符号渐进符号(O)的定义:)的定义:当且仅当存在一个正的常数当且仅当存在一个正的常数 C C,使得对所有的,使得对所有的 n n n n0 0 ,有,有 f(n) f(n) C Cg(n)g(n), 则则f(n) = f(n) = O O(g(n)(g(n) 3n+2=O(n) /* 3n+2 4n for n 2 */3n+3=O(n) /* 3n+3 4n for n 3 */100n+6=O(n) /* 100n+6 101n for n 10 */10n2+4n+2=O(n2) /* 10n2+4n+2 11n2 for n 5 */6*2n+n2=O(2n) /* 6*2n+n2 7*2n for n 4 */例:例:28例:例:分析以下程序段的时间复杂度。分析以下程序段的时间复杂度。i=1; while(i=n) i=i*2; 该算法的运行时间由算法中所有语句的该算法的运行时间由算法中所有语句的频度频度(即该语(即该语句重复执行的次数)句重复执行的次数)之和之和构成。构成。解:解:分析:分析:显然,语句显然,语句的频度是的频度是1。设语句。设语句2的频度是的频度是f(n),则有:,则有:即即f(n)log2n,取最大值取最大值f(n)=log2n所以该程序段的时间复杂度所以该程序段的时间复杂度T(n)=1+f(n)=1+ log2n= O( log2n)算法的时间复杂度是由算法的时间复杂度是由嵌套最深层嵌套最深层语句的频度决定的。语句的频度决定的。29Q4. 空间复杂度如何表示?空间复杂度如何表示?S(n) = O(f(n)S(n) = O(f(n) 一个上机执行的程序一个上机执行的程序除了除了需要存储空间来寄存需要存储空间来寄存本本身所用指令、常数、变量和输入数据身所用指令、常数、变量和输入数据外,外,也也需要一些需要一些对数据进行操作的工作单元和存储一些为实现计算所对数据进行操作的工作单元和存储一些为实现计算所需信息的辅助空间。需信息的辅助空间。原地工作:原地工作:若额外空间相对于输入数据量来说是常若额外空间相对于输入数据量来说是常 数,则称此算法为原地工作。数,则称此算法为原地工作。注:注:若额外空间所占空间量依赖于特定的输入,则除若额外空间所占空间量依赖于特定的输入,则除 特别指明外,均按特别指明外,均按最坏最坏情况来分析。情况来分析。30作业:作业:1.思考思考:数据结构、数据类型、抽象数据类型的区别数据结构、数据类型、抽象数据类型的区别2. 算法与程序的区别算法与程序的区别2. 请试做配套习题集请试做配套习题集(P10_16)。3. 复习复习C+语言。语言。31上堂课要点回顾上堂课要点回顾数据结构课程数据结构课程涉及数学、计算机硬件和计涉及数学、计算机硬件和计算机软件算机软件数据结构定义数据结构定义指互相有关联的数据元素的指互相有关联的数据元素的集合,用集合,用D_S=( D, S ) 或或 S=( D, R) 表示。数据结构内容数据结构内容数据的逻辑结构、存储结构数据的逻辑结构、存储结构和运算和运算 算法效率指标算法效率指标时间效率和空间效率时间效率和空间效率 32数据结构课程的内容数据结构课程的内容逻辑结构唯一逻辑结构唯一存储结构不唯一存储结构不唯一运算的实现依赖运算的实现依赖于存储结构于存储结构33近近3周周上课上课内容内容第第2 2章章 线性表线性表第第3 3章章 栈和队列栈和队列第第4 4章章 串串第第5 5章章 数组和广义表数组和广义表 线性结构线性结构若若结结构构是是非非空空有有限限集集,则则有有且且仅仅有有一一个个开开始始结结点点和和一一个个终终端端结结点点,并并且且所所有有结结点点都都最最多多只只有有一一个个直接前趋和一个直接后继。直接前趋和一个直接后继。可表示为可表示为:(:(a1 , a2 , , an) 线性结构的定义:线性结构的定义:(逻辑、存储(逻辑、存储和运算)和运算)34线性结构的特点:线性结构的特点: 只有一个首结点和尾结点;只有一个首结点和尾结点; 除除首首尾尾结结点点外外,其其他他结结点点只只有有一一个个直直接接前前驱驱和和一一个直接后继。个直接后继。线性结构表达式:(线性结构表达式:(a1 , a2 , , an) 线性结构包括线性表、堆栈、队列、字符串、数线性结构包括线性表、堆栈、队列、字符串、数组等等,其中,最典型、最常用的是组等等,其中,最典型、最常用的是-线性表线性表简言之,线性结构反映结点间的逻辑关系是简言之,线性结构反映结点间的逻辑关系是 一对一一对一 的的见第见第2章章35第第2章章 线性表线性表2.1 线性表的逻辑结构线性表的逻辑结构 2.2 线性表的顺序表示和实现线性表的顺序表示和实现2.3 线性表的链式表示和实现线性表的链式表示和实现2.4 应用举例应用举例 (一元多项式的表示及相加)(一元多项式的表示及相加)作业作业36(a1, a2, ai-1,ai, ai1 ,, an)2.1 2.1 线性表的类型定义线性表的类型定义 一一. . 线性表的定义:线性表的定义:用数据元素的有限序列表示用数据元素的有限序列表示n=0时称为时称为数据元素数据元素线性起点线性起点a ai i的直接前趋的直接前趋a ai i的直接后继的直接后继下标,下标,是元素的是元素的序号,表示元素序号,表示元素在表中的位置在表中的位置n n为元素总个为元素总个数,即表长数,即表长空表空表线性终点线性终点37例例1 分析分析26 个英文字母组成的英文表个英文字母组成的英文表 ( A, B, C, D, , Z)学号学号姓名姓名性别性别年龄年龄班级班级20010118102052001011810205于春梅于春梅女女 18 1820012001级电信级电信016016班班20010118102602001011810260何仕鹏何仕鹏男男 18 1820012001级电信级电信017017班班20010118102842001011810284王王 爽爽女女 18 1820012001级通信级通信011011班班20010118103602001011810360王亚武王亚武男男 18 1820012001级通信级通信012012班班: :例例2 分析学生情况登记表分析学生情况登记表数据元素都是记录数据元素都是记录; 元素间关系是线性元素间关系是线性数据元素都是字母数据元素都是字母; 元素间关系是线性元素间关系是线性同一线性表中的元素必定具有相同特性同一线性表中的元素必定具有相同特性38练:练:判断下列叙述的正误:判断下列叙述的正误:1. 1. 数数据据的的逻逻辑辑结结构构是是指指数数据据元元素素之之间间的的逻逻辑辑关关系系,是用户按使用需要建立的。是用户按使用需要建立的。2. 2. 线线性性表表的的逻逻辑辑结结构构定定义义是是唯唯一一的的,不不依依赖赖于于计计算机。算机。3. 3. 线性结构反映结点间的逻辑关系是一对一的。线性结构反映结点间的逻辑关系是一对一的。4. 4. 一维向量是线性表,但二维或一维向量是线性表,但二维或N N维数组不是。维数组不是。5. 5. “同同一一数数据据逻逻辑辑结结构构中中的的所所有有数数据据元元素素都都具具有有相相同同的的特特性性”是是指指数数据据元元素素所所包包含含的的数数据据项项的的个数都相等。个数都相等。39二二. .线性表的类型定义线性表的类型定义(详见课本P19) 对线性表的有关操作举例请大家看课本对线性表的有关操作举例请大家看课本P20P20例例2-12-1,例,例2-22-2 402.2 线性表的顺序表示和实现线性表的顺序表示和实现2.2.1 顺序表的表示顺序表的表示2.2.2 顺序表的实现顺序表的实现2.2.3 顺序表的运算效率分析顺序表的运算效率分析本节小结本节小结作作 业业412.2.1 顺序表的表示顺序表的表示用用一一组组地地址址连连续续的的存存储储单单元元依依次次存存储储线线性性表表的的元元素素,可可通通过过数数组组VnVn来来实实现。现。把逻辑上相邻的数据元素存储在物把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。理上相邻的存储单元中的存储结构。线性表的顺序表示又称为线性表的顺序表示又称为顺序存储结构顺序存储结构或或顺序映像顺序映像。顺序存储定义:顺序存储定义:顺序存储方法:顺序存储方法:简言之,逻辑上相邻,物理上也相邻简言之,逻辑上相邻,物理上也相邻1.1.顺序表示顺序表示422.2.线性表顺序存储特点:线性表顺序存储特点:1) 逻辑上相邻的数据元素,其物理上也相邻;逻辑上相邻的数据元素,其物理上也相邻;2)若已知表中首元素在存储器中的位置,则其他元若已知表中首元素在存储器中的位置,则其他元素存放位置亦可求出(素存放位置亦可求出(利用数组下标利用数组下标)。计算方法是)。计算方法是(参见存储结构示意图参见存储结构示意图):): 设首元素设首元素a1的存放地址为的存放地址为LOC(a1)(称称为首地址为首地址),), 设每个元素占用存储空间(地址长度)为设每个元素占用存储空间(地址长度)为L字节,字节, 则表中任一数据元素的则表中任一数据元素的存放地址存放地址为:为: LOC(ai) = LOC(a1) + L *(i-1) LOC(ai+1) = LOC(ai)+L 注意:注意:C C语言中的数组的下标从语言中的数组的下标从0 0开始,开始, 即:即:VnVn的有效范围是的有效范围是V0V0Vn-1Vn-143线性表的顺序存储结构示意图线性表的顺序存储结构示意图a a1 1a a2 2a ai ia ai+1i+1a an n 地址地址 内容内容 元素在表中的位序元素在表中的位序1 1i i2 2n n空闲区空闲区i+1i+1Lb=LOC(a1)b + + L Lb +(i-1)+(i-1)L Lb +(n-1)+(n-1)L Lb +(max-1)+(max-1)L L44一个一维数组,下标的范围是到一个一维数组,下标的范围是到,每个数组元素用相邻的,每个数组元素用相邻的个字节个字节存储。存储。存储器按字节编址,设存储数组元素存储器按字节编址,设存储数组元素 的第一个字节的地址是的第一个字节的地址是,则,则 的第一个字节的地址是的第一个字节的地址是 113因此:因此:LOC( M3 ) = 98 + 5 (3-0) =113解:解:地址计算通式为:地址计算通式为:LOC(ai) = LOC(a1) + L *(i-1)例例1 1453.3.线性表的动态分配顺序存储结构线性表的动态分配顺序存储结构# define LIST_INIT_SIZE 100 / / 线性表存储空间的初始分配量线性表存储空间的初始分配量# define LISTINCREMENT 10 / / 线性表存储空间的分配增量线性表存储空间的分配增量Typedef struct /若后面不再用,可省略结构名若后面不再用,可省略结构名 ElemType *elem; /表基址表基址 int length; /表长(特指元素个数)表长(特指元素个数) int listsize; /表当前存储容量(字节数)表当前存储容量(字节数)SqList;46问问1:自定义结构类型变量自定义结构类型变量test的长度的长度m是多少?是多少? msizeof(test)问问2:结构变量结构变量test的首地址的首地址(指针指针p)是多少?)是多少? p(test*)malloc(m)问问3:怎样删除结构变量怎样删除结构变量test?只能借助其指针删除!?只能借助其指针删除! free(p) 补充:补充:介绍三个有用的库函数(都在介绍三个有用的库函数(都在 中):中):sizeof(x)计算变量计算变量x的长度;的长度;malloc(m) 开辟开辟m字节长度的地址空间,并返回这段空间的字节长度的地址空间,并返回这段空间的首地址;首地址;free(p) 释放指针释放指针p所指变量的存储空间,即彻底删除一所指变量的存储空间,即彻底删除一个变量。个变量。*nextdatatest,长度为长度为m字节字节p472.2.2 顺序表的实现(或操作)顺序表的实现(或操作)回忆回忆:数据结构基本运算操作有:数据结构基本运算操作有: 修改、插入、删除、修改、插入、删除、查找、排序查找、排序1.1.修改修改 通过数组的下标便可访问某个特定通过数组的下标便可访问某个特定元素并修改之。元素并修改之。核心语句核心语句: : Vi=x;显然,顺序表修改操作的时间效率是显然,顺序表修改操作的时间效率是T(n)=O(1)482.2.建立建立 Status InitList_Sq( SqList &L ) /构造一个空的线性表构造一个空的线性表L。 L.elem = ( ElemType * ) malloc( LIST_INIT_SIZE * sizeof ( ElemType) ); If ( ! L.elem ) exit ( OVERFLOW ) ; / 存储分配失败存储分配失败 L.length = 0; / 空表长度为空表长度为0 L.listsize = LIST_INIT_SIZE; / 初始存储容量初始存储容量 return OK; / InitList_Sq493.3.插入插入 在线性表的第在线性表的第i i个位置个位置前前插入插入一个元素一个元素实现步骤:实现步骤:(n为表长)为表长)将第将第n至第至第i 位的元素向后移动一个位置;位的元素向后移动一个位置;将要插入的元素写到第将要插入的元素写到第i个位置;个位置;表长加表长加1。注意:注意:事先应判断事先应判断: 插入位置插入位置i 是否合法是否合法?表是否已满表是否已满? 应当有应当有1in+1 或或 i=1,n+1for (p=&(L.elemn-1);p=q;-p)for (p=&(L.elemn-1);p=q;-p)*(p+1)=*p;*(p+1)=*p;*p=e; *p=e; +n;+n;/ / 元素后移一个位置元素后移一个位置/ / 插入插入e e / / 表长加表长加1 1 核心语句:核心语句:50在线性表的第在线性表的第i i个位置前插入一个元素的示意图如下:个位置前插入一个元素的示意图如下:121321242830427712132124252830427712345678123456789插入插入252551实现步骤:实现步骤:(n为表长)为表长)将第将第i +1至第至第n 位的元素向前移动一个位置;位的元素向前移动一个位置;表长减表长减1。注意:注意:事先需要判断,事先需要判断,删除位置删除位置i 是否合法是否合法?应当有应当有1in 或或 i=1, n4.4.删除删除 删除删除线性表的第线性表的第i i个位置上的元素个位置上的元素for (+p;p=q;+p)for (+p;p=q;+p)*(p-1)=*p; *(p-1)=*p; -n;-n;/ / 元素前移一个位置元素前移一个位置/ / 表长减表长减1 1 核心语句:核心语句:52123456789121321242528304277123456781213212428304277删除顺序表中某个指定的元素的示意图如下:删除顺序表中某个指定的元素的示意图如下:532.2.3 顺序表的运算效率分析顺序表的运算效率分析算法时间主要耗费在移动元素的操作上,因此算法时间主要耗费在移动元素的操作上,因此 计算时间复杂度的基本操作(最深层语句频度)计算时间复杂度的基本操作(最深层语句频度) T(n)= O (移动元素次数移动元素次数)移动元素的个数取决于插入或删除元素的位置移动元素的个数取决于插入或删除元素的位置.思考:思考:若插入在尾结点之后,则根本无需移动(特别快);若插入在尾结点之后,则根本无需移动(特别快);若插入在首结点之前,则表中元素全部要后移(特别慢);若插入在首结点之前,则表中元素全部要后移(特别慢);若要考虑在各种位置插入(共若要考虑在各种位置插入(共n+1种可能)的种可能)的平均平均移动次数,该如何计算?移动次数,该如何计算?讨论讨论1:若在长度为若在长度为 n 的线性表的第的线性表的第 i 位前位前 插入一插入一 元素,则向后移动元素的次数元素,则向后移动元素的次数f(n)为为:f(n) =n i + 1时间效率分析时间效率分析:54证证明明:假假定定在在每每个个元元素素位位置置上上插插入入x x的的可可能能性性都都一一样样(即即概率概率P P相同),则应当这样来计算平均执行时间:相同),则应当这样来计算平均执行时间:将所有位置的执行时间相加,然后取平均。将所有位置的执行时间相加,然后取平均。若在首结点前插入,需要移动的元素最多,后移若在首结点前插入,需要移动的元素最多,后移n n次;次;若在若在a a1 1后面插入,要后移后面插入,要后移n-1n-1个元素,后移次数为个元素,后移次数为n-1;n-1;若在若在a an-1n-1后面插入,要后移后面插入,要后移1 1个元素;个元素;若在尾结点若在尾结点a an n之后插入,则后移之后插入,则后移0 0个元素;个元素;所有可能的元素移动次数合计:所有可能的元素移动次数合计: 0+1+n = n(n+1)/2 0+1+n = n(n+1)/2共有多少种插入形式共有多少种插入形式?连头带尾有连头带尾有n+1n+1种种所以平均移动次数为:所以平均移动次数为:n(n+1)/2n(n+1)/2(n+1n+1)n/2n/2O(n)O(n) 同理可证:同理可证:顺序表删除一元素的时间效率为顺序表删除一元素的时间效率为: :T T(n)=(n-1)/2 n)=(n-1)/2 O(n)O(n) 55讨论讨论2:若在长度为若在长度为n n的线性表上删除第的线性表上删除第i i位元素,向位元素,向前移动元素的次数前移动元素的次数f(n)f(n)为:为:f(n)=f(n)=思考:思考:若删除尾结点,则根本无需移动(特别快);若删除尾结点,则根本无需移动(特别快);若删除首结点,则表中元素全部要前移(特别慢);若删除首结点,则表中元素全部要前移(特别慢);若要考虑在各种位置删除(共若要考虑在各种位置删除(共n种可能)的平均移动次数,该如何计算?种可能)的平均移动次数,该如何计算?n i可以证明:可以证明:插入、删除操作平均需要移动一半元素插入、删除操作平均需要移动一半元素(n/ 2)插入、删除算法的平均插入、删除算法的平均时间时间复杂度为:复杂度为:O(n)显然,顺序表的显然,顺序表的空间空间复杂复杂度度S(n)=O(1)S(n)=O(1)(没有占用辅助空间)(没有占用辅助空间)56教材教材P25算法算法2.5也是对执行效率的推导:也是对执行效率的推导:假定在表中任意位置插入、删除元素都是等概率的,假定在表中任意位置插入、删除元素都是等概率的,插入概率插入概率p(i)=1/(n+1) ,删除概率,删除概率q(i)=1/n ,则:,则:插入操作时间效率插入操作时间效率(平均移动次数)(平均移动次数) 删除操作时间效率删除操作时间效率(平均移动次数)(平均移动次数) 57本节小结本节小结线性表线性表顺序存储结构特点:顺序存储结构特点:逻辑关系上相邻的逻辑关系上相邻的两个元素在物理存储位置上也相邻;两个元素在物理存储位置上也相邻;优点:优点:可以随机存取表中任一元素;可以随机存取表中任一元素;缺点:缺点:在插入,删除某一元素时,需要移动大在插入,删除某一元素时,需要移动大量元素。量元素。为克服这一缺点,我们引入另一种存储形式:为克服这一缺点,我们引入另一种存储形式:链式存储结构链式存储结构见见2.3节节58作业:作业: 实验:实验: P181 P181 实验一实验一: : 集合的交并和差运算的实现集合的交并和差运算的实现59 第第2章章 线性表线性表2.1 线性表的逻辑结构线性表的逻辑结构 2.2 线性表的顺序表示和实现线性表的顺序表示和实现2.3 线性表的链式表示和实现线性表的链式表示和实现2.4 应用举例应用举例 作业作业60上堂课要点回顾上堂课要点回顾1.1.线性结构线性结构( (包括表、栈、队、数组)的定义和特点:包括表、栈、队、数组)的定义和特点:2.2. 仅一个首、尾结点,其余元素仅一个直接前驱和仅一个首、尾结点,其余元素仅一个直接前驱和 一个直接后继。一个直接后继。2. 2. 线性表线性表逻辑结构:逻辑结构:“一对一一对一” ” 或或 1:1 1:1存储结构:顺序、链式存储结构:顺序、链式运运 算算 :修改、插入、删除:修改、插入、删除3.3.顺序存储顺序存储特征:逻辑上相邻,物理上也相邻;特征:逻辑上相邻,物理上也相邻;优点:随机查找快优点:随机查找快 O(1) O(1)缺点:插入、删除慢缺点:插入、删除慢 O(n) O(n)612.3 线性表的链式表示和实现线性表的链式表示和实现2.3.1 链表的表示链表的表示2.3.2 链表的实现链表的实现线性链表,循环链表,双向链表线性链表,循环链表,双向链表2.3.3 链链表的运算效率分析表的运算效率分析本节小结本节小结作作 业业(续上堂课)续上堂课)622.3.1 链表的表示链表的表示1.1.链式存储特点链式存储特点2.2.与链式存储有关的术语与链式存储有关的术语3.3.线性表的单链表存储结构线性表的单链表存储结构631. 1. 链式存储特点:链式存储特点:逻辑上相邻,物理上逻辑上相邻,物理上不一定不一定相邻相邻链表存放示意图如下:链表存放示意图如下: a1heada2/an讨论讨论1 1:每个存储结点都包含两部分:数据和:每个存储结点都包含两部分:数据和 。讨论讨论2 2:在单链表中,除了首元结点外,任一结点的存储位置:在单链表中,除了首元结点外,任一结点的存储位置 由由 指示。指示。 其直接前驱结点的链域的值其直接前驱结点的链域的值指针域指针域( (链域链域) )642. 2. 与链式存储有关的术语与链式存储有关的术语1) 1) 结点结点: : 数据元素的存储映像。由数据域和指针域两部分组成;数据元素的存储映像。由数据域和指针域两部分组成;2 2)链表:链表: n n 个结点由指针链组成一个链表。它是线性表的链式个结点由指针链组成一个链表。它是线性表的链式 存储映像存储映像,称为线性表的链式存储结构称为线性表的链式存储结构。3 3)单链表、双链表、多链表、循环链表单链表、双链表、多链表、循环链表: 结点只有一个指针域的链表,称为结点只有一个指针域的链表,称为单链表单链表或或线性链表线性链表;有两个指针域的链表,称为有两个指针域的链表,称为双链表双链表;有多个指针域的链表,称为有多个指针域的链表,称为多链表多链表;首尾相接的链表称为首尾相接的链表称为循环链表循环链表。4)头指针、头结点和首元结点头指针、头结点和首元结点以教材以教材P27 图图2.5和图和图2.6内容为例说明。内容为例说明。65何谓头指针、头结点和首元结点头指针、头结点和首元结点?头指针头指针是指向链表中第一个结点(或为头结点或为是指向链表中第一个结点(或为头结点或为首元结点)的指针。首元结点)的指针。 单链表可由一个头指针唯一确定。单链表可由一个头指针唯一确定。头结点头结点是在链表的首元结点之前附设的一个结点;是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息数据域内只放空表标志和表长等信息; ;首元结点首元结点是指链表中存储线性表第一个数据元素是指链表中存储线性表第一个数据元素a a1 1的结点。的结点。 头指针头指针头结点头结点 首元结点首元结点a166一个线性表的逻辑结构为:一个线性表的逻辑结构为:(ZHAO,QIAN,SUN,LI,ZHOU,WU,ZHENG,WANGZHAO,QIAN,SUN,LI,ZHOU,WU,ZHENG,WANG),其存储),其存储结构用单链表表示如下,结构用单链表表示如下,请问其头指针的值是多少请问其头指针的值是多少?存储地址存储地址数据域数据域指针域指针域1LI437QIAN1313SUN119WANGNULL25WU3731ZHAO737ZHENG1943ZHOU25例例:答:答:头指针是指向头指针是指向链表中第一个结点链表中第一个结点的指针,因此关键的指针,因此关键是要寻找第一个结是要寻找第一个结点的地址。点的地址。7ZHAOH31头指针的值是头指针的值是3167上例链表的逻辑结构示意图有以下两种形式:上例链表的逻辑结构示意图有以下两种形式:ZHAOQIANLISUNZHOUWUZHENG/WANGHZHAOQIANLISUNZHOUWUZHENG/WANGH区别:区别: 无头结点无头结点 有头结点有头结点68Typedef struct Lnode ElemType data; /数据域 struct Lnode *next; /指针域Lnode, *LinkList; / *LinkList为Lnode类型的指针3. 线性表的单链表存储结构线性表的单链表存储结构692.3.2 链表的实现链表的实现1. 1. 单链表的建立单链表的建立2. 2. 单链表的查找单链表的查找3. 3. 单链表的插入单链表的插入4. 4. 单链表的删除单链表的删除5. 5. 其它链表形式其它链表形式701. 1. 单链表的建立单链表的建立难点分析:难点分析:每个数据元素在内存中是每个数据元素在内存中是“零散零散”存放的,存放的,其首地址怎么找?又怎么一一链接?其首地址怎么找?又怎么一一链接?实现思路:实现思路:先开辟头指针,然后陆续为每个数据元先开辟头指针,然后陆续为每个数据元素开辟存储空间并赋值,并及时将地址送给前面的素开辟存储空间并赋值,并及时将地址送给前面的指针。指针。71Void CreateList_L (LinkList &L, int n) /逆位序输入逆位序输入n n个元素的值个元素的值, ,建立带表头结点的单链线性表建立带表头结点的单链线性表L.L. L = (LinkList) malloc (sizeof(LNode); Lnext = NULL; / / 先建立一个带头结点的单链表先建立一个带头结点的单链表 for ( i =n; i0; -i) p = (LinkList) malloc (sizeof(LNode); / / 生成新结点生成新结点 scanf (&p data); / / 输入元素值输入元素值 pnext = Lnext ; Lnext = p; / / 插入到表头插入到表头 / CreateList_L722. 2. 单链表的查找单链表的查找思路:思路:要修改第要修改第i个数据元素,关键是要先找到该结个数据元素,关键是要先找到该结 点的指针点的指针p,然后用,然后用p-data=new_value 即可即可。难点:难点:单链表中想取得第单链表中想取得第i个元素,必须从头指针出个元素,必须从头指针出 发寻找(顺藤摸瓜),不能随机存取发寻找(顺藤摸瓜),不能随机存取 。核心语句:核心语句:见教材见教材P29的的GetElem_L函数说明函数说明73Status Status GetElem_L(LinkList L, int i, ElemType &e) / L/ L为带头结点的单链表的头指针为带头结点的单链表的头指针 / / 当第当第i i个元素存在时个元素存在时, ,其值赋给其值赋给e e并返回并返回OK,OK,否则返回否则返回ERRORERROR P = L-next; j=1; / / 初始化初始化 ,p ,p指向第一个结点指向第一个结点,j,j为计数器为计数器 while(p&jnext; +j; / / 顺指针向后查找顺指针向后查找, ,直到直到p p指向第指向第i i个元素或个元素或p p为空为空 if(!p|ji) return ERROR; / / 第第i i个元素不存在个元素不存在 e=p-data; / / 取第取第i i个元素个元素 return OK; / GetElem_L讨论:讨论:要统计链表中数据元素的个数,该如何改写?要统计链表中数据元素的个数,该如何改写? 743. 3. 单链表的插入单链表的插入在链表中插入一个元素的示意图如下:在链表中插入一个元素的示意图如下:xsbapabp插入步骤(即核心语句):插入步骤(即核心语句):Step 1Step 1:s-next=p-next;Step 2Step 2:p-next=s ;p-nexts-next思考:思考:步骤步骤1 1和和2 2能互换么?能互换么?元素元素x x结点应预先生成:结点应预先生成:S=(test*)malloc(m);S-data=x;S-next=p-next754. 4. 单链表的删除单链表的删除在链表中删除某元素的示意图如下:在链表中删除某元素的示意图如下:cabp删除步骤(即核心语句):删除步骤(即核心语句):q = p-next; /保存保存b的指针,靠它才能指向的指针,靠它才能指向cp-next=q-next; /a、c两结点相连两结点相连free(q) ; /删除删除b结点,彻底释放结点,彻底释放p-next思考:思考: 省略省略free(q)语句语句行不行?行不行?(p-next) - next76应用举例:两个链表的归并(教材应用举例:两个链表的归并(教材P31P31)算法要求:算法要求:已知:已知:线性表线性表 A、B,分别由,分别由单链表单链表 LA , LB 存储,其存储,其中数据元素按值中数据元素按值非递减有序非递减有序排列,排列,要求:要求:将将 A ,B 归并归并为一个新的线性表为一个新的线性表C , C 的数据元的数据元素仍按值非递减排列素仍按值非递减排列 。设线性表。设线性表 C 由由单链表单链表 LC 存储。存储。假设:假设:A=(3,5,8,11),),B=(2,6,8,9,11)预测:预测:合并后合并后 C =( 2 , 3 , 5 , 6 , 8 , 8 , 9 , 11,11 )77用链表可表示为:用链表可表示为: 3 511 / 8 LaLa 2 611 / 8 LbLb 9 2 3 6 5 LcLc 8头结点头结点78算法分析:算法分析:算法主要包括:算法主要包括:搜索、比较、插入搜索、比较、插入三个操作:三个操作:搜索:搜索:需要需要两个指针两个指针搜索两个链表;搜索两个链表;比较:比较:比较结点数据域中数据的大小;比较结点数据域中数据的大小;插入:插入:将两个结点中数据将两个结点中数据小的结点插入新链表小的结点插入新链表。79La3 5 8 11 Lb2 6 8 119 PaPbPaPbPa、Pb用于搜索用于搜索La和和Lb, Pc指向新链表当前结点指向新链表当前结点 Lc Pa3 PcPa5 Pc11Pc2 PbPcPa80算法实现:算法实现: Void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc) /按值排序的单链表按值排序的单链表LA,LB,归并为,归并为LC后也按值排序后也按值排序 free(Lb); /释放释放Lb的头结点的头结点 /MergeList_L pc-next = pa?pa:pb ; /插入剩余段插入剩余段 while(pa&pb) /将将pa 、pb结点按大小依次插入结点按大小依次插入C中中 if(pa-datadata) pc-next=pa; pc=pa; pa=pa-next; else pc-next=pb; pc=pb; pb=pb-next pa=La-next; pb=Lb-next; Lc=pc=La; /初始化初始化 81思考:思考:1、不用、不用Lc,直接把,直接把La表插到表插到Lb表中;或者把表中;或者把Lb表表 插到插到La表中,如何编程?表中,如何编程?2、要求不能有重复的数据元素,如何编程?、要求不能有重复的数据元素,如何编程?825. 其它链表形式1) 循环链表:循环链表:将表中最后一个结点的指针域指向头结将表中最后一个结点的指针域指向头结 点点(P-next=head;)(P-next=head;),整个链表形成一个环。,整个链表形成一个环。 (示例见课本(示例见课本P35 P35 图图2.122.12)A.A.特点:特点:从任一结点出发均可找到表中其他结点。从任一结点出发均可找到表中其他结点。从任一结点出发均可找到表中其他结点。从任一结点出发均可找到表中其他结点。B. B. 与单链表的区别:与单链表的区别:与单链表的区别:与单链表的区别:循环条件循环条件循环条件循环条件 单链表单链表单链表单链表 - p = NULL - p = NULL 或或或或 p -next =NULL p -next =NULL 循环链表循环链表循环链表循环链表- p= head - p= head 或或或或 p-next = head p-next = headC. C. 设立尾指针:设立尾指针:设立尾指针:设立尾指针:可以使链表合并简化(课本可以使链表合并简化(课本可以使链表合并简化(课本可以使链表合并简化(课本P35-P35-图图图图2.132.13)。)。)。)。832)双向链表:双向链表:有两个指针域的链表,称为双链表。有两个指针域的链表,称为双链表。 ( (示例见课本示例见课本P36P36图图2.14)2.14)A.A.结点结构:结点结构:B.B.特点:特点:可以双向查找表中结点。可以双向查找表中结点。C.C.运算:运算: 插入插入课本课本P36P36算法算法2.182.18,图,图2.162.16。 删除删除课本课本P37P37算法算法2.192.19,图,图2.152.15。 注:注:双向链表在非线性结构(如树结构)中将大量使用。双向链表在非线性结构(如树结构)中将大量使用。 prior datanext842.3.3 链表的运算效率分析链表的运算效率分析时间效率分析1. 查找查找 因线性链表只能顺序存取,即在查找时因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为要从头指针找起,查找的时间复杂度为 O(n)。2. 插入和删除插入和删除 因线性链表不需要移动元素,只要修因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为改指针,一般情况下时间复杂度为 O(1)。但是,如果要在单链表中进行前插或删除操作,由于但是,如果要在单链表中进行前插或删除操作,由于要从头查找前驱结点,所耗时间复杂度为要从头查找前驱结点,所耗时间复杂度为 O(n)。85练习:练习:在在n n个结点的单链表中要删除已知结点个结点的单链表中要删除已知结点*P*P,需找到它,需找到它的的前驱结点的地址前驱结点的地址,其时间复杂度为,其时间复杂度为 O O(n)(n) 。空间效率分析空间效率分析链表中每个结点都要增加一个指针空间,相当于总共链表中每个结点都要增加一个指针空间,相当于总共增加了增加了n n 个整型变量,空间复杂度为个整型变量,空间复杂度为 O O(n)(n)。862.4 应用举例应用举例1.一元多项式的数学通式?一元多项式的数学通式?2.用抽象数据类型如何描述它的定义?用抽象数据类型如何描述它的定义?3.用用C语言如何描述它的定义?语言如何描述它的定义?4.如何编程实现两个一元多项式相加?如何编程实现两个一元多项式相加?一元多项式的表示及相加一元多项式的表示及相加(参见教材参见教材P39 43)讨论:讨论:871. 一元多项式的数学通式?一元多项式的数学通式?一元多项式的通式可表示为:一元多项式的通式可表示为:分析:分析:一元多项式在计算机内存储时,既可用一元多项式在计算机内存储时,既可用顺序表顺序表存储,存储,又可用又可用链表链表存储。但当多项式的次数很高且零系数项很多时,存储。但当多项式的次数很高且零系数项很多时,则更适于用链表存储(则更适于用链表存储(通常设计两个数据域和一个指针域通常设计两个数据域和一个指针域)。)。a0a1a2am-2am-1顺序表顺序表链表链表am-1 em-1am-2 em-2 a0 e0 或或0.0 -1 am-1 em-1 a0 e0882. 2. 用抽象数据类型如何用抽象数据类型如何定义定义一元多项式?一元多项式?(参见(参见P P4040)ADT PolynomialPolynomial数据对象:D=ai|aiTermSet,i=1,2,m, m0TermSetTermSet中的每个元素包含一个表示系数的实数和表示指数的整数中的每个元素包含一个表示系数的实数和表示指数的整数数据关系:R1=| ai-1, ai D, 且且a ai-1i-1中的指数值中的指数值aexpon =、Pb-expon等情况,再决定是等情况,再决定是将两系数域的数值相加(并判其和是否为将两系数域的数值相加(并判其和是否为0 0),还),还是将较高指数项的结点插入到新表是将较高指数项的结点插入到新表c c中。中。3 142 81 0 aPaPa8 14-3 1010 6 bPbPb11 14-3 102 81 0 cPcPc10 6+93具体编程(用具体编程(用C语言)语言)1.利用建表操作CreatPolyn(&P,mCreatPolyn(&P,m) )分别建立链表a和链表b;详细内容参见教材P42下部描述。2. 利用加操作AddPolyn(&AddPolyn(&P Pa a,&P,&Pb b) )对对链表a和链表b进行相加;详细内容参见教材P43描述。编程时请注意,在前面定义中已规定:编程时请注意,在前面定义中已规定:初始条件:一元多项式初始条件:一元多项式P Pa a和和P Pb b已存在。已存在。操作结果:完成多项式相加运算,即:操作结果:完成多项式相加运算,即:P Pa aP Pa aP Pb b,并并销毁一元多项式销毁一元多项式P Pb b。从教材程序中可从教材程序中可“猜猜”出,例中的链表是按指数项升序排列的。出,例中的链表是按指数项升序排列的。94Void AddPolyn(polynomial&Void AddPolyn(polynomial&P Pa a, , polynomialpolynomial &P &Pb b)ha=GetHead(Pa); hb =GetHead(Pb); /取二表头指针取二表头指针( (指向头结点)指向头结点)qa=NextPos(Pa,ha); qb = NextPos(Pb,hb); /指向二表首元结点指向二表首元结点while (qa & qb) / 若二表均未到末尾,若二表均未到末尾, a=GetCurElem(qa); /*/* 则比较两结点的数据域(注意则比较两结点的数据域(注意a,ba,b含有含有 b=GetCurElem(qb); 两个数据分量)两个数据分量)*/*/switch (*cmp(a,b) / cmp(a,b)cmp(a,b)是用户自定义函数,见是用户自定义函数,见P42P42倒倒9 9行行case -1: / 若若a a的指数值小于的指数值小于b b,此,此a a结点不动(升序)结点不动(升序)ha=qa; qa=NextPos(Pa,ha); break;case 0: / 若若a a的指数值等于的指数值等于b b,则系数相加,则系数相加sum=a.coef+b.coef;If(sum!=0.0)SetCurElem(qa,sum); ha=qa; 95 elseDelFirst(ha,qa); FreeNode(qa);/若系数为若系数为0 0,则两结点都删,则两结点都删 DelFirst(hb,qb); FreeNode(qb); qa=NextPos(Pa,ha); qb=NextPos(Pb,hb); break;case 1: / 若若a a的指数值大于的指数值大于b b,应前插,应前插b b(保持升序)保持升序)DelFirst(hb,qb); InsFirst(ha,qb); qb=NextPos(Pb,hb); ha=NextPos(Pa,ha); break; /a/a表大结点后移表大结点后移/switch/whileIf(!List Empty(Pb) Append(Pa,qb); / 若若a a表空,则表空,则b b表剩余项全部表剩余项全部 /链接到链接到a a表中;而表中;而b b表空时无需动作,因为表空时无需动作,因为a a表本身就是求和结果。表本身就是求和结果。FreeNode(hb); /无论什么结局,最终无论什么结局,最终b b表都是要废掉的。表都是要废掉的。/ AddPolynAddPolyn96运算效率分析运算效率分析:(1)系数相加系数相加0 加法次数加法次数 min(m, n)其中其中 m和和n分别表示表分别表示表A和表和表B的结点数。的结点数。(2)指数比较指数比较极端情况是极端情况是表表A和表和表B 没有一项指数相同,没有一项指数相同,比较次数最多为比较次数最多为m+n-1 (3)新结点的创建新结点的创建极端情况是产生极端情况是产生m + n 个新结点个新结点合计时间复杂度为合计时间复杂度为 O(m+n)97本章小结(本章小结(讨论题形式讨论题形式)问问1:线性表的逻辑结构特点是什么?其顺序存储线性表的逻辑结构特点是什么?其顺序存储结构和链式存储结构的特点是什么?结构和链式存储结构的特点是什么?答:答:线性表逻辑结构特点是,只有一个首结点和尾结点;线性表逻辑结构特点是,只有一个首结点和尾结点;除首尾结点外其他结点只有一个直接前驱和一个直接后继。除首尾结点外其他结点只有一个直接前驱和一个直接后继。简言之,线性结构反映结点间的逻辑关系是一对一简言之,线性结构反映结点间的逻辑关系是一对一(1:11:1)的。)的。 顺序存储时,相邻数据元素的存放地址也相邻(逻辑与物顺序存储时,相邻数据元素的存放地址也相邻(逻辑与物理统一);要求内存中可用存储单元的地址必须是连续的。理统一);要求内存中可用存储单元的地址必须是连续的。 链式存储时,相邻数据元素可随意存放,但所占存储空间链式存储时,相邻数据元素可随意存放,但所占存储空间分两部分,一部分存放结点值,另一部分存放表示结点间分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针。关系的指针。98问问2:顺序存储和链式存储各有哪些优缺点?顺序存储和链式存储各有哪些优缺点?答:顺序存储的优点是存储密度大答:顺序存储的优点是存储密度大( (1)1),存储空,存储空间利用率高。缺点是插入或删除元素时不方便。间利用率高。缺点是插入或删除元素时不方便。 链式存储的优点是插入或删除元素时很方便,使链式存储的优点是插入或删除元素时很方便,使用灵活。缺点是存储密度小(用灵活。缺点是存储密度小(1M)上溢 else vtop+top+=x;Push (B);Push (C);Push (D);toptoptoptop低地址低地址LPush (A);高地址高地址MBCD109 出栈操作出栈操作例如从栈中取出例如从栈中取出BB (注意要遵循(注意要遵循“后进先出后进先出” ” 原则)原则)DCBAtoptopDCABDCBAtopDCBAtoptop低地址低地址L高地址高地址MD核心语句:核心语句:Pop ( );Pop ( );Printf( Pop () );顺序栈中的顺序栈中的POPPOP函数函数status Pop( )status Pop( ) if(top=L) if(top=L) 下溢下溢 else y=velse y=v-top-top; ; return(y); return(y); 110例例1:一个栈的输入序列是一个栈的输入序列是12345,若在入栈的过,若在入栈的过程中允许出栈程中允许出栈,则栈的输出序列则栈的输出序列43512可能实现可能实现吗?吗?12345的输出呢?的输出呢? 43512不不可可能能实实现现,主主要要是是其其中中的的12顺顺序序不不能能实现;实现; 12345的输出可以实现,只需压入一个立即弹的输出可以实现,只需压入一个立即弹出一个即可。出一个即可。 435612中到了中到了12顺序不能实现;顺序不能实现; 135426可以实现。可以实现。例例2:如果一个栈的输入序列为如果一个栈的输入序列为123456,能否得,能否得到到435612和和135426的出栈序列?的出栈序列?答:答:答:答:111例例3(严题集(严题集3.1)一个栈的输入序列为一个栈的输入序列为123,若在,若在入栈的过程中允许出栈,则可能得到的出栈序列是入栈的过程中允许出栈,则可能得到的出栈序列是什么?什么?答:答: 可以通过穷举所有可能性来求解:可以通过穷举所有可能性来求解: 1 1入入1 1出,出, 2 2入入2 2出,出,3 3入入3 3出,出, 即即123123; 1 1入入1 1出,出, 2 2、3 3入入3 3、2 2出,出, 即即132132; 1 1、2 2入,入,2 2出,出, 3 3入入3 3出,出, 即即231231; 1 1、2 2入,入,2 2、1 1出,出,3 3入入3 3出,出, 即即213213; 1 1、2 2、3 3入,入,3 3、2 2、1 1出,出, 即即321321;合计有合计有5 5种可能性。种可能性。112补充补充1:若入栈动作使地址向高端增长,称为若入栈动作使地址向高端增长,称为“向上生成向上生成”的栈;的栈;若入栈动作使地址向低端增长,称为若入栈动作使地址向低端增长,称为“向下生成向下生成”的栈;的栈; 对于向上生成的栈对于向上生成的栈入栈入栈口诀:堆栈指针口诀:堆栈指针top先压后加(先压后加(vvtop+top+=x=x););出栈出栈口诀:堆栈指针口诀:堆栈指针top先减后弹(先减后弹(y=vy=v-top-top) 。3.1 栈栈补充补充2: 栈不存在的条件:栈不存在的条件: base=NULL; 栈为空栈为空 的条件的条件 : base=top; 栈满的条件栈满的条件 : top-base=stacksize;113补充补充3:链栈:链栈 链栈示意图链栈示意图s(栈底栈底)(栈顶栈顶)topa3a2a4a1114链栈的入栈函数、出栈函数链栈的入栈函数、出栈函数(以头指针为栈顶,在头指针处插入或删除!)(以头指针为栈顶,在头指针处插入或删除!)公共说明部分:公共说明部分: typedef struct snode SElemType data; struct snode *link;NODE;NODE *st, *p;int m=sizeof(NODE); 115Push (SElemType x) p=(NODE*)malloc(m); if(!p)上溢上溢else p-data=x; p-link=st; st=p; S Status pop( ) pop( ) if(st=NULL) if(st=NULL)下溢下溢 elsey=st-data;p=st;st=st-link; elsey=st-data;p=st;st=st-link; free(p); return(y);free(p); return(y); 链栈链栈入栈入栈函数函数链栈链栈出栈出栈函数函数插入插入表头表头从表头从表头删除删除116 链栈不必设头结点,因为栈顶(表头)操作链栈不必设头结点,因为栈顶(表头)操作链栈不必设头结点,因为栈顶(表头)操作链栈不必设头结点,因为栈顶(表头)操作频繁;频繁;频繁;频繁;采用链栈存储方式,可使多个栈共享空间;采用链栈存储方式,可使多个栈共享空间;采用链栈存储方式,可使多个栈共享空间;采用链栈存储方式,可使多个栈共享空间;当栈中元素个数变化较大,且存在多个栈的当栈中元素个数变化较大,且存在多个栈的当栈中元素个数变化较大,且存在多个栈的当栈中元素个数变化较大,且存在多个栈的情况下,链栈是栈的首选存储方式。情况下,链栈是栈的首选存储方式。情况下,链栈是栈的首选存储方式。情况下,链栈是栈的首选存储方式。说说 明明117问:为什么要设计堆栈?它有什么独特用途?问:为什么要设计堆栈?它有什么独特用途?1.调用函数或子程序非它莫属;调用函数或子程序非它莫属;2.递归运算的有力工具;递归运算的有力工具;3.用于保护现场和恢复现场;用于保护现场和恢复现场;4.简化了程序设计的问题简化了程序设计的问题。答:答:118 数制转换(十转数制转换(十转N) P48 设计思路:用栈暂存低位值设计思路:用栈暂存低位值例例2:括号匹配的检验括号匹配的检验P49 设计思路:用栈暂存左括号设计思路:用栈暂存左括号例例3 :表达式求值表达式求值 -P52 设计思路:用栈暂存运算符设计思路:用栈暂存运算符例例4:汉诺仪(汉诺仪(Hanoi)塔)塔-P55 设计思路:用栈实现递归调用设计思路:用栈实现递归调用例例1:119例例3 表达式求值表达式求值 ( 这是栈应用的典型例子这是栈应用的典型例子 ) 这里,这里,表达式求值的算法是表达式求值的算法是 “算符优先法算符优先法”。例如:例如:3*(7 2 ) (1)要正确求值,首先了解算术四则运算的规则:)要正确求值,首先了解算术四则运算的规则: a. 从左算到右从左算到右 b. 先乘除,后加减先乘除,后加减 c. 先括号内,后括号外先括号内,后括号外 由此,此表达式的计算顺序为:由此,此表达式的计算顺序为: 3*(7 2 )= 3 * 5 = 15120(2)根据上述三条运算规则,在运算的每一步中,对)根据上述三条运算规则,在运算的每一步中,对任意相继出现的算符任意相继出现的算符 1和和 2 ,都要比较优先权关系。,都要比较优先权关系。算符优先法所依据的算符间的优先关系算符优先法所依据的算符间的优先关系见教材见教材P53表表3.1 (是提供给计算机用的表!)(是提供给计算机用的表!)由表可看出,右括号由表可看出,右括号 ) 和井号和井号 # 作为作为 2时时级别最低;级别最低; 由由c 规则规则得出:得出: * ,/, + ,-为为 1时的优先权低于时的优先权低于(,高于,高于) 由由a规则规则得出:得出:(=) 表明括号内运算,已算表明括号内运算,已算完。完。 # = # 表明表达式求值完毕。表明表达式求值完毕。栈的应用(表达式求值)栈的应用(表达式求值)121(3)算法思想:)算法思想:设定两栈:设定两栈:操作符栈操作符栈 OPTR ,操作数栈操作数栈 OPND栈初始化栈初始化:设操作数栈:设操作数栈 OPND 为空;操作符栈为空;操作符栈 OPTR 的栈的栈底元素为表达式起始符底元素为表达式起始符 #;依次读入字符依次读入字符:是操作数则入:是操作数则入OPND栈,栈,是操作符则要判断:是操作符则要判断: if 操作符操作符 栈顶元素栈顶元素,压入,压入OPTR栈。栈。栈的应用(表达式求值)栈的应用(表达式求值)122栈的应用(表达式求值)栈的应用(表达式求值)OPTROPNDINPUTOPERATE3*(7-2)#Push(opnd,3) #*(7-2)#3#Push(optr,*)#,*3(7-2)#Push(optr,()#,*,(37-2)#Push(opnd,7)#,*,(3,7-2)#Push(optr,-)#,*,(,3,72)#Push(opnd,2)#,*,(,3,7,2)#Operate(7-2)#,*,(3,5)#Pop(optr)#,*3,5#Operate(3*5)#15#GetTop(opnd)123Status EvaluateExpression( OperandType &result) InitStack(OPND); InitStack(OPTR);Push(OPTR ,#);c=getchar(); while(c!=#)&(GetTop(OPTR)!=#) if (!In(c,OP) Push(OPND,c); c=getchar(); else switch(compare(c,GetTop(OPTR) case : Push(OPTR , c); c=getchar();break; case =: Pop(OPTR);c=getchar();break; case next=S; rear=S;出队(头部删除):出队(头部删除):front-next=p-next;完整动作设计参完整动作设计参见教材见教材P61-62 队列会满吗?队列会满吗?front=rear一般不会,因为删除时有一般不会,因为删除时有free动作。除非内存不足!动作。除非内存不足!1271)构造一个空队列2)Status InitQuene( LinkQuene &Q ) 3)4) Q.front = Q.rear = 5) ( QuenePtr ) malloc ( sizeof (QNode);6) if ( !Q.front ) exit ( OVERFLOW);7) Q.front next = NULL;8) return OK;9) 1282) 销毁队列Status DestroyQuene ( LinkQuene &Q) while ( Q.front ) Q.rear = Q.frontnext; free ( Q.front); Q.front = Q.rear; return OK;1293) 入队Status EnQuene ( LinkQuene &Q, QElemType e) /插入元素插入元素e为为Q的新的队尾元素的新的队尾元素 p = ( QuenePtr) malloc ( sizeof ( Qnode); if ( !p) exit (OVERFLOW); pdata = e; pnext = NULL; Q.rearnext = p; Q.rear = p; return OK; 1304) 出队Status DeQuene ( LinkQuene &Q, QElemType &e) /若队列不空,则删除若队列不空,则删除Q的队头元素,用的队头元素,用e返回其值,并返回返回其值,并返回OK; / 否则返回否则返回ERROR if ( Q.front = = Q.rear ) return ERROR; p = Q.front next; e = p data; Q.front next = p next; if ( Q.rear = = p) Q.rear = Q.front; free (p); return OK;131顺序队示意图顺序队示意图Q(队尾队尾)(队首队首)front a1 a2 a3data a40.99rear 队列会满吗?队列会满吗?很可能会,因为数组前很可能会,因为数组前端空间无法释放。因此端空间无法释放。因此数组应当有长度限制。数组应当有长度限制。 空队列的特征?空队列的特征? 约定:约定:front=rear队尾后地址队尾后地址入队入队(尾部插入尾部插入):vrear=e; rear+;出队出队(头部删除头部删除):x=vfront; front+;讨论:讨论:假溢假溢假溢假溢出出出出 有有缺缺陷陷 怎样实现入队和怎样实现入队和出队操作?出队操作?3.2 队列队列132问:什么叫问:什么叫“假溢出假溢出” ?如何解决?如何解决?答:答:在顺序队中,当尾指针已经到了数组在顺序队中,当尾指针已经到了数组的上界,不能再有入队操作,但其实数组的上界,不能再有入队操作,但其实数组中还有空位置,这就叫中还有空位置,这就叫“假溢出假溢出”。3.2 队列队列解决假溢出的途径解决假溢出的途径采用循环队列采用循环队列133a3a2a10123N-1rearfront循环队列(臆造)循环队列(臆造)循环队列(臆造)循环队列(臆造)顺序队列顺序队列顺序队列顺序队列a3a2a1frontrear 0 1 2 3 . .N-1新问题:新问题:在循环队列中,空队特征是在循环队列中,空队特征是front=rear;队满时也会有队满时也会有front=rear;判决条件将出现二义性!判决条件将出现二义性!解决方案有三:解决方案有三: 加设标志位,让删除动作使其为加设标志位,让删除动作使其为1,插入动作使其为插入动作使其为0,则可识别当前则可识别当前 front=rear 使用一个计数器记录队列中元素个数(即队列长度);使用一个计数器记录队列中元素个数(即队列长度); 人为浪费一个单元,令人为浪费一个单元,令队满特征为队满特征为front=(rear+1)%N;134队空条件队空条件 : front = rear (初始化时:初始化时:front = rear )队满条件队满条件: front = (rear+1) % N (N=maxsize)队列长度:队列长度:L=(Nrearfront)% N J2 J3J1 J4 J5frontrear 选用选用空闲单元法:空闲单元法:即即front和和rear之一指向实元素,另一指向空闲元素。之一指向实元素,另一指向空闲元素。 问问3: 在具有在具有n个单元的循个单元的循环队列中,队满时共有多少环队列中,队满时共有多少个元素?个元素? n-1个个5 问问2:左图中队列长度左图中队列长度L=?问问1:左图中队列容量左图中队列容量 maxsize N=?6 135注意: 循环队列中的循环队列中的“长度长度”到底是指总长度还是数据元到底是指总长度还是数据元素素个数?个数? 答:一般情况下的长度(答:一般情况下的长度(L)是指元素个数(不含头结)是指元素个数(不含头结点或空闲单元),而点或空闲单元),而maxsize N 才是指所有单元总才是指所有单元总 数,即数,即“容量容量”。136J6 J5J7J8 J9例例1: 一循环队列如下图所示,若先删除一循环队列如下图所示,若先删除4个元素,接着再个元素,接着再插入插入4个元素,请问队头和队尾指针分别指向哪个位置个元素,请问队头和队尾指针分别指向哪个位置? J2 J3J1 J4 J5frontrear解:解:由图可知,队头和队尾指针的初态分别为由图可知,队头和队尾指针的初态分别为front=1和和rear=6。frontrear删除删除4个元素后个元素后front=5;再插入;再插入4个元素后,个元素后,rear=4。 frontfrontfrontfrontfront137例例2 :数组数组n用来表示一个循环队列,用来表示一个循环队列,f 为当前队列头为当前队列头元素的元素的前一前一位置,位置,r 为队尾元素的位置。假定队列中元素的为队尾元素的位置。假定队列中元素的个数小于个数小于n,计算队列中元素的公式为,计算队列中元素的公式为:()() rf ()()(nfr)% n ()()nrf ()() (nrf)% n4种公式哪种合理?种公式哪种合理?当当 r f 时(时(A)合理;)合理;当当 r f 时(时(C)合理;)合理;分析分析 :综合综合2种情况,以种情况,以(D)的表达最为合理)的表达最为合理例例3:在一个循环队列中,若约定队首指针指向队首元素在一个循环队列中,若约定队首指针指向队首元素的的前一个前一个位置。那么,从循环队列中删除一个元素时,其位置。那么,从循环队列中删除一个元素时,其操作是操作是 先先 ,后,后 。 移动队首指针移动队首指针取出元素取出元素138问:为什么要设计队列?它有什么独特用途?问:为什么要设计队列?它有什么独特用途?1.离散事件的模拟(模拟事件发生的先后顺序);离散事件的模拟(模拟事件发生的先后顺序);2.操作系统中多道作业的处理(一个操作系统中多道作业的处理(一个CPU执行多个作执行多个作业);业);3.3. 简化程序设计。简化程序设计。答:答:循环队列的操作实现循环队列的操作实现见教材见教材P64P64139循环队列的操作实现循环队列的操作实现1)初始化一空队列初始化一空队列算法要求:生成一空队列算法要求:生成一空队列算法操作:为队列分配基本容量空间算法操作:为队列分配基本容量空间 设置队列为设置队列为空队列空队列,即即 front=rear=0(也可为任意单元,如(也可为任意单元,如 2,或,或 4);140算法:算法:Status InitQueue ( SqQueue &q )/初始化空循环队列初始化空循环队列 q q . base=(QElemType *)malloc(sizeof(QElemType)* QUEUE_MAXSIZE); /分配空间分配空间 if (!q.base) exit(OVERFLOW); /失败,退出程序失败,退出程序 q.front =q.rear=0; /置空队列置空队列 return OK; /InitQueue;新开单元的地址为新开单元的地址为0则表示内存不足则表示内存不足141算法说明:向循环队列的队尾插入一个元素算法说明:向循环队列的队尾插入一个元素分分 析析:(1) 插入前应当先判断队列是否满插入前应当先判断队列是否满 if ( q . rear + 1 ) % QUEUE_MAXSIZE )=q.front) return ERROR;(2)插入动作)插入动作 q.base q.rear = e; q.rear = ( q . rear + 1 ) % QUEUE_MAXSIZE; 2) 入队操作入队操作142Status EnQueue(SqQueue &q, QElemType e)/向循环队列向循环队列 q 的队尾加入一个元素的队尾加入一个元素 e if ( (q.rear+1) % QUEUE_MAXSIZE = = q.front ) return ERROR ; q.base q.rear = e; /存储存储 q.rear = ( q . rear + 1 ) % QUEUE_MAXSIZE;/指针后移指针后移 return OK; / EnQueue;2) 入队操作(完整函数)入队操作(完整函数)1433)出队操作)出队操作算法说明:删除队头元素,返回其值算法说明:删除队头元素,返回其值 e 分分 析:析: (1) 在删除前应当判断队列是否空;在删除前应当判断队列是否空; if (q.front = q.rear ) return ERROR; (2)插入动作分析;插入动作分析; 因为队头指针因为队头指针front指向队头元素的位置,所以:指向队头元素的位置,所以: e = q.base q.front ; q.front = ( q . front + 1 ) % QUEUE_MAXSIZE; 144Status DeQueue ( SqQueue &q, QElemType &e) /若队列不空,删除循环队列若队列不空,删除循环队列 q 的队头元素,的队头元素, /由由 e 返回其值,并返回返回其值,并返回OK if ( q.front = = q.rear ) return ERROR;/队列空队列空 e = q.base q.front ; /队头元素出队队头元素出队 q.front=(q.front+1) % QUEUE_MAXSIZE ; /指针后移指针后移 return OK; / DeQueue算法:算法:145讨论(代本章小结)讨论(代本章小结)线性表、栈与队的异同点线性表、栈与队的异同点相相同同点点:逻逻辑辑结结构构相相同同,都都是是线线性性的的;都都可可以以用用顺顺序序存存储储或或链链表表存存储储;栈栈和和队队列列是是两两种种特特殊殊的的线线性性表表,即即受受限限的的线线性表性表(只是对插入、删除运算加以限制)。(只是对插入、删除运算加以限制)。不同点:不同点: 运运算算规规则则不不同同,线线性性表表为为随随机机存存取取,而而栈栈是是只只允允许许在在一一端端进进行行插插入入和和删删除除运运算算,因因而而是是后后进进先先出出表表LIFOLIFO;队队列列是是只只允允许许在在一一端端进进行行插插入入、另另一一端端进进行行删删除除运运算算,因因而而是先进先出表是先进先出表FIFOFIFO。 用途不同,线性表比较通用;堆栈用于函数调用、递归用途不同,线性表比较通用;堆栈用于函数调用、递归和简化设计等;队列用于离散事件模拟、多道作业处理和简化设计等;队列用于离散事件模拟、多道作业处理和简化设计等。和简化设计等。146注:下次课前请完成注:下次课前请完成第第3 3章自测卷章自测卷全部内容,有关算全部内容,有关算法设计题,建议上机验证。法设计题,建议上机验证。147补充:补充:C语言中常用的串运算语言中常用的串运算串比较,串比较,int strcmp(chars1,char s2); / StrCompare(S,T) 求串长,求串长, int strlen(char s); / StrLength(S)串连接,串连接,char strcat(char to,char from) / Concat(&T, S1, S2) 子串子串T T定位,定位,char strchr(char s,char c); / Index(S, T, pos) Concat concatenation,在字符串处理中,在字符串处理中,把多个短字符串合成为长字符把多个短字符串合成为长字符串的操作。串的操作。注:用注:用C处理字符串时,要调用标准库函数处理字符串时,要调用标准库函数 #include148数据结构课程的内容数据结构课程的内容149第第4章章 串串(String)4.2 4.2 串的表示和实现串的表示和实现4.3 4.3 串的模式匹配算法串的模式匹配算法1. 定义定义2. 逻辑结构逻辑结构3. 存储结构存储结构4. 运算规则运算规则5. 实现方式实现方式4.1 4.1 串类型的定义串类型的定义150记为:记为: s = a1 , a2 , . , an (n0 ) 串名串名串值(用串值(用 括起来)括起来)串中字符个数(串中字符个数(n0n0). n=0 . n=0 时称为空串时称为空串 。由一个或多个空格符组成的串。由一个或多个空格符组成的串。串串s s中任意个连续的字符序列叫中任意个连续的字符序列叫s s的子串的子串; S; S叫叫主串主串。子串的第一个字符的序号。子串的第一个字符的序号。字符在串中的序号。字符在串中的序号。串长度相等,且对应位置上字符相等。串长度相等,且对应位置上字符相等。 串串即字符串,是由即字符串,是由零个或多个零个或多个字符组成的有限序列,是数据字符组成的有限序列,是数据元素为单个字符的元素为单个字符的特殊线性表特殊线性表。4.1 4.1 串类型的定义串类型的定义若干术语:若干术语: 串长:串长: 空白串:空白串: 子串:子串:子串位置:子串位置:字符位置:字符位置: 串相等:串相等:隐含结束符隐含结束符/0 /0 ,即,即ASCIIASCII码码NULLNULL151练练1 1:串是由串是由 字符组成的序列,一般记为字符组成的序列,一般记为 。练练2 2:现有以下现有以下4 4个字符串:个字符串: a =BEI b =JING c = BEIJING d = BEI JING问:问: 他们各自的长度?他们各自的长度? a a是哪个串的子串?在主串中的位置是多少?是哪个串的子串?在主串中的位置是多少?a =3,b =4,c = 7,d=8a是是c和和d的子串,在的子串,在c和和d中的位置都是中的位置都是1练练3 3:空串和空白串有无区别?空串和空白串有无区别?答:答:有区别。空串有区别。空串(Null String)(Null String)是指长度为零的串;是指长度为零的串; 而空白串而空白串(Blank String),(Blank String),是指包含一个或多个是指包含一个或多个 空白字符空白字符 ( (空格键空格键) )的字符串的字符串. .0 0个或多个个或多个S=a1a2an152ADT StingObjects: D=ai | aiCharacterSet, i=1, 2,,n, n0Relations: R1= | ai-1,ai D, i=2, ,nfunctions: / 有有1313种之多种之多StrAssign(&T, chars) / 串赋值,生成值为串赋值,生成值为charschars的串的串T TStrCompare(S,T) / 串比较,若串比较,若STST,返回值大于,返回值大于00 StrLength(S) / 求串长,即返回求串长,即返回S S的元素个数的元素个数 Concat(&T, S1, S2) / 串连接,用串连接,用T T返回返回S1S1S2S2的新串的新串SubString(&Sub, S, pos, len) / 求求S S中中pospos起长度为起长度为lenlen的子的子串串Index(S, T, pos) / 返回子串返回子串T T在在pospos之后的位置之后的位置 Replace(&S, T,V) / 用子串用子串V V替换子串替换子串T TADT Sting串的抽象数据类型定义(串的抽象数据类型定义(参见教材参见教材P71)最最小小操操作作子子集集153 设设 s =I AM A STUDENT, t =GOOD, q=WORKER。求:。求:练习:练习: StrLength(s) StrLength(t) SubString(s, 8, 7)= SubString(t, 2, 1)= Index(s, A)= Index(s, t)=Replace(s, STUDENT,q)=144STUDENTO30 ( s中没有中没有t!)!)I AM A WORKER再问:再问:Concat(SubString(s,6,2), Concat(t,SubString(s,7,8) ? A GOOD STUDENT1544.2串的表示和实现串的表示和实现定长顺序存储表示定长顺序存储表示用一组地址连续的存储单元存储串值的字用一组地址连续的存储单元存储串值的字符序列。符序列。堆分配存储表示堆分配存储表示用一组地址连续的存储单元存储串值的字用一组地址连续的存储单元存储串值的字符序列符序列, ,但存储空间是在程序执行过程中动态但存储空间是在程序执行过程中动态分配而得。分配而得。串的块链存储表示串的块链存储表示链式方式存储链式方式存储首先强调:首先强调:串与线性表的运算有所不同,是以串与线性表的运算有所不同,是以“串的整体串的整体”作作为操作对象,例如查找某子串,在主串某位置上插入一个子串为操作对象,例如查找某子串,在主串某位置上插入一个子串等。等。串有三种机内表示方法:串有三种机内表示方法:顺序顺序存储存储链式链式存储存储1551.1.定长顺序存储特点:定长顺序存储特点: 用一组连续的存储单元来存放串,直接使用定长的字符数用一组连续的存储单元来存放串,直接使用定长的字符数组来定义,数组的组来定义,数组的上界预先给出上界预先给出,故称为静态存储分配。,故称为静态存储分配。例如:例如:#define Maxstrlen 255 /用户可用的最大串长用户可用的最大串长 typedef unsigned char SString Maxstrlen1 ; SString s; /s是一个可容纳是一个可容纳255个字符的顺序串。个字符的顺序串。注:注: 一般用一般用SString0来存放串长信息;来存放串长信息;C语言约定在串尾加结束符语言约定在串尾加结束符 0,以利操作加速,但不计入串,以利操作加速,但不计入串长;长;若字符串超过若字符串超过Maxstrlen 则自动截断(因为静态数组存不则自动截断(因为静态数组存不 进去)。进去)。 实现方式:参见教材实现方式:参见教材P73编程两例,两串连接和编程两例,两串连接和求子串求子串1561) 1) 串连接串连接Concat(&T, S1,S2)Status Concat( Sstring &T, Sstring S2) if ( S10 +S20 = MAXSTRLEN) / / 未截断未截断 T1.S10 = S1 1.S10; T S10+1 . S10+S20 = S2 1 . S20 ; T0 = S10 + S20; uncut = TRUE; else if ( S10 MAXSTRSIZE ) / / 截断截断 T 1.S10 = S1 1.S10 ; T S10 +1 . MAXSTRLEN = S2 1. MAXSTRLEN- S10; T0 = MAXSTRLEN; uncut = FALSE; else T 0. MAXSTRLEN = S1 0. MAXSTRLEN ; / / 截取(仅取截取(仅取S1S1) uncut = FALSE; return uncut; / Concat 1572) 2) 求子串函数求子串函数SubString (&Sub, S, pos, len) Status SubString (SString &sub, SString S, int pos, int len ) if (posS0 | lenS0-pos+1) return ERROR; /pos/pos不合法则不合法则警警告告 Sub1len = Spospos+len-1; Sub0=len; return OK; 将串将串S S中从第中从第pospos个字符开始长度为个字符开始长度为lenlen的字符序列复的字符序列复制到串制到串SubSub中中(注:串(注:串SubSub的预留长度与的预留长度与S S一样)一样)s = a1 , a2 , . , ann串长串长poslen158讨论:讨论:想存放超长字符串怎么办?想存放超长字符串怎么办?静态数组有缺陷!静态数组有缺陷!改用改用动态分配动态分配的一维数组的一维数组“堆堆”!159思路:思路:利用利用mallocmalloc函数合理预设串长空间。函数合理预设串长空间。特点:特点: 若在操作中串值改变,还可以利用若在操作中串值改变,还可以利用reallocrealloc函数按新函数按新串长度串长度增加增加( (堆砌堆砌) )空间。空间。Typedef struct char *ch; / / 若非空串若非空串, ,按串长分配空间按串长分配空间; ; 否则否则 ch = NULL ch = NULLint length; /串长度串长度HString2.2.堆分配存储特点:堆分配存储特点:仍用一组连续的存储单元来存放仍用一组连续的存储单元来存放串,但存储空间是在程序执行过程中动态分配而得。串,但存储空间是在程序执行过程中动态分配而得。约定:约定:所有按堆存储的串,其关键信息放置在:所有按堆存储的串,其关键信息放置在:160Status StrInsert ( HString &S, int pos, HString T ) /在串在串S S的第的第pospos个字符之前(包括尾部)插入串个字符之前(包括尾部)插入串T Tif (posS.length+1) return ERROR; /pos/pos不合法则告警不合法则告警 if(T.length) /只要串只要串T T不空,就需要重新分配不空,就需要重新分配S S空间,以便插入空间,以便插入T T if (!(S.ch=(char*)realloc(S.ch, (S.length+T.length)*sizeof(char) ) exit(OVERFLOW); for ( i=S.length-1; i=pos-1; -i ) /为插入为插入T T而腾出而腾出pospos之后的位置之后的位置 S.chi+T.length = S.chi; /从从S S的的pospos位置起全部字符均后移位置起全部字符均后移 S.chpos-1pos+T.length-2 = T.ch0T.length-1; /插入插入T T,略,略/0/0 S.length + = T.length; /刷新刷新S串长度串长度 return OK;/StrInsert1 1)用)用“堆堆”实现串插入操作实现串插入操作( (教材教材P75)P75) 161Status StrAssign( HString &T, char *chars ) if (T.ch) free(T.ch);for (i=0, c=chars; c; +i, +c); /求串长度求串长度if (!i) T.ch = NULL; T.length = 0;else if (!(T.ch = (char*)malloc(i*sizeof(char) exit(OVERFLOW); T.ch0.i-1 = chars0.i-1; T.length =i;Return OK;/StrAssign指针变量指针变量C也可以自也可以自增!意即每次后移一增!意即每次后移一个数据单元。个数据单元。2) 堆分配存储表示堆分配存储表示直到终值为直到终值为“假假”停止,串尾特停止,串尾特征是征是/0NULL=01623) 比较字符串是否相同比较字符串是否相同Int Strcompare ( Hstring S, Hstring T ) for ( i = 0; i S.length & i T.length; +i ) if ( S.chi != T.chi ) return S.ch i T.chi; return S.length- T.length; / StrCompare 4) 清空字符串清空字符串 Status ClearString ( Hstring &S) if ( S.ch ) free(S.ch); S.ch = NULL; S.length = 0; return OK; / ClearString 1635) 5) 联接两个串成新串联接两个串成新串Status Concat ( HString &T, Hstring S1, Hstring S2 ) / / 用用T T返回由返回由S1S1和和S2S2联接而成的新串。联接而成的新串。 if (T.ch) free(T.ch); / / 释放旧空间释放旧空间 if ( !(T.ch = (char *) malloc (S1.length+S2.length) * sizeof (char) ) ) ) exit ( OVERFLOW); T.ch0 . S1.length-1 = S1.ch0 . S1.length-1; T.length = S1.length + S2.length ; T.chS1.length . T.length-1 = S2.ch0 . S2.length-1; return OK; / Concat 1646 6)求子串)求子串 Status SubString ( Hstring &Sub, Hstring S,int len ) / / 用用SubSub返回串返回串S S的第的第pospos个字符起长度为个字符起长度为lenlen的子串。的子串。/ 其中其中,1=pos= StrLength (S) 且且 0=len=StrLength(S)-pos+1。 if ( pos S.length | lenS.length-pos+1) return ERROR; / / 参数不合法参数不合法 if ( Sub.ch) free ( Sub.ch); / / 释放旧空间释放旧空间 if (!len) Sub.ch = NULL; Sub.length = 0; / / 空子串空子串 else / / 完整子串完整子串 Sub.ch = ( char *) malloc ( len *sizeof ( char ); Sub.ch0.len-1 = S.ch pos-1. Pos+len-2; Sub.length = len; return OK;165讨论:讨论:法法1 1存储密度为存储密度为 ;法;法2 2存储密度为存储密度为 ;显然,若显然,若数据元素很多,用法数据元素很多,用法2 2存储更优存储更优称为称为块链结构块链结构3.3.链式存储特点链式存储特点 :用链表存储串值,易插入和删除。用链表存储串值,易插入和删除。法法1 1:链表结点(数据域)大小取链表结点(数据域)大小取1 1法法2 2:链表结点(数据域)大小取链表结点(数据域)大小取n(n(例如例如n=4)n=4)1/21/29/15=3/59/15=3/5 A B C I NULLheadheadA B C D E F G H I # # # NULL166#define CHUNKSIZE 80 /可由用户定义的块大小可由用户定义的块大小typedef struct Chunk /首先定义结点类型首先定义结点类型 char ch CHUNKSIZE ; /结点中的数据域结点中的数据域 struct Chunk * next ; /结点中的指针域结点中的指针域Chunk; 块链类型定义:块链类型定义:例略例略typedef struct /其次定义用链式存储的串类型其次定义用链式存储的串类型 Chunk *head; /头指针头指针 Chunk *tail; /尾指针尾指针 int curLen; /结点个数结点个数 Lstring; /串类型只用一次,前面可以不加串类型只用一次,前面可以不加LstringLstring167再次强调:再次强调: 串与线性表的运算有所不同,是以串与线性表的运算有所不同,是以“串的整体串的整体”作为操作对象,例如查找某子串,在主串某位置上插作为操作对象,例如查找某子串,在主串某位置上插入一个子串等。入一个子串等。 这类操作中均涉及到这类操作中均涉及到定位问题定位问题,称为,称为串的模式匹配串的模式匹配。它是串处理系统中最重要的操作之一。它是串处理系统中最重要的操作之一。1684.3 串的模式匹配算法串的模式匹配算法模式匹配模式匹配(Pattern Matching) (Pattern Matching) 即即子串定位运算(子串定位运算(IndexIndex函数)函数)。算法目的:算法目的:确定主串中所含子串第一次出现的位置(定位)确定主串中所含子串第一次出现的位置(定位) 即如何实现即如何实现 Index(S,T,pos) Index(S,T,pos)函数(见教材函数(见教材P71P71)初始条件:初始条件:串串S S和和T T存在,存在,T T是非空串,是非空串,1posStrLength(s)1posStrLength(s)操作结果:操作结果:若主串若主串S S中存在和串中存在和串T T值相同的子串,则返回它在主值相同的子串,则返回它在主串串S S中第中第pospos个字符之后第一次出现的位置;否则函数值为个字符之后第一次出现的位置;否则函数值为0 0。注:注:S S称为称为被匹配的串被匹配的串,T T称为称为模式串。模式串。若若S S包含串包含串T T,则称,则称 “ “匹配成功匹配成功”,否则称否则称 “ “匹配不成功匹配不成功” ” 。169 BF BF算法设计思想:算法设计思想:将主串的第将主串的第pospos个字符和模式的第个字符和模式的第1 1个字符比较,个字符比较, 若若相等相等,继续逐个比较后续字符;,继续逐个比较后续字符; 若若不等不等,从主串的下一字符(,从主串的下一字符(pos+1pos+1)起,重新与第一个起,重新与第一个字符比较。字符比较。 BF算法算法 (又称古典或经典的、朴素的、穷举的)(又称古典或经典的、朴素的、穷举的) KMP算法算法(特点:速度快)(特点:速度快)算法算法种类:种类:直到主串的一个连续子串字符序列与模式相等直到主串的一个连续子串字符序列与模式相等 。返回值。返回值为为S S中与中与T T匹配的子序列匹配的子序列第一个字符的序号第一个字符的序号,即匹配成功。,即匹配成功。 否则,匹配失败,返回值否则,匹配失败,返回值 0 .0 .S=a b a b c a b c a c b a bT=T=a b c a cpos=5170Int Index(SString S, SString T, int pos) i=pos; j=1; while ( i=S0 & jT0) return i-T0; /子串结束,说明匹配成功子串结束,说明匹配成功 else return0;/Index BF BF算法的实现算法的实现即即Index()操作的实现()操作的实现 (见教材(见教材P79) S=a b a b c a b c a c b a bT=T=a b c a cpos=5相当于子串向右滑动一个字符位置相当于子串向右滑动一个字符位置匹配成功后指针仍要回溯!因为要返回的是被匹匹配成功后指针仍要回溯!因为要返回的是被匹配的首个字符位置。配的首个字符位置。i ij j171讨论:讨论:若若n n为主串长度,为主串长度,m m为子串长度,则串的为子串长度,则串的BFBF匹配算法匹配算法最坏的情况最坏的情况下需要比较字符的总次数为下需要比较字符的总次数为(n-m+1)*mO(n*m)最恶劣情况是:最恶劣情况是:主串中前面主串中前面n-mn-m个位置都个位置都部分匹配部分匹配到子串的最到子串的最后一位时出现不等,此时需要将指针后一位时出现不等,此时需要将指针i i回溯,并从模式的第一回溯,并从模式的第一个字符开始重新比较,整个匹配过程中,指针个字符开始重新比较,整个匹配过程中,指针i i需回溯(需回溯(n-m)n-m)次,则次,则whilewhile循环次数为(循环次数为(n-m+1)*mn-m+1)*m。BFBF匹配算法的最坏时间复杂度匹配算法的最坏时间复杂度但一般情况下但一般情况下BFBF算法的时间复杂度为算法的时间复杂度为O(n+m)O(n+m)172KMP算法算法(特点:速度快)(特点:速度快) KMPKMP算法设计思想算法设计思想 KMP算法的推导过程算法的推导过程 KMPKMP算法的实现算法的实现 (关键技术(关键技术: :计算计算nextjnextj) KMPKMP算法的算法的时间复杂度时间复杂度173能否利用已经部分匹配的结果而加快模式串的滑动速度?能否利用已经部分匹配的结果而加快模式串的滑动速度?能!而且主串能!而且主串S S的指针的指针i i不必回溯!可提速到不必回溯!可提速到O(n+m)O(n+m)!例:例: KMP KMP算法设计思想:算法设计思想: ( (参见教材参见教材P80-84P80-84)S=a b a b c a b c a c b a bT=T=a b c a cS=a b a b c a b c a c b a bT=T=a b c a cS=a b a b c a b c a c b a bT=T=a b c a cIndex_kmpIndex_kmp的返回值应为的返回值应为i=6i=6需要讨论两个问题:需要讨论两个问题: 如何如何“记忆记忆”部分匹配结果?部分匹配结果? 如何由如何由“记忆记忆”结果计算出主串结果计算出主串S S第第i i个字符应该与模式个字符应该与模式T T中中哪个字符再比较?即确定模式哪个字符再比较?即确定模式T T中的新比较起点中的新比较起点k k. .i ii ii ik kk k a b aa b c174 KMP算法的推导过程:算法的推导过程:(见教材(见教材P81)抓住部分匹配结果的两个特征:抓住部分匹配结果的两个特征:两式联立可得:两式联立可得:T1Tk-1=Tj-(k-1)j-(k-1) Tj-1注意:注意:j 为当前已知的失配位置,我们的目标是计算新起点为当前已知的失配位置,我们的目标是计算新起点 k,仅剩一个未知数仅剩一个未知数k,理论上已可解,且,理论上已可解,且k仅与模式串仅与模式串T有关!有关! 则则S S前前i-1i-1i-(k-1)i-(k-1)位位T T的的j-1j-1j-(k-1)j-(k-1)位位 即即(4-3(4-3)式含义)式含义S=a b a b c a b c a c b a bT=T=a b c a ci ik k则则T T的的k-1k-11 1位位S S前前i-1i-1i-(k-1)i-(k-1)位位 即即(4-2(4-2)式含义)式含义i ik kj jS=a b a b c a b c a c b a bT=T=a b c a c刚才肯定是在刚才肯定是在S的的i处和处和T的第的第j字符字符 处失配处失配设目前应与设目前应与T的第的第k字符开始比较字符开始比较175 KMP算法的推导过程算法的推导过程(续):续):根据模式串根据模式串T的规律:的规律: T1Tk-1=Tj-(k-1)j-(k-1) Tj-1和已知的当前失配位置和已知的当前失配位置j ,可以归纳出计算新起点,可以归纳出计算新起点 k的表达式。的表达式。令令k = next j ,则,则next j 0 当当j1时时max k |1kj 且且T1Tk-1=Tj-(k-1)j-(k-1) Tj-1 1 其他情况其他情况讨论:讨论: next j 有何意义?有何意义? 一旦失配,应从模式串一旦失配,应从模式串T中第中第next j 个字符开始与个字符开始与S的失配点的失配点i 重新匹配重新匹配! next j 怎么求?怎么求? 后面会举例(参见教材后面会举例(参见教材P81)176怎样计算模式怎样计算模式怎样计算模式怎样计算模式T T所有可能的失配点所有可能的失配点所有可能的失配点所有可能的失配点 j j 所对应的所对应的所对应的所对应的 nextj nextj?例:模例:模 式式 串串 T: a b a a b c a c 可能失配位可能失配位 j: 1 2 3 4 5 6 7 8新匹配位新匹配位 nextj :next j 0 当当j1时时max k |1kj 且且T1Tk-1=Tj-(k-1)j-(k-1) Tj-1 1 其他情况其他情况0 1 1 2 2 3 1 2讨论:讨论:j=1时时, next j 0;因为属于因为属于“j=1”;j=2时时, next j 1;因为属于因为属于“其他情况其他情况”;刚才已归纳:刚才已归纳:j=3时时, k=2,只需查看,只需查看T1 T2 2;j=4时时, k=2,3,要查看,要查看T1=T3 3 和和T1T2 T2 2 T3 3 j=5时时, k=2,3,4,要查看,要查看T1=T4 4 、T1T2 T3 3T4 和和 T1T2T3 3 T2 2T3 3T4以此类推,可得后续以此类推,可得后续nextj值。值。177第一步,先第一步,先把模式把模式T所有可能的失配点所有可能的失配点j所对应的所对应的nextj计算出来计算出来;第二步:执行定位函数第二步:执行定位函数index_kmp (与(与BF算法模块非常相似)算法模块非常相似) KMP KMP算法的实现算法的实现即即Index( )操作的实现操作的实现 (见教材(见教材P82) Int Index_KMP(SString S, SString T, int pos) i=pos; j=1; while ( i=S0 & jT0) return i-T0; /子串结束,说明匹配成功子串结束,说明匹配成功 else return0;/Index_KMP178void get_next (SString T, int &next ) /next函数值存入数组函数值存入数组next i=1; next1=0; j=0; while(iT0 ) if ( j= = 0|Ti= = Tj ) +i; +j; nexti=j; else j=nextj; / while/ get_next i 1 2 3 4 5 6 7 8模式模式 a b a a b c a cj=Nexti 0 1 1 2 2 3 1 2求解求解nextj 算法的流程图算法的流程图:179i=1; j=0next1=0iT0j=0 | Ti=Tj+i; +j;nexti=j;j=nextj;ENDYYNN附:求解附:求解nextj 算法流程图算法流程图:180模式:模式: a a a a bj:1 2 3 4 5 nextj: 0 1 2 3 4S: a a a b a a a a b T: a a a a b i: 1 2 3 4 5 6 7 8 9 a a a a ba a a a ba a a a ba a a a b next j 是否完美无缺?是否完美无缺?答:未必,例如当答:未必,例如当 S=a b a a a a b,T=a a a a b时时 ,仍有仍有 多余动作(参见多余动作(参见P84改进算法改进算法,称为称为nextval j )181当当P Pj j=P=Pnextjnextj时,则时,则如果如果S Si i != P != Pj j,= S Si i != P!= Pnextjnextj因此,因此,S Si i 没有必要继续与没有必要继续与 P Pnextjnextj进行比较,进行比较,而应该直接和而应该直接和P Pnextjnextj的下一个字符的下一个字符P Pnextnextjnextnextj进行比较。进行比较。因此,在计算因此,在计算nextnext函数时,函数时,如果出现如果出现P Pj j=P=Pnextj nextj = = P Pk k则则nextj=nextk=nextnextjnextj=nextk=nextnextj修改算法见教材修改算法见教材P84 P84 算法算法4.8 4.8 此时效率不高的原因为:此时效率不高的原因为:182void get_nextval(SString T, int &nextval ) /next函数修正值存入数组函数修正值存入数组nextval i=1; nextval1=0; j=0; while ( iT0 ) if (j= = 0 | Ti= =Tj ) +i;+j; If (Ti!=Tj ) nextvali=j; else nextvali = nextvalj; else j=nextvalj; / while/ get_nextval i 1 2 3 4 5模式模式 a a a a bj=Nexti 0 1 2 3 4Nextvali 0 0 0 0 4183 KMP KMP算法的算法的时间复杂度时间复杂度注意:注意:由于由于BFBF算法在一般情况下的时间复杂度也近似于算法在一般情况下的时间复杂度也近似于O(n+m)O(n+m), 所以至今仍被采用。所以至今仍被采用。而此时而此时KMPKMP的情况的情况是:由于指针是:由于指针i i无须回溯,比较次数仅为无须回溯,比较次数仅为n,n,即使加上计算即使加上计算nextjnextj时所用的比较次数时所用的比较次数m m,比较总次数也仅,比较总次数也仅为为n+m=n+m=O(nm),大大快于大大快于BFBF算法。算法。回顾回顾BFBF的最恶劣情况的最恶劣情况:S S与与T T之间存在大量的部分匹配,比较总之间存在大量的部分匹配,比较总次数为:次数为: (n-m+1)*mO(n*m)因为主串指针因为主串指针i i不必回溯,所以从外存输入文件时可以做不必回溯,所以从外存输入文件时可以做到边读入边查找,到边读入边查找,“流式流式”作业!作业!KMPKMP算法的用途:算法的用途:算法的用途:算法的用途:本章结束本章结束184字符串字符串ababaaabababaaab 的的 nextval nextval 函数值函数值为为 _ _ 。模式串模式串 P= P= abaabcacabaabcac 的的 next next 函函数值序列为数值序列为 _ _ 。01122312011223120101042101010421185数据结构课程的内容数据结构课程的内容186第第5章章 数组和广义表(数组和广义表(Arrays & Lists) 元素的值并非原子类型,可以再分解,表中元素也是一元素的值并非原子类型,可以再分解,表中元素也是一个线性表(即广义的线性表)。个线性表(即广义的线性表)。 所有数据元素仍属所有数据元素仍属同一数据类型同一数据类型。5.1 数组的定义数组的定义5.2 数组的顺序表示和实现数组的顺序表示和实现5.3 矩阵的压缩存储矩阵的压缩存储(即数组的应用即数组的应用)5.4 广义表的定义广义表的定义5.5 广义表的存储结构广义表的存储结构数组和广义表的特点:数组和广义表的特点:一种特殊的线性表一种特殊的线性表1875.1 数组的定义数组的定义 数组:数组: 由一组名字相同、下标不同的变量构成。由一组名字相同、下标不同的变量构成。注意:注意: 本章所讨论的数组与高级语言中的数组有所区别:本章所讨论的数组与高级语言中的数组有所区别:高级语言中的数组是顺序结构;而高级语言中的数组是顺序结构;而本章的数组既可以是顺序本章的数组既可以是顺序的,也可以是链式结构的,也可以是链式结构,用户可根据需要选择。,用户可根据需要选择。答:答:对的对的。因为:。因为: 数组中各元素具有数组中各元素具有统一的类型统一的类型; 数组元素的下标一般具有数组元素的下标一般具有固定的上界和下界固定的上界和下界,即数组一,即数组一旦被定义,它的维数和维界就不再改变。旦被定义,它的维数和维界就不再改变。数组的数组的基本操作比较简单基本操作比较简单,除了结构的初始化和销毁之,除了结构的初始化和销毁之外,只有存取元素和修改元素值的操作。外,只有存取元素和修改元素值的操作。讨论:讨论:“数组的处理比其它复杂的结构要简单数组的处理比其它复杂的结构要简单”,对吗,对吗?188二维数组的特点:二维数组的特点:一维数组的特点:一维数组的特点:1 1个下标,个下标,a ai i 是是a ai+1i+1的直接前驱的直接前驱2 2个下标,个下标,每个元素每个元素ai,j受到两个关系受到两个关系(行关系和列关系)的约束:(行关系和列关系)的约束:一个一个mn的二维数组可以的二维数组可以看成是看成是m行的一维数组,或行的一维数组,或者者n列的一维数组。列的一维数组。(见课本(见课本P91图图5.1)N N维数组的特点:维数组的特点:n n个下标,个下标,每个元素受到每个元素受到n n个关系约束。个关系约束。一个一个n维数组可以看成是维数组可以看成是由若干个由若干个n1维数组组成的线性表。维数组组成的线性表。 a11 a12 a1n a21 a22 a2n am1 am2 amn Amn=189N维数组的数据类型定义维数组的数据类型定义n_ARRAY = (D, R)其中: Ri = | aj1,j2,jijn , aj1,j2,ji+1jn D 数据关系:数据关系:R = R1 ,R2,. Rn 数据对象:数据对象:D =D =aj1,j2jn| |j ji i为数组元素的第为数组元素的第i i 维下标维下标,aj1,j2jn Elemset 数组的抽象数据类型定义(数组的抽象数据类型定义(略)略),参见教材参见教材P90P90构造数组、销毁数组、读数组元素、写数组元素构造数组、销毁数组、读数组元素、写数组元素基本操作:基本操作:1905.2 数组的顺序存储表示和实现数组的顺序存储表示和实现问题:问题:计算机的存储结构是一维的,而数组一般是多维的,计算机的存储结构是一维的,而数组一般是多维的,怎样存放?怎样存放?解决办法:解决办法:事先约定按某种次序将数组元素排成一列序列,事先约定按某种次序将数组元素排成一列序列, 然后将这个线性序列存入存储器中。然后将这个线性序列存入存储器中。例如:例如:在二维数组中,我们既可以规定按在二维数组中,我们既可以规定按行行存储,也可以规存储,也可以规定按定按列列存储存储。注意:注意:若规定好了次序,则数组中任意一个元素的存放地址便若规定好了次序,则数组中任意一个元素的存放地址便有规律可寻,可形成地址计算公式;有规律可寻,可形成地址计算公式;约定的次序不同,则计算元素地址的公式也有所不同;约定的次序不同,则计算元素地址的公式也有所不同;C C和和PASCALPASCAL中一般采用中一般采用行行优先顺序;优先顺序;FORTRANFORTRAN采用采用列列优先。优先。191补充:补充:补充:补充:计算二维数组元素地址的通式计算二维数组元素地址的通式计算二维数组元素地址的通式计算二维数组元素地址的通式设一般的二维数组是设一般的二维数组是AcAc1 1.d.d1 1, c, c2 2.d.d2 2 ,这里这里c c1 1,c,c2 2不一定是不一定是0 0。无论规定行优先或列优先,只要知道以下三要素便可随时求出无论规定行优先或列优先,只要知道以下三要素便可随时求出任一元素的地址(任一元素的地址(这样数组中的任一元素便可以随机存取!这样数组中的任一元素便可以随机存取!):二维数组二维数组列优先列优先存储的通式为:存储的通式为:LOC(aij)=LOC(ac1,c2)+(j-c2)*(d1-c1+1)+i-c1)*L ac1,c2 ac1,d2 aij ad1,c2 ad1,d2 Amn=单个元素单个元素长度长度aij之前的之前的行数行数数组基址数组基址总列数,即总列数,即第第2 2维长度维长度aij本行前面的本行前面的元素个数元素个数开始结点的存放地址(即基地址)开始结点的存放地址(即基地址)维数和每维的上、下界;维数和每维的上、下界;每个数组元素所占用的单元数每个数组元素所占用的单元数则则行优先行优先存储时的地址公式为:存储时的地址公式为:LOC(aij)=LOC(ac1,c2) + (i-c1)*(d2-c2+1)+j-c2)*L192例例2 2:已知二维数组已知二维数组A Am,mm,m按行存储的元素地址公式是:按行存储的元素地址公式是: Loc(a Loc(aijij)= Loc(a)= Loc(a1111)+(i-1)*m+(j-1)*K , )+(i-1)*m+(j-1)*K , 按列存储的公式是?按列存储的公式是? Loc(aij)=Loc(a11)+(j-1)*m+(i-1)*K (尽管是方阵,但公式仍不(尽管是方阵,但公式仍不同)同)例例1 1软考题软考题:一个二维数组一个二维数组A A,行下标的范围是,行下标的范围是1 1到到6 6,列,列下标的范围是下标的范围是0 0到到7 7,每个数组元素用相邻的,每个数组元素用相邻的6 6个字节存储,存个字节存储,存储器按字节编址。那么,这个数组的体积是储器按字节编址。那么,这个数组的体积是 个字节。个字节。 288例例3:0000年计算机系考研题年计算机系考研题设数组设数组a160, 170a160, 170的基地的基地址为址为20482048,每个元素占,每个元素占2 2个存储单元,若以列序为主序顺序存储,个存储单元,若以列序为主序顺序存储,则元素则元素a32,58a32,58的存储地址为的存储地址为 。8950LOC(aij)=LOC(ac1,c2)+(j-c2)*(d1-c1+1)+i-c1)*L得:得:LOC(a32,58)=2048+(58-1)*(60-1+1)+32-1)*28950答:请注意审题!答:请注意审题! 利用列优先通式:利用列优先通式:答:答: Volume=m*n*L=(6-1+1)*(7- 0 +1)*6=48*6=288193Loc(jLoc(j1 1,j,j2 2,j,jn n)=LOC(0,0,0)=LOC(0,0,0)若是若是N维数组,其中任一元素的地址该如何计算?维数组,其中任一元素的地址该如何计算?其中其中Cn=L, Ci-1=biCi, 1in Ci = bi+1 bi+2 bn L一个元一个元素长度素长度数组基址数组基址前面若干元素占用前面若干元素占用的地址字节总数的地址字节总数第第i i维长度维长度与所存元素个数有关的系与所存元素个数有关的系数,可用递推法求出数,可用递推法求出教材已给出教材已给出低维低维优先的地址计算公式,优先的地址计算公式,见见P93P93(5-25-2)式)式该式称为该式称为n n维数组的映像函数维数组的映像函数: C i = b i+1 b i+2 b n L194#define MAX_ARRAY_DIM 8 /假设最大维数为假设最大维数为8 8 typedef struct ELemType *base; /数组元素基址数组元素基址 int dim; /数组维数数组维数 int *bound; /数组数组各维长度信息保存区各维长度信息保存区基址基址 int *constants; /数组数组映像函数常量映像函数常量的基址的基址 Array;即即Ci信息保存区信息保存区数组的基本操作函数说明(有数组的基本操作函数说明(有5个)个)(请阅读教材(请阅读教材P93-95P93-95)N维数组的顺序存储表示维数组的顺序存储表示(见教材见教材P93P93)以以销毁数组销毁数组函数为例函数为例195 :利用宏利用宏va_start、va_arg和和va_end提供提供遍历未知数目和类型的函数参数表的功能。遍历未知数目和类型的函数参数表的功能。Va_start ( va_list ap, x ):初始化初始化ap,使其指向所在函,使其指向所在函数的参数数的参数x x之后的第一个参数。之后的第一个参数。Va_arg ( va_list ap , 类型类型):返回返回apap当前指向的参数当前指向的参数的值,并修改的值,并修改ap,使得,使得ap指向下一个参数(指向下一个参数(“类型类型”为参数类型)。为参数类型)。Va_end ( va_list ap):用在所有的参数处理完毕之后,用在所有的参数处理完毕之后,表示表示ap使用完毕。使用完毕。几个函数说明:几个函数说明:196Status InitArray (Array &A, int dim,) /若维数若维数dimdim和各维长度合法,则和各维长度合法,则构造相应的数组构造相应的数组构造相应的数组构造相应的数组A A A A并返回并返回OKOK if (dimMAX_ARRAY_DIM) return ERROR; A.dim=dim; A.bounds=(int *)malloc(dim * sizeof(int); if(!a.bounds) exit(OVERFLOW); / / 分配存放分配存放“各维长度各维长度”的空的空间间 /若各维长度合法,则存入若各维长度合法,则存入A.bounds,A.bounds,并求出并求出A A的元素总数的元素总数elemtotalelemtotal elemtotal=1; va_start(ap, dim); /ap /ap为为va_listva_list类型,是存放变长参数表信息的类型,是存放变长参数表信息的 类型,将类型,将apap指向指向dimdim后的第一个参数后的第一个参数数组的基本操作函数说明(数组的基本操作函数说明(5个)个) (见教材见教材P93-95P93-95)197 for(i=0;idim;+i) A.boundsi=va_arg (ap, int); / / 返回返回apap当前指向的参数,并按参数类型将当前指向的参数,并按参数类型将apap指向下一个参数指向下一个参数 if (A.boundsi=0;-i) A.constantsi=A.boundsi+1*A.constantsi+1; return OK; b i+1 C i+1 198数组基址指针数组基址指针各维长度保各维长度保存区指针存区指针映像函数映像函数CiCi保存区指针保存区指针Status DestroyArray (Array &A) /销毁数组销毁数组A A if ( ! A.base ) return ERROR; free(A.base); A .base = NULL; if ( ! A.bounds ) return ERROR; free( A .bounds ); A.bounds = NULL; if ( !A.constants ) return ERROR; free ( A. constants ) ; A. constants = NULL; return OK; 199Status Locate(Array A, va_list ap, int &off) /若若ap指示的各下标值合法,则求出该元素在指示的各下标值合法,则求出该元素在A A中相对地址中相对地址offoff off=0; for(i=0;iA.dim;+i) ind= va_arg(ap, int); if (indA.boundsi) return OVERFLOW; off += A.constantsi * ind ; C i j i return OK; 200Status Value(Array A, ElemType &e,) /A/A是是n n维数组,维数组,e e为元素变量,随后是为元素变量,随后是n n个下标值,若个下标值,若各下标不超界,则各下标不超界,则e e赋值为所指定的赋值为所指定的A A的元素值,即将指的元素值,即将指定元素值读到定元素值读到e e变量中。变量中。 va_start (ap, e); / / 将将apap指向指向e e后的参数后的参数 if(result=Locate(A, ap, off)=0) return result; e=*(A.base+off); return OK; 201Status Assign(Array &A,ElemType e,) /A/A是是n n维数组,维数组,e e为元素变量,随后是为元素变量,随后是n n个下标值,若各下个下标值,若各下 标不超界,则标不超界,则e e的值赋为所指定的的值赋为所指定的A A的元素值,的元素值,即:将即:将即:将即:将e e e e值写值写值写值写入指定数组单元。入指定数组单元。入指定数组单元。入指定数组单元。 va_start(ap,e); if( (result=Locate(A,ap,off ) )=0) return result; *(A.base+off)=e; return OK; 202顺序存储方式:顺序存储方式:按低地址优先(或高地址优先)顺序存入一维按低地址优先(或高地址优先)顺序存入一维数组。数组。行指针向量行指针向量a11a12a1nam1am2amn补充:补充:链式存储方式:链式存储方式:用用带行指针向量的单链表带行指针向量的单链表来表示。来表示。注:注:数组的运算参见下一节实例(稀疏矩阵的转置)数组的运算参见下一节实例(稀疏矩阵的转置)(难点是多维数组与一维数组的地址映射关系难点是多维数组与一维数组的地址映射关系)2035.3 5.3 矩阵的压缩存储讨论:讨论:1. 什么是压缩存储?什么是压缩存储?若多个数据元素的若多个数据元素的值都相同值都相同,则只分配一个元素值的存储空间,则只分配一个元素值的存储空间,且零元素不占存储空间。且零元素不占存储空间。2. 什么样的矩阵具备压缩条件?什么样的矩阵具备压缩条件? 特殊矩阵(对称矩阵,对角矩阵,三角矩阵)特殊矩阵(对称矩阵,对角矩阵,三角矩阵)和稀疏矩阵。和稀疏矩阵。3. 什么叫什么叫稀疏矩阵?稀疏矩阵?矩阵中非零元素的个数较少(一般小于矩阵中非零元素的个数较少(一般小于5%5%)重点介绍稀疏矩阵的压缩和相应的操作。重点介绍稀疏矩阵的压缩和相应的操作。204一、一、稀疏矩阵的压缩存储稀疏矩阵的压缩存储问题:问题:如果只存储如果只存储稀疏矩阵中的非零元素,那这些元素的稀疏矩阵中的非零元素,那这些元素的位置信息位置信息该如何表示?该如何表示?解决思路:解决思路:对每个非零元素对每个非零元素增开增开若干存储单元,例如存放其所若干存储单元,例如存放其所在的行号和列号,便可准确反映该元素所在位置。在的行号和列号,便可准确反映该元素所在位置。实现方法:实现方法:将每个非零元素用一个三元组将每个非零元素用一个三元组(i,j,aij)来表示,)来表示,则每个则每个稀疏矩阵可用一个稀疏矩阵可用一个三元组表三元组表来表示。来表示。二、二、稀疏矩阵的操作稀疏矩阵的操作205例例1 : 三元素组表中的每个结点对应于稀疏矩阵的三元素组表中的每个结点对应于稀疏矩阵的一个非零元素,它包含有三个数据项,分别表示该一个非零元素,它包含有三个数据项,分别表示该元素的元素的 、 和和 。 行下标行下标列下标列下标元素值元素值例例2 2:写出右图所示稀疏写出右图所示稀疏矩阵的压缩存储形式。矩阵的压缩存储形式。0 12 9 0 0 00 0 0 0 0 0 -3 0 0 0 14 00 0 24 0 0 00 18 0 0 0 015 0 0 -7 0 0( ( 1,2,12)( 1,2,12) ,(1,3,9)(1,3,9), (3,1,-3) (3,1,-3), (3,5,14) (3,5,14), (4,3,24) (4,3,24), (5,2,18) (5,2,18) ,(6,1,15)(6,1,15), (6,4,-7) (6,4,-7) )法法1 1:用线性表表示:用线性表表示:0 12 9 0 0 00 0 0 0 0 0 -3 0 0 0 14 00 0 24 0 0 00 18 0 0 0 015 0 0 -7 0 0206法法2 2:用三元组矩阵表示:用三元组矩阵表示:0 12 9 0 0 00 0 0 0 0 0 -3 0 0 0 14 00 0 24 0 0 00 18 0 0 0 015 0 0 -7 0 0121213931-3351443245218611564-7注意:注意:为更可靠描述,为更可靠描述,通常再加一行通常再加一行“总体总体”信息:即信息:即总行数、总行数、总列数、非零元素总总列数、非零元素总个数个数668ijvalue稀疏矩阵压缩存储的稀疏矩阵压缩存储的缺点缺点:将失去随机存取功能将失去随机存取功能 :-(0 01 12 23 34 45 56 67 78 8207法法3 3:用用带辅助向量带辅助向量的三元组表示。的三元组表示。 方法:方法: 增加增加2 2个辅助向量:个辅助向量: 记录每行非记录每行非0元素个数,用元素个数,用NUM(i)表示;表示; 记录稀疏矩阵中每行第一个非记录稀疏矩阵中每行第一个非0元素元素在三元在三元 组中的行号组中的行号,用,用POS(i)表示。表示。76531211202NUM( i)6543POS( i )21i0 12 9 0 0 00 0 0 0 0 0 -3 0 0 0 14 00 0 24 0 0 00 18 0 0 0 015 0 0 -7 0 0-7461516182524341453-3139311221866vji0123456783用途:用途:通过三元组通过三元组高效访问稀疏矩阵高效访问稀疏矩阵中中任一非零元素。任一非零元素。规律:规律:POS(1)1 POS(i)POS(i-1)+NUM(i-1)208法法4 4:用用十字链表十字链表表示表示用途:用途:方便稀疏矩阵的加减运算;方便稀疏矩阵的加减运算;方法:方法:每个非每个非0元素占用元素占用5个域。个域。right downvji同一列中下一非同一列中下一非零元素的指针零元素的指针同一行中下一非同一行中下一非零元素的指针零元素的指针十字链表的特点:十字链表的特点:每行非零元素链接每行非零元素链接成带表头结点的循环链表;成带表头结点的循环链表;每列非零元素也链接每列非零元素也链接成带表头结点的循环链表。成带表头结点的循环链表。则每个非零元素既是行循环链表中的一个结点;又是列循环则每个非零元素既是行循环链表中的一个结点;又是列循环链表中的一个结点,即链表中的一个结点,即呈十字链状。呈十字链状。以刚才的以刚才的稀疏矩阵稀疏矩阵为例:为例:122100H19311825209 #define MAXSIZE 125000 /设非零元素最大个数设非零元素最大个数125000125000 typedef struct int i; /元素行号元素行号 int j; /元素列号元素列号 ElemType e; /元素值元素值 Triple; typedef struct Triple dataMAXSIZE+1; /三元组表,以行为主序存入一维向量三元组表,以行为主序存入一维向量 data data 中中 int mu; /矩阵总行数矩阵总行数 int nu; /矩阵总列数矩阵总列数 int tu; /矩阵中非零元素总个数矩阵中非零元素总个数 TsMatrix; 三元组表的顺序存储表示三元组表的顺序存储表示(见教材(见教材P98P98):):/一个结点的结构定义一个结点的结构定义/整个三元组表的定义整个三元组表的定义210二、二、稀疏矩阵的操作稀疏矩阵的操作 0 12 9 0 0 00 0 0 0 0 0-3 0 0 0 14 00 0 24 0 0 00 18 0 0 0 015 0 0 -7 0 00 0 3 0 0 1512 0 0 0 18 0 9 0 0 24 0 00 0 0 0 0 -70 0 14 0 0 00 0 0 0 0 0(1, 2, 12)(1, 3, 9 )(3, 1, -3)(3, 5, 14)(4, 3, 24)(5, 2, 18)(6, 1, 15)(6, 4, -7)(1, 3, -3)(1, 6, 15)(2, 1, 12)(2, 5, 18)(3, 1, 9)(3, 4, 24)(4, 6, -7)(5, 3, 14)三三元元组组表表a.data三三元元组组表表b.data转置后转置后MT(以转置运算为例)(以转置运算为例)目的:目的:211答:答:肯定不正确!肯定不正确!除了:除了: (1 1)每个元素的行下标和列下标互换(即三元组中)每个元素的行下标和列下标互换(即三元组中的的i i和和j j互换互换););还应该:还应该:(2 2)T T的总行数的总行数mumu和总列数和总列数nunu与与M M的不同的不同(互换);互换); (3 3)重排重排三元组内元素顺序三元组内元素顺序,使转置后的三元组也,使转置后的三元组也按行(或列)为主序有规律的排列。按行(或列)为主序有规律的排列。上述(上述(1 1)和()和(2 2)容易实现,难点在)容易实现,难点在(3 3)。 若采用三元组压缩技术存储稀疏矩阵,只要把每个若采用三元组压缩技术存储稀疏矩阵,只要把每个元素的元素的行下标和列下标互换行下标和列下标互换,就完成了对该矩阵的转置运,就完成了对该矩阵的转置运算,这种说法正确吗?算,这种说法正确吗? 有两种实现方法有两种实现方法压缩转置压缩转置( (压缩压缩) )快速转置快速转置提问:提问:212方法方法1 1:压缩转置压缩转置思路:思路:反复扫描反复扫描a.dataa.data中的中的列序列序,从小到大依次进行转置。,从小到大依次进行转置。三三元元组组表表a.data三三元元组组表表b.data(1, 3, -3)(1, 6, 15)(2, 1, 12) (2, 5, 18)(3, 1, 9) (3, 4, 24) (4, 6, -7) (5, 3, 14)(1, 2, 12)(1, 3, 9 )(3, 1, -3)(3, 5, 14)(4, 3, 24)(5, 2, 18)(6, 1, 15)(6, 4, -7)11 22col q1234 p1234213Status TransPoseSMatrix(TSMatrix M, TSMatrix &T)T.mu=M.nu; T.nu=M.mu; T.tu=M.tu; if (T.tu) q=1; for(col=1; col=M.nu; col+) for(p=1; p=M.tu; p+) if (M.datap.j=col) T.dataq.i=M.datap.j; T.dataq.j=M.datap.i; T.dataq.value=M.datap.value; q+; return OK; /TranposeSMatrix;压缩转置算法描述压缩转置算法描述:(见教材(见教材P99P99)/用三元组表存放稀疏矩阵用三元组表存放稀疏矩阵M M,求,求M M的转置矩阵的转置矩阵T T/q q是转置矩阵是转置矩阵T T的结点编号的结点编号/colcol是扫描是扫描M M三元表列序的变量三元表列序的变量/p是是M M三元表中结点编号三元表中结点编号2141 1、主要时间消耗主要时间消耗在查找在查找M.datap.j=colM.datap.j=col的元素的元素,由两重循,由两重循环完成环完成: : for(col=1; col=M.nuM.nu; col+) 循环次数循环次数nunu for(p=1; p=M.tuM.tu; p+) 循环次数循环次数tutu所以该算法的时间复杂度为所以该算法的时间复杂度为O(O(nu*tunu*tu) ) - -即即M M的列数与的列数与M M中非零元素的个数之中非零元素的个数之积积最恶劣情况:最恶劣情况:M M中全是非零元素,此时中全是非零元素,此时tu=mu*nutu=mu*nu, 时间复杂度为时间复杂度为 O( O(nunu2 2*mu*mu ) )注:注:若若M M中基本上是非零元素时,即使用非压缩传统转置算法中基本上是非零元素时,即使用非压缩传统转置算法的时间复杂度也不过是的时间复杂度也不过是O(O(nu*munu*mu) ) (程序见(程序见教材教材P99P99)结论:结论:压缩转置算法不能滥用。压缩转置算法不能滥用。前提:前提:仅适用于非零元素个数很少(即仅适用于非零元素个数很少(即tutumu*numu*nu)的情况。)的情况。压缩转置算法的效率分析压缩转置算法的效率分析:215方法方法2 2 快速转置快速转置三三元元组组表表a.data三三元元组组表表b.data(1, 3, -3)(2 ,1,12)(2, 5, 18)(3, 1, 9)(4, 6, -7)(5, 3, 14)(1, 6, 15)(3, 4, 24)(1, 2, 12)(1, 3, 9 )(3, 1, -3)(3, 5, 14)(4, 3, 24)(5, 2, 18)(6, 1, 15)(6, 4, -7)思路:依次思路:依次把把a.dataa.data中的元素直接送入中的元素直接送入b.datab.data的恰当位的恰当位置上(置上(即即M M三元组的三元组的p p指针不回溯指针不回溯)。)。关键:关键:怎样寻找怎样寻找b.datab.data的的“恰当恰当”位置位置? p1234 q 3 5216如果能如果能预知预知M矩阵矩阵每一列每一列( (即即T的每一行的每一行) )的的非零元素个数非零元素个数,又,又能预知能预知第一个非零元素第一个非零元素在在b.datab.data中的中的位置位置, ,则扫描则扫描a.data时便时便可以将每个元素准确定位(可以将每个元素准确定位(因为已知若干参考点因为已知若干参考点)。)。技巧:技巧:利用利用带辅助向量带辅助向量的三元组表,它正好携带每行(或列)的三元组表,它正好携带每行(或列)的非零元素个数的非零元素个数 NUM(i)以及每行(或列)的第一个非以及每行(或列)的第一个非零元素在三元组表中的位置零元素在三元组表中的位置POS(i) 等信息。等信息。设计思路:设计思路:i123456NUM(i)202112POS( i )133567不过我们需要的是不过我们需要的是按列生成的按列生成的M矩阵的辅助向量。矩阵的辅助向量。规律:规律:POS(1)1POS(i)POS(i-1)+NUM(i-1)请回忆:请回忆:请注意请注意a.dataa.data特征:每列首个非零元素必定先被扫描到。特征:每列首个非零元素必定先被扫描到。217令:令:M中的列变量用中的列变量用col表示;表示; num col :存放存放M中第中第col 列中非列中非0 0元素个数,元素个数, cpot col :存放存放M中第中第col列的第一个非列的第一个非0 0元素的位置,元素的位置, (即(即b.datab.data中待计算的中待计算的“恰当恰当”位置所需参考点)位置所需参考点)讨论:讨论:按列优先的辅助向量求出后,按列优先的辅助向量求出后,下一步该如何操作?下一步该如何操作?由由a.dataa.data中每个元素的列信息,即可直接查出中每个元素的列信息,即可直接查出b.datab.data中的中的重要参考点之位置,进而可确定当前元素之位置!重要参考点之位置,进而可确定当前元素之位置!col123456numcol222110cpotcol1规律:规律: cpot(1)1cpotcol cpotcol-1 + numcol-10 12 9 0 0 00 0 0 0 0 0-3 0 0 0 14 00 0 24 0 0 00 18 0 0 0 015 0 0 -7 0 0M 3 5 7 8 8col 1 2 3 4 5 6218Status FastTransposeSMatrix(TSMatirx M, TSMatirx &T) T.mu = M.nu ;T .nu = M.mu ; T.tu = M.tu ; if ( T.tu ) for(col = 1; col =M.nu; col+) numcol =0; for( i = 1; i =M.tu; i +) col =M.data i .j ; +num col ; cpos 1 =1; for(col = 2; col =M.nu; col+) cposcol =cposcol-1+num col-1 ; for( p =1; p =M.tu ; p + ) col =M.data p . j ; q =cpos col ; T.dataq.i = M.datap. j; T.dataq.j = M.datap. i; T.dataq. value = M.datap. value; + + cposcol ;+ + cposcol ; /for /ifreturn OK; /FastTranposeSMatrix;快速转置算法描述快速转置算法描述:/M/M用顺序存储表示,求用顺序存储表示,求M M的转置矩阵的转置矩阵T T/初始化初始化M M中各列元素个数为中各列元素个数为0 0/再生成每列首元位置辅助向量表再生成每列首元位置辅助向量表/p/p指向指向a.dataa.data,循环次数为非,循环次数为非0 0元素总个数元素总个数tutu/查辅助向量表得查辅助向量表得q q,即,即T T中位置中位置/重要语句!重要语句!修改向量表中列坐标值,供修改向量表中列坐标值,供同一列同一列下一非零元素定位之用!下一非零元素定位之用!2191. 1. 与常规算法相比,附加了生成辅助向量表的工作。增开了与常规算法相比,附加了生成辅助向量表的工作。增开了2 2个长度为列长的数组个长度为列长的数组( (num 和和cpos )。 传统转置:传统转置:O(mu*nu) O(mu*nu) 压缩转置:压缩转置:O(mu*tu) O(mu*tu) 压缩快速转置:压缩快速转置:O(nu+tu)O(nu+tu)牺牲空间效率换时间效率。牺牲空间效率换时间效率。快速转置算法的效率分析快速转置算法的效率分析:2. 2. 从时间上,此算法用了从时间上,此算法用了4 4个并列的单循环,而且其中前个并列的单循环,而且其中前3 3个个单循环都是用来产生辅助向量表的。单循环都是用来产生辅助向量表的。 for(col = 1; col =M.nu; col+) 循环次数循环次数nu;nu; for( i = 1; i =M.tu; i +) 循环次数循环次数tu;tu; for(col = 2; col =M.nu; col+) 循环次数循环次数nu;nu; for( p =1; p 1n1时,其余的结点时,其余的结点分为分为m(m0)m(m0)个个互不相交互不相交的有限集合的有限集合T1,T2T1,T2,TmTm。每个集合本身又是棵树,被称作这个根的每个集合本身又是棵树,被称作这个根的子树子树 。233树的表示法有几种:树的表示法有几种:图形表示法图形表示法嵌套集合表示法嵌套集合表示法广义表表示法广义表表示法目录表示法目录表示法左孩子右兄弟表示法左孩子右兄弟表示法这些表示法的示意图这些表示法的示意图参见教材参见教材P120P120树的抽象数据类型定义树的抽象数据类型定义参参见教材见教材P118-119P118-119234图形表示法:图形表示法:教师教师学生学生其他人员其他人员9999级级20002000级级 20012001级级20022002级级华中科技大学华中科技大学计算机系计算机系电信系电信系自控系自控系叶子叶子根根子树子树235广义表表示法广义表表示法( A ( B ( E ( K, L ), F ), C ( G ), D ( H ( M ), I, J ) ) 根作为根作为由子树森林组成的由子树森林组成的表的名字写在表的左边表的名字写在表的左边datalink 1 link 2.link n麻烦问题:应当开设多少个链域麻烦问题:应当开设多少个链域?236左孩子右兄弟表示法左孩子右兄弟表示法A AB BC CD DE EF FGGHHI IJ JKKL LMM数据数据左孩子左孩子 右兄弟右兄弟( A ( B ( E ( K, L ), F ), C ( G ), D ( H ( M ), I, J ) ) )237 树的抽象数据类型定树的抽象数据类型定义义(见教材(见教材P118-119P118-119)ADT Tree数据对象数据对象D:数据关系数据关系R:基本操作基本操作 P:ADT Tree若若D为空集,则称为空树;为空集,则称为空树;/允许允许n=0若若D中仅含一个数据元素,则中仅含一个数据元素,则R为空集;为空集;其他情况下的其他情况下的R存在二元关系:存在二元关系: root 唯一唯一 /关于根的说明关于根的说明 DjDk= /关于子树不相交的说明关于子树不相交的说明 /关于数据元素的说明关于数据元素的说明D是具有相同特性的数据元素的集合。是具有相同特性的数据元素的集合。/至少有至少有15个个2382. 若干术语若干术语即上层的那个结点即上层的那个结点(直接前驱直接前驱)即下层结点的子树的根即下层结点的子树的根(直接后继直接后继)同一双亲下的同层结点(孩子之间互称兄弟)同一双亲下的同层结点(孩子之间互称兄弟)即双亲位于同一层的结点(但并非同一双亲)即双亲位于同一层的结点(但并非同一双亲)即从根到该结点所经分支的所有结点即从根到该结点所经分支的所有结点即该结点下层子树中的任一结点即该结点下层子树中的任一结点ABCGEIDHFJMLK 根根 叶子叶子 森林森林有序树有序树无序树无序树即根结点即根结点(没有前驱没有前驱)即终端结点即终端结点(没有后继没有后继)指指m棵不相交的树的集棵不相交的树的集合合(例如删除例如删除A后的子树个数后的子树个数)双亲双亲孩子孩子兄弟兄弟堂兄弟堂兄弟祖先祖先子孙子孙结点各子树从左至右有序,不能互换(左为第一)结点各子树从左至右有序,不能互换(左为第一)结点各子树可互换位置。结点各子树可互换位置。2392. 若干术语(续)若干术语(续)即树的数据元素即树的数据元素结点挂接的子树数结点挂接的子树数(有几个直接后继就是几度,(有几个直接后继就是几度,亦称亦称“次数次数”)结点结点结点的度结点的度结点的层次结点的层次终端结点终端结点分支结点分支结点树的度树的度树的深度树的深度(或高度或高度)ABCGEIDHFJMLK从根到该结点的层数(根结点算第一层)从根到该结点的层数(根结点算第一层)即度为即度为0的结点,即叶子的结点,即叶子即度不为即度不为0的结点(也称为内部结点)的结点(也称为内部结点)所有结点度中的最大值(所有结点度中的最大值(Max各结点的度各结点的度)指所有结点中最大的层数(指所有结点中最大的层数(Max各结点的层次各结点的层次)问:问:右上图中的结点数右上图中的结点数 ;树的度;树的度 ;树的深度;树的深度13133 34 42403. 树的逻辑结构树的逻辑结构 ( (特点特点) ): 一对多(一对多(1:n1:n),有多个直接后继(如家谱),有多个直接后继(如家谱树、目录树等等),但只有一个根结点,且树、目录树等等),但只有一个根结点,且子树之间互不相交子树之间互不相交。 4. 树的存储结构树的存储结构 讨论讨论1:树是非线性结构,该怎样存储?树是非线性结构,该怎样存储?仍然有顺序存储、链式存储等方式。仍然有顺序存储、链式存储等方式。 241讨论讨论3:树的树的链式存储链式存储方案应该怎样制定?方案应该怎样制定?可规定为:可规定为:从上至下、从左至右从上至下、从左至右将树的结点依次存入内存。将树的结点依次存入内存。重大缺陷:复原困难(不能唯一复原就没有实用价值)。重大缺陷:复原困难(不能唯一复原就没有实用价值)。讨论讨论2:树的树的顺序存储顺序存储方案应该怎样制定?方案应该怎样制定?可用多重链表:可用多重链表:一个前趋指针,一个前趋指针,n n个后继指针。个后继指针。细节问题:细节问题:树中结点的结构类型样式该如何设计?树中结点的结构类型样式该如何设计? 即应该设计成即应该设计成“等长等长”还是还是“不等长不等长”?缺点:缺点:等长结构太浪费(每个结点的度不一定相同);等长结构太浪费(每个结点的度不一定相同); 不等长结构太复杂(要定义好多种结构类型)。不等长结构太复杂(要定义好多种结构类型)。解决思路:解决思路:先研究最简单、最有规律的树,然后设法把先研究最简单、最有规律的树,然后设法把一般的树转化为简单树。一般的树转化为简单树。二叉树二叉树二叉树二叉树2425. 树的运算树的运算 要明确:要明确:1. 普普通通树树(即即多多叉叉树树)若若不不转转化化为为二二叉叉树树,则则运运算很难实现。算很难实现。2. 二二叉叉树树的的运运算算仍仍然然是是插插入入、删删除除、修修改改、查查找找、排排序序等等,但但这这些些操操作作必必须须建建立立在在对对树树结结点点能能够够“遍历遍历”的基础上!的基础上!(遍遍历历指指每每个个结结点点都都被被访访问问且且仅仅访访问问一一次次,不遗漏不重复)。不遗漏不重复)。本章重点:二叉树的表示和实现本章重点:二叉树的表示和实现2436.2 6.2 二叉树二叉树为何要重点研究每结点最多只有两个为何要重点研究每结点最多只有两个 “ “叉叉” ” 的树?的树?二叉树的结构最简单,规律性最强;二叉树的结构最简单,规律性最强;可以证明,所有树都能转为唯一对应的二叉树,不失一般性。可以证明,所有树都能转为唯一对应的二叉树,不失一般性。1. 二叉树的定义二叉树的定义2. 二叉树的性质二叉树的性质3. 二叉树的存储结构二叉树的存储结构(二叉树的运算(二叉树的运算见见6.3节节)2441. 1. 1. 1. 二叉树的定义二叉树的定义二叉树的定义二叉树的定义定义:定义:是是n(n0)个结点的有限集合,由一个根结点以及两棵)个结点的有限集合,由一个根结点以及两棵互不相交的、分别称为互不相交的、分别称为左子树和右子树左子树和右子树的二叉树组成的二叉树组成 。逻辑结构:逻辑结构: 一对二(一对二(1:2) 基本特征基本特征: 每个结点最多只有两棵子树(不存在度大于每个结点最多只有两棵子树(不存在度大于2 2的结点);的结点); 左子树和右子树次序不能颠倒(有序树)。左子树和右子树次序不能颠倒(有序树)。基本形态:基本形态: 问:具有问:具有问:具有问:具有3 3 3 3个结点的二叉树可能有几种不同形态?普通树呢?个结点的二叉树可能有几种不同形态?普通树呢?个结点的二叉树可能有几种不同形态?普通树呢?个结点的二叉树可能有几种不同形态?普通树呢? 5种种/2种种245二叉树的抽象数据类型定义二叉树的抽象数据类型定义(见教材(见教材P P121-122121-122)ADT BinaryTree数据对象数据对象D:数据关系数据关系R:基本操作基本操作 P:ADT BinaryTree若若D=,则,则R= ;若若D,则,则R= H;存在二元关系:;存在二元关系: root 唯一唯一 /关于根的说明关于根的说明 DjDk= /关于子树不相交的说明关于子树不相交的说明 /关于数据元素的说明关于数据元素的说明 /关于左子树和右子树的说关于左子树和右子树的说明明D是具有相同特性的数据元素的集合。是具有相同特性的数据元素的集合。/至少有至少有20个个2462. 2. 2. 2. 二叉树的性质二叉树的性质二叉树的性质二叉树的性质 (3+2)(3+2)(3+2)(3+2)讨论讨论1 1:第:第i i层的结点数至多是多少?层的结点数至多是多少? (利用二进制性质可轻松求出)(利用二进制性质可轻松求出) 性质性质1: 1: 在二叉树的第在二叉树的第i i层上至多有层上至多有2 2 2 2i-1i-1i-1i-1个结点(个结点(i=1i=1)。)。性质性质2: 2: 深度为深度为k k的二叉树至多有的二叉树至多有2 2 2 2k k k k-1-1-1-1个结点(个结点(k=1k=1)。)。2 2i-1i-1个个提问:第提问:第i i层上至少有层上至少有 个结点?个结点?1 1讨论讨论2 2:深度为:深度为k k的二叉树,至多有多少个结点?的二叉树,至多有多少个结点? (利用二进制性质可轻松求出)(利用二进制性质可轻松求出)2 2k k-1-1提问:深度为提问:深度为k k时至少有时至少有 个结点?个结点?k k247讨论讨论3 3:二叉树的叶子数和度为:二叉树的叶子数和度为2 2的结点数之间有关系吗?的结点数之间有关系吗?性质性质3: 3: 对于任何一棵二叉树,若对于任何一棵二叉树,若2 2度的结点数有度的结点数有n n2 2个,个,则叶子数(则叶子数(n n0 0)必定为必定为n n2 21 1 (即(即n0=n2+1)证明:证明:证明:证明: 二叉树中全部结点数二叉树中全部结点数nn0+n1+n2( (叶子数叶子数1 1度结点数度结点数2 2度结点数度结点数) )又又又又二叉树中全部结点数二叉树中全部结点数nB+1 ( ( 总分支数根结点总分支数根结点 ) ) (除根结点外,每个结点必有一个直接前趋,即一个分支)(除根结点外,每个结点必有一个直接前趋,即一个分支)而而而而 总分支数总分支数B= n1+2n2 (1(1度结点必有度结点必有1 1个直接后继,个直接后继,2 2度结点必有度结点必有2 2个个) )三式联立可得:三式联立可得:三式联立可得:三式联立可得: n0+n1+n2= n1+2n2 +1, 即即n0=n2+1实际意义:实际意义:叶子数叶子数2 2度结点数度结点数1 1ABCGEIDHFJ248对于两种特殊形式的二叉树(对于两种特殊形式的二叉树(满二叉树和完全二叉树满二叉树和完全二叉树),),还特别具备以下还特别具备以下2 2个性质:个性质:性质性质4: 4: 具有具有n n个结点的完全二叉树的深度必为个结点的完全二叉树的深度必为loglog2 2nn1 1性质性质5: 5: 对完全二叉树,若从上至下、从左至右编号,则对完全二叉树,若从上至下、从左至右编号,则编号为编号为i 的结点,其左孩子编号必为的结点,其左孩子编号必为2i,其右孩子编号必,其右孩子编号必为为2i1;其双亲的编号必为;其双亲的编号必为i/2(i1 时为根时为根, ,除外除外)。)。 证证明明:根根据据性性质质2 2,深深度度为为k k的的二二叉叉树树最最多多只只有有2 2k k-1-1个个结结点点,且且完完全全二二叉叉树树的的定定义义是是与与同同深深度度的的满满二二叉叉树树前前面面编编号号相相同同,即即它它的的总总结结点点数数n n位位于于k k层层和和k-1k-1层满二叉树容量之间,即层满二叉树容量之间,即 2 2k-1k-1-1n2-1n2k k-1 -1 或或2 2k-1k-1n n 2 2k k三边同时取对数,于是有:三边同时取对数,于是有:k-1logk-1log2 2nk n1)f=n*fact(n-1); else f=1; return(f); 261先序遍历算法先序遍历算法DLR( liuyu *root ) if (root !=NULL) /非空二叉树非空二叉树 printf(“%d”,root-data); /访问访问D D DLR(root-lchild); /递归遍历左子树递归遍历左子树 DLR(root-rchild); /递归遍历右子树递归遍历右子树 return(0); 结点数据类型自定义结点数据类型自定义typedef struct liuyu int data; struct liuyu *lchild,*rchild; liuyu;liuyu *root; 262中序遍历算法中序遍历算法 LDR(x*root) if(root !=NULL) LDR(root-lchild); printf(“%d”,root-data); LDR(root-rchild); return(0); 263 后序遍历算法后序遍历算法 LRD (x*root) if(root !=NULL) LRD(root-lchild); LRD(root-rchild); printf(“%d”,root-data); return(0); 264对遍历的分析:对遍历的分析:1. 从前面的三种遍历算法可以知道:如果将从前面的三种遍历算法可以知道:如果将print语句抹去,语句抹去,从递归的角度看,这三种算法是完全相同的,或者说这三种从递归的角度看,这三种算法是完全相同的,或者说这三种遍历算法的遍历算法的访问路径是相同的,只是访问结点的时机不同访问路径是相同的,只是访问结点的时机不同。从虚线的出发点到终点的路径从虚线的出发点到终点的路径上,每个结点经过上,每个结点经过3次次。AFEDCBG第第1次经过时访问先序遍历次经过时访问先序遍历第第2次经过时访问中序遍历次经过时访问中序遍历第第3次经过时访问后序遍历次经过时访问后序遍历2. 2. 二叉树遍历的时间效率和空间效率二叉树遍历的时间效率和空间效率时间效率时间效率: :O(n)O(n)O(n)O(n) /每个结点只访问一次每个结点只访问一次空间效率空间效率: :O(n)O(n)O(n)O(n) /栈占用的最大辅助空间栈占用的最大辅助空间(精确值:树深为(精确值:树深为k k的递归遍历需要的递归遍历需要k+1k+1个辅助单元!)个辅助单元!)265例:例:编写递归算法,计算二叉树中叶子结点的数目。编写递归算法,计算二叉树中叶子结点的数目。 思路:思路:输出叶子结点比较简单,用任何一种遍历算法,凡输出叶子结点比较简单,用任何一种遍历算法,凡是左右指针均空者,则为叶子,将其统计并打印出来。是左右指针均空者,则为叶子,将其统计并打印出来。 DLR(liuyu *root) /采用中序遍历的递归算法采用中序遍历的递归算法 if ( root!=NULL ) /非空二叉树条件,还可写成非空二叉树条件,还可写成if(root)if(root) if(!root-lchild&!root-rchild) /是叶子结点则统计并打印是叶子结点则统计并打印 sum+; printf(%dn,root-data); DLR(root-lchild); /递归遍历左子树,直到叶子处;递归遍历左子树,直到叶子处; DLR(root-rchild); /递归遍历右子树,直到叶子处;递归遍历右子树,直到叶子处; return(0); 266注:要实现遍历运算必须先把二叉树存入机内。注:要实现遍历运算必须先把二叉树存入机内。思路:思路:利用利用前序前序遍历来建树遍历来建树 (结点值陆续从键盘输入,用(结点值陆续从键盘输入,用DLR为宜为宜)Bintree createBTpre( ) Bintree T; char ch;scanf(“%c”,&ch);if(ch=)T=NULL; elseT=( Bintree )malloc(sizeof(BinTNode);T-data=ch;T-lchild=createBTpre();T-rchild=createBTpre(); return T;怎样建树?见教材怎样建树?见教材P131P131程序。程序。267习题讨论:习题讨论:1. 1. 1. 1. 求二叉树深度,或从求二叉树深度,或从求二叉树深度,或从求二叉树深度,或从x x x x结点开始的子树深度。结点开始的子树深度。结点开始的子树深度。结点开始的子树深度。 算法思路:算法思路:只查各结点后继链表指针,若左只查各结点后继链表指针,若左( (右右) )孩子孩子 的左的左( (右右) )指针非空,则层次数加指针非空,则层次数加1 1;否则;否则 函数返回。函数返回。当当T= NULLT= NULL时时, ,深度为深度为0;0;否则否则, T, T的深度的深度= MAX= MAX左子树深度左子树深度, ,右子树深度右子树深度+1;+1; 2682. 2. 按层次输出二叉树中所有结点。按层次输出二叉树中所有结点。按层次输出二叉树中所有结点。按层次输出二叉树中所有结点。 算法思路:算法思路:既然要求从上到下,从左到右,则既然要求从上到下,从左到右,则利用利用队列队列存放各子树结点的指针是个好办法,而不必拘存放各子树结点的指针是个好办法,而不必拘泥于递归算法。泥于递归算法。技巧:技巧:当根结点入队后,令其左、右孩子结点入队,当根结点入队后,令其左、右孩子结点入队,而根结点以外的结点出队时又令它的左右孩子结点而根结点以外的结点出队时又令它的左右孩子结点入队,入队,由此便可产生按层次输出的效果。由此便可产生按层次输出的效果。 A B CD E2693.3.3.3.中序遍历的非递归中序遍历的非递归中序遍历的非递归中序遍历的非递归( ( ( (迭代迭代迭代迭代) ) ) )算法算法算法算法算法思路:算法思路:若不用递归,则要实现二叉树遍历的若不用递归,则要实现二叉树遍历的“嵌套嵌套”规则,必用堆栈。规则,必用堆栈。参见教材参见教材P130-131P130-131程序。程序。Status InOrderTraverse( Bitree T, Status (* Visit) (TElemType e) ) InitStack( S ); Push( S, T ); / 根指针进栈根指针进栈 while ( !StackEmpty(S) ) while( GetTop( S, p ) & p ) Push( S, p-lchild ); / 向左走到尽头向左走到尽头 Pop( S, p ); / 空指针退栈空指针退栈 if ( !StackEmpty(S) ) / 访问结点访问结点,向右一步向右一步 Pop( S, p ); if (!Visit ( p-data ) ) return ERROR; Push ( S, p-rchild ); / 将右儿子入栈,则下次循环时打印右儿子将右儿子入栈,则下次循环时打印右儿子 / if /while return OK; / InOrderTraverse 270Status InorderTraverse(BiTree T,Status( *Visit)(TElemType e) InitStack( S ); p=T; while ( p | !StackEmpty(S) ) / 栈不空或树不空栈不空或树不空 if ( p ) Push(S,p); p= p-lchild; / 根指针进栈根指针进栈,遍历左子树遍历左子树 else / 根指针退栈根指针退栈,访问根结点访问根结点,遍历右子树遍历右子树 Pop( S,p); if ( !Visit( p-data) ) return ERROR; p = p-rchild; / else / while return OK; / InorderTraverse 2714. 4.判别给定二叉树是否为完全二叉树(即顺序二叉树)判别给定二叉树是否为完全二叉树(即顺序二叉树)判别给定二叉树是否为完全二叉树(即顺序二叉树)判别给定二叉树是否为完全二叉树(即顺序二叉树) 算法思路:算法思路:完全二叉树的特点是:没有左子树空而右完全二叉树的特点是:没有左子树空而右子树单独存在的情况子树单独存在的情况(前前k-1层都是满的,且第层都是满的,且第k层左层左边也满)边也满)。技巧技巧: 按层序遍历方式,先把所有结点按层序遍历方式,先把所有结点(不管当前结(不管当前结点是否有左右孩子)点是否有左右孩子)都入队列都入队列.若为完全二叉树若为完全二叉树,则层则层序遍历时得到的肯定是一个连续的不包含空指针的序序遍历时得到的肯定是一个连续的不包含空指针的序列列.如果序列中出现了空指针,则说明不是完全二叉如果序列中出现了空指针,则说明不是完全二叉树。树。272特别讨论:特别讨论:特别讨论:特别讨论:若已知先序若已知先序若已知先序若已知先序/ /后序遍历结果和中序遍历后序遍历结果和中序遍历后序遍历结果和中序遍历后序遍历结果和中序遍历结果,能否结果,能否结果,能否结果,能否“ “恢复恢复恢复恢复” ”出二叉树?出二叉树?出二叉树?出二叉树?【严题集【严题集6.31】 证明:由一棵二叉树的先序序列和中序证明:由一棵二叉树的先序序列和中序序列可唯一确定这棵二叉树。序列可唯一确定这棵二叉树。 例:例:已知一棵二叉树的已知一棵二叉树的中序序列中序序列和和后序序列后序序列分别是分别是BDCEAFHG 和和 DECBHGFA,请画出这棵二叉树。,请画出这棵二叉树。分析:分析:由后序遍历特征,根结点必在后序序列尾部由后序遍历特征,根结点必在后序序列尾部(即(即A A);由由中中序序遍遍历历特特征征,根根结结点点必必在在其其中中间间,而而且且其其左左部部必必全全部部是是左子树子孙左子树子孙(即(即BDCEBDCE),其右部必全部是右子树子孙,其右部必全部是右子树子孙(即(即FHGFHG);继继而而,根根据据后后序序中中的的DECBDECB子子树树可可确确定定B B为为A A的的左左孩孩子子,根根据据HGFHGF子串可确定子串可确定F F为为A A的右孩子;以此类推。的右孩子;以此类推。273中序遍历:中序遍历:B D C E A F H G后序遍历:后序遍历:D E C B H G F A(B D C E)( F H G)ABF (D C E) ( H G)CD EGHABBFF274问:问:用二叉链表法(用二叉链表法(l_child, r_child)存储包含)存储包含n个结点的二个结点的二叉树,结点的指针区域中会有多少个空指针?叉树,结点的指针区域中会有多少个空指针?分分析析:用用二二叉叉链链表表存存储储包包含含n n个个结结点点的的二二叉叉树树,结结点点必必有有2n个链域个链域(见二叉链表数据类型说明)(见二叉链表数据类型说明)。除除根根结结点点外外,二二叉叉树树中中每每一一个个结结点点有有且且仅仅有有一一个个双双亲亲(直直接接前前驱驱),所所以以只只会会有有n1个个结结点点的的链链域域存存放放指指针针,指指向非空子女结点(即直接后继)。向非空子女结点(即直接后继)。思考:思考:二叉链表空间效率这么低,能否利用这些空闲区存放二叉链表空间效率这么低,能否利用这些空闲区存放有用的信息或线索?有用的信息或线索?我们可以用它来存放当前结点的直接前驱和后继等线索,我们可以用它来存放当前结点的直接前驱和后继等线索,以加快查找速度。以加快查找速度。所以,所以, 空指针数目空指针数目2n(n-1)=n+1个个。n+1275二、线索二叉树线索二叉树(Threaded Binary Tree)普通二叉树只能找到结点的左右孩子信息,普通二叉树只能找到结点的左右孩子信息,而该结点而该结点的直接前驱和直接后继只能在遍历过程中获得。的直接前驱和直接后继只能在遍历过程中获得。若将若将遍历后遍历后对应的有关前驱和后继对应的有关前驱和后继预存预存起来,则从起来,则从第第一个结点一个结点开始就能很快开始就能很快“顺藤摸瓜顺藤摸瓜”而遍历整个树了。而遍历整个树了。两种解决方法:两种解决方法:增加两个域:增加两个域:fwd和和bwd;存放前驱指针存放前驱指针存放后继指针存放后继指针如何预存这类信息?如何预存这类信息?例如中序遍历结果:例如中序遍历结果:B D C E A F H GB D C E A F H G,实际上,实际上已将二叉已将二叉树转为树转为线性排列线性排列,显然具有唯一前驱和唯一后继。,显然具有唯一前驱和唯一后继。1. 1. 定义定义 2. 2. 生成生成 3. 3. 遍历遍历利用空链域(利用空链域(n+1个空链域)个空链域)276规定:规定:1)若结点有左子树,则)若结点有左子树,则lchild指向其左孩子;指向其左孩子; 否则,否则, lchild指向其直接前驱指向其直接前驱(即线索即线索);2)若结点有右子树,则)若结点有右子树,则rchild指向其右孩子;指向其右孩子; 否则,否则, rchild指向其直接后继指向其直接后继(即线索即线索) 。为区别两种不同情况,特增加两个标志域(各为区别两种不同情况,特增加两个标志域(各1bit)lchilddatarchild约定约定:当当Tag域为域为0时时,表示表示正常正常情况情况;当当Tag域为域为1时时,表示表示线索线索情况情况.右孩子或后继右孩子或后继左孩子或前驱左孩子或前驱LTagRTag277附:有关线索二叉树的几个术语:附:有关线索二叉树的几个术语: 线索链表:线索链表:用用含含Tag的结点样式所构成的二叉链表的结点样式所构成的二叉链表 线线 索:索:指向结点前驱和后继的指针指向结点前驱和后继的指针线索二叉树:线索二叉树:加上线索的二叉树加上线索的二叉树 线线 索索 化:化:对二叉树以对二叉树以某种次序遍历某种次序遍历使其变为线索二使其变为线索二叉树的过程叉树的过程讨论:讨论:增加了前驱和后继等线索有什么好处?增加了前驱和后继等线索有什么好处?能方便找出当前结点的前驱和后继,不用能方便找出当前结点的前驱和后继,不用堆栈也能遍历整个树。堆栈也能遍历整个树。278dataAGEIDJHCFBltag0011110101rtag0001010111AGEIDJHCFB例:例:某某先序遍历先序遍历结果如下表所示,请画出对应的结果如下表所示,请画出对应的二叉树。二叉树。 (多带了两个标志!)(多带了两个标志!)2792. 2. 线索二叉树的生成线索二叉树的生成线索化过程就是线索化过程就是在遍历过程中修改空指针在遍历过程中修改空指针的过程:的过程:将空的将空的l lchildchild改为结点的直接前驱;改为结点的直接前驱;将空的将空的r rchildchild改为结点的直接后继。改为结点的直接后继。非空指针呢?仍然指向孩子结点(称为非空指针呢?仍然指向孩子结点(称为“正常情况正常情况”)280ABCGEIDHFroot悬空悬空?悬空?悬空?解:解:该二叉树中序遍历结果为该二叉树中序遍历结果为: : H, D, I, B, E, A, F, C, G所以添加线索应当按如下路径进行:所以添加线索应当按如下路径进行:为避免悬空为避免悬空态,应增设态,应增设一个头结点一个头结点例例1 1:画出以下二叉树对应的画出以下二叉树对应的中序中序线索二叉树。线索二叉树。28100A00C00B11E11F11G00D11I11H注:此图中序遍历结果为注:此图中序遍历结果为: : H, D, I, B, E, A, F, C, G0-root0对应的中序线索二叉树存储结构如图所示:对应的中序线索二叉树存储结构如图所示:282例例2 2:【 2000年计算机系考研题】年计算机系考研题】给定如图所示给定如图所示二叉树二叉树T T,请画出与其对应的中序线索二叉树。,请画出与其对应的中序线索二叉树。 2825405560330854解解: :因为中序遍历序列是:因为中序遍历序列是:5555 40 25 60 40 25 60 2828 08 33 08 33 5454对应线索树应当按此规律连线,即对应线索树应当按此规律连线,即在原二叉树中添加虚线。在原二叉树中添加虚线。NILNILNILNIL283线索二叉树的生成算法线索二叉树的生成算法(算法算法6.6, 见教材见教材P134)目的:在依某种顺序遍历二叉树时修改空指针,添加前驱或后继。目的:在依某种顺序遍历二叉树时修改空指针,添加前驱或后继。注解:为方便添加结点的前驱或后继,需要设置两个指针:注解:为方便添加结点的前驱或后继,需要设置两个指针: p指针指针当前结点之指针;当前结点之指针; pre指针指针前驱结点之指针。前驱结点之指针。技巧:技巧:当结点当结点p的左的左/右域为空右域为空时,只改写它的左域(装入前驱时,只改写它的左域(装入前驱pre),而其右域(后继)留给下一结点来填写。,而其右域(后继)留给下一结点来填写。 或者说,当前结点的指针或者说,当前结点的指针p应当送到前驱结点的空右域中。应当送到前驱结点的空右域中。若若p-lchildNULL, ,则则p-Ltag=1;p-Ltag=1;p-lp-lchildchildpre; ; /p/p的前驱结点指针的前驱结点指针pre存入左空域存入左空域若若pre-rchildNULL, 则则pre-Rtagpre-Rtag1;pre-rchild=p; /p存入其前驱结点存入其前驱结点pre的右的右空域空域284Status InorderThreading(BiThrTree & Thrt, BiThrTree T) /中序遍历二叉树中序遍历二叉树T,并将其中序线索化并将其中序线索化, Thrt 指向头结点指向头结点. if ( ! (Thrt = (BiThrTree) malloc ( sizeof (BiThrnode) ) ) exit ( OVERFLOW ) ; Thrt -LTag = Link; Thrt -RTag = Thead; / 建头结点建头结点 Thrt -rchild = Thrt ; /右指针回指右指针回指 if ( !T ) Thrt -lchild = Thrt ; / 若二叉树空若二叉树空,则左指针回指则左指针回指 else Thrt -lchild = T; pre = Thrt; /将头结点与树相连将头结点与树相连 InThreading(T); / 中序遍历进行中序线索化中序遍历进行中序线索化 pre -rchild = Thrt; pre -RTag = Thread; /最后一个结点线索化最后一个结点线索化 Thrt -rchild = pre; return OK; / InOrderThreading285void InThreading (BiThrTree p) if (p) InThreading( p-lchild ); / 左子树线索化左子树线索化 if ( !p-lchild ) p-LTag=Thread; p-lchild=pre; / 前驱线索前驱线索 if ( !pre-rchild ) pre-RTag=Thread; pre-rchild=p; /后继线索后继线索 pre = p; / 保持保持pre指向指向p的前驱的前驱 InThreading(p-rchild); /右子树线索化右子树线索化 / InThreading2863. 3. 线索二叉树的遍历线索二叉树的遍历理论上,只要找到序列中的理论上,只要找到序列中的第一个结点第一个结点,然后,然后依次访依次访问结点的后继问结点的后继直到后继为空时结束。直到后继为空时结束。但是,但是,但是,但是,在线索化二叉树中,并不是每个结点都能直接找到其在线索化二叉树中,并不是每个结点都能直接找到其后继的,后继的,当标志为当标志为0 0时,时,R_child=R_child=右孩子地址指针,并非后继!右孩子地址指针,并非后继!需要通过一定运算才能找到它的后继。需要通过一定运算才能找到它的后继。以以中序线索二叉树中序线索二叉树为例:为例:对叶子结点(对叶子结点(RTag=1),直接后继指针就在其),直接后继指针就在其rchild域内;域内;对其他结点(对其他结点(RTag=0),直接后继是其),直接后继是其右子树最左下的结点右子树最左下的结点;(因为中序遍历规则是(因为中序遍历规则是LDR,先左再根再右先左再根再右先左再根再右先左再根再右) 287程序注解程序注解 (非递归,且不用栈非递归,且不用栈):):P=T-lchild; /从头结点进入到根结点;从头结点进入到根结点;while( p!=T) / 空树或遍历结束时,空树或遍历结束时,p=T while(p-LTag=link) p=p-lchild; /先找到中序遍历起点先找到中序遍历起点 if (!visit(p-data) return ERROR; /访问最左结点访问最左结点 while(p-RTag=Thread & p-rchild!=T) /p-rchild=T即为最后一个结点即为最后一个结点 p=p-rchild; Visit(p-data); /若有后继标志,则直接提取若有后继标志,则直接提取p-rchild中线索并访问后继结点;中线索并访问后继结点; p=p-rchild; /当前结点右域不空当前结点右域不空或或已经找好了后继已经找好了后继,则一律从结点,则一律从结点的右子树开始重复的右子树开始重复 的全部过程。的全部过程。 Return OK;线索二叉树的线索二叉树的中序中序遍历算法遍历算法(算法算法6.5, 参见教材参见教材P134)288算法流程:算法流程:return OK;p=T-lchild;p!=Tp-LTag=0p=p-lchild;vist(p-data);p-LTag=1&p-rchild!=Tp=p-rchild;visit(p-data);p=p-rchild;YNYNYN演演示示程程序序289提前介绍:二叉树的应用提前介绍:二叉树的应用平衡树平衡树排序树排序树字典树字典树判定树判定树带权树带权树最优树最优树特点:左右子树深度差特点:左右子树深度差 1特点:特点:“左小右大左小右大”(见实验二的方案(见实验二的方案1)由字符串构成的二叉树排序树由字符串构成的二叉树排序树例如,例如,12个球只称个球只称3次分出轻重次分出轻重特点:路径长度带权值特点:路径长度带权值 带权路径长度最短的树,又称带权路径长度最短的树,又称 Huffman树,用途之一是通信中的压缩编码。树,用途之一是通信中的压缩编码。2906.4 树和森林1. 树和森林与二叉树的转换树和森林与二叉树的转换2. 树和森林的存储方式树和森林的存储方式3. 树和森林的遍历树和森林的遍历2911. 树和森林与二叉树的转换树和森林与二叉树的转换转换步骤:转换步骤:step1: step1: 将树中同一结点的兄弟相连将树中同一结点的兄弟相连; ;step2: step2: 保留结点的最左孩子连线,删除其它孩保留结点的最左孩子连线,删除其它孩子连线;子连线;step3: step3: 将同一孩子的连线绕左孩子旋转将同一孩子的连线绕左孩子旋转4545度角。度角。加线加线抹线抹线旋转旋转讨论讨论1 1:树如何转为二叉树?:树如何转为二叉树?292方法:方法:加加线线抹线抹线旋转旋转 abeidfhgc树转二叉树举例:树转二叉树举例:abeidfhgc兄弟相连兄弟相连长兄为父长兄为父孩子靠左孩子靠左根结点肯定没根结点肯定没根结点肯定没根结点肯定没有右孩子!有右孩子!有右孩子!有右孩子!293讨论讨论2 2:二叉树怎样还原为树?:二叉树怎样还原为树?abeidfhgc要点:把所有右孩子变为兄弟!要点:把所有右孩子变为兄弟! abeidfhgc294法一:法一: 各森林先各自转为二叉树;各森林先各自转为二叉树; 依次连到前一个二叉树的右子树上。依次连到前一个二叉树的右子树上。讨论讨论3 3:森林如何转为二叉树?:森林如何转为二叉树?法二:法二:森林直接变兄弟,再转为二叉树森林直接变兄弟,再转为二叉树(参见教材(参见教材P138P138图图6.176.17,两种方法都有转换示意图),两种方法都有转换示意图)即即F=TF=T1 1, T, T2 2, ,T, ,Tm m B=root, LB, RB B=root, LB, RB295ABCDEFGHJIABCDEFGHJIA ABCDEFGHJI森林转二叉树举例:森林转二叉树举例:(法二)(法二)兄弟相连兄弟相连 长兄为父长兄为父孩子靠左孩子靠左 头根为根头根为根 A A296讨论讨论4 4:二叉树如何还原为森林?:二叉树如何还原为森林?要点:要点:把最右边的子树变为森林,其余右子树变为兄弟把最右边的子树变为森林,其余右子树变为兄弟 ABCDEFGHJIABCDEFGHJIEFABCDGHJI即即B=root, LB, RB F=TB=root, LB, RB F=T1 1, T, T2 2, ,T, ,Tm m 2972. 2. 树和森林的存储方式树和森林的存储方式树有三种常用存储方式:树有三种常用存储方式:双亲表示法双亲表示法 孩子表示法孩子表示法 孩子兄弟表示法孩子兄弟表示法 (1)(1)用双亲表示法来存储用双亲表示法来存储思路:思路:用一组用一组连续空间连续空间来存储树的结点,同时在每个来存储树的结点,同时在每个结点中结点中附设一个指示器附设一个指示器,指示其双亲结点在链表中的,指示其双亲结点在链表中的位置。位置。parentsdata结点结构结点结构dataparents1树结构树结构 23n298A AB BC CGGE EI ID DHHF F缺点:求结点的孩子时需要遍历整个结构。缺点:求结点的孩子时需要遍历整个结构。01234567812233ABCDEFGHI-1001例例1: 双亲表示法双亲表示法299思路:将每个结点的孩子排列起来,形成一个带表头思路:将每个结点的孩子排列起来,形成一个带表头(装父结点)的线性表(装父结点)的线性表(n n个结点要设立个结点要设立n n个链表);个链表);再将再将n n个表头用数组存放起来,这样就形成一个混合个表头用数组存放起来,这样就形成一个混合结构。结构。例如例如:abecdfhgdefghgfedcbah12345678(2)(2)用孩子表示法来存储用孩子表示法来存储bc300思路:思路:用二叉链表来表示树,但链表中的两个用二叉链表来表示树,但链表中的两个指针域含义不同。指针域含义不同。左指针指向该结点的第一个孩子;左指针指向该结点的第一个孩子;右指针指向该结点的下一个兄弟结点。右指针指向该结点的下一个兄弟结点。nextsiblingdatafirstchild(3)(3)用孩子兄弟表示法来存储用孩子兄弟表示法来存储指向左孩子指向左孩子指向右兄弟指向右兄弟301abecdfhgbacedfgh问:问:树树转转二叉树的二叉树的“连线连线抹线抹线旋转旋转” ” 如如何由计算机自动实现?何由计算机自动实现?答:答:用用“左孩子右兄弟左孩子右兄弟”表示法来存储即可。表示法来存储即可。 存储的过程就是转换的过程!存储的过程就是转换的过程!例如例如:302先序遍历先序遍历F若森林为空,返回;若森林为空,返回;F访问森林中第一棵树的根结点;访问森林中第一棵树的根结点;F先序遍历第一棵树中根结点的子树森林;先序遍历第一棵树中根结点的子树森林;F先序遍历除去第一棵树之后剩余的树构成的森林。先序遍历除去第一棵树之后剩余的树构成的森林。中序遍历中序遍历F若森林为空,返回;若森林为空,返回;F中序遍历森林中第一棵树的根结点的子树森林;中序遍历森林中第一棵树的根结点的子树森林;F访问第一棵树的根结点;访问第一棵树的根结点;F中序遍历除去第一棵树之后剩余的树构成的森林。中序遍历除去第一棵树之后剩余的树构成的森林。3.3.森林的遍历森林的遍历ABCDEFGHJI303路路 径径:路径长度路径长度:树的路径长度树的路径长度:带权路径长度带权路径长度:树的带权路径长度树的带权路径长度:霍霍 夫夫 曼曼 树树:6.5 Huffman6.5 Huffman树及其应用树及其应用一、最优二叉树(一、最优二叉树(霍夫曼霍夫曼树)树)由一结点到另一结点间的分支所构成由一结点到另一结点间的分支所构成路径上的分支数目路径上的分支数目从树根到从树根到每一结点每一结点的路径长度之和。的路径长度之和。结点到根的路径长度与结点上权的乘积结点到根的路径长度与结点上权的乘积预备知识:若干术语预备知识:若干术语debacf g树中所有树中所有叶子结点叶子结点的带权路径长度之和的带权路径长度之和带权路径长度最小的树。带权路径长度最小的树。aeae的路径长度的路径长度树长度树长度2 21010304HuffmanHuffman树简介:树简介:树的带权路径长度如何计算?树的带权路径长度如何计算?WPLWPL = = w wk kl lk k k=1k=1n nabdc7524(a)cdab2457(b)bdac7524(c)经典之例:经典之例:经典之例:经典之例:WPL=36WPL=46WPL= 35哈夫曼树则是:哈夫曼树则是:WPL WPL 最小的树。最小的树。Huffman树树Weighted Path LengthWeighted Path Length305(1)(1) 由给定的由给定的由给定的由给定的 n n 个权值个权值个权值个权值 w w0 0, , w w1 1, , w w2 2, , , , w wn n-1-1 ,构造具有,构造具有,构造具有,构造具有 n n 棵扩棵扩棵扩棵扩充二叉树的充二叉树的充二叉树的充二叉树的森林森林森林森林F F = = T T0 0, , T T1 1, , T T2 2, , , , T Tn n-1 -1 ,其中每一棵扩充二,其中每一棵扩充二,其中每一棵扩充二,其中每一棵扩充二叉树叉树叉树叉树 T Ti i 只有一个带有权值只有一个带有权值只有一个带有权值只有一个带有权值 w wi i 的根结点,其左、右子树均为空。的根结点,其左、右子树均为空。的根结点,其左、右子树均为空。的根结点,其左、右子树均为空。 (2)(2) 重复以下步骤重复以下步骤重复以下步骤重复以下步骤, , 直到直到直到直到 F F 中仅剩下一棵树为止:中仅剩下一棵树为止:中仅剩下一棵树为止:中仅剩下一棵树为止: 在在在在 F F 中中中中选取两棵根结点的权值最小选取两棵根结点的权值最小选取两棵根结点的权值最小选取两棵根结点的权值最小的扩充二叉树的扩充二叉树的扩充二叉树的扩充二叉树, , 做为左、做为左、做为左、做为左、右子树构造一棵新的二叉树。置新的二叉树的右子树构造一棵新的二叉树。置新的二叉树的右子树构造一棵新的二叉树。置新的二叉树的右子树构造一棵新的二叉树。置新的二叉树的根结点的权值为根结点的权值为根结点的权值为根结点的权值为其左、右子树上根结点的权值之和其左、右子树上根结点的权值之和其左、右子树上根结点的权值之和其左、右子树上根结点的权值之和。 在在在在 F F 中删去这两棵二叉树。中删去这两棵二叉树。中删去这两棵二叉树。中删去这两棵二叉树。 把新的二叉树加入把新的二叉树加入把新的二叉树加入把新的二叉树加入 F F。构造霍夫曼树的基本思想:构造霍夫曼树的基本思想:构造构造HuffmanHuffman树的步骤(即树的步骤(即HuffmanHuffman算法):算法):权值大的结点用短路径,权值小的结点用长路径权值大的结点用短路径,权值小的结点用长路径权值大的结点用短路径,权值小的结点用长路径权值大的结点用短路径,权值小的结点用长路径。先举例!先举例!306例例1:设有设有4 4个字符个字符d,i,a,nd,i,a,n,出现的频度分别为,出现的频度分别为7,5,2, 7,5,2, 4 4,怎样编码才能使它们组成的报文在网络中传得最快?,怎样编码才能使它们组成的报文在网络中传得最快?法法1 1:等长编码等长编码。例如用二进制编码来实现。例如用二进制编码来实现。 取取 d= d=0000,i=i=0101,a=a=1010,n=n=1111怎样实现怎样实现HuffmanHuffman编码?编码?法法2 2:不等长编码不等长编码,例如用哈夫曼编码来实现。,例如用哈夫曼编码来实现。 取取 d= d=0 0; i=; i=1010, a=, a=110110, n=, n=111111最快的编码是哪个?最快的编码是哪个?是非等长的是非等长的HuffmanHuffman码!码!先要构造先要构造HuffmanHuffman树!树!307操作要点操作要点1 1:对权值的合并、删除与替换对权值的合并、删除与替换在权值集合在权值集合7,5,2,47,5,2,4中,总是合并中,总是合并当前值最小当前值最小的两个权的两个权构造构造HuffmanHuffman树的步骤:树的步骤:注:方框表示外结点(叶子,字符对应的权值),注:方框表示外结点(叶子,字符对应的权值), 圆框表示内结点(合并后的权值)。圆框表示内结点(合并后的权值)。308操作要点操作要点2 2:按左按左0 0右右1 1对对HuffmanHuffman树的所有分支编号!树的所有分支编号!d da ai in n1 11 11 10 00 00 0HuffmanHuffman编码结果:编码结果:d=d=0 0, i=, i=1010, a=, a=110110, n=, n=111111WPL=1bit7WPL=1bit72bit5+3bit(2+4)=2bit5+3bit(2+4)=3535特点:每一码都不是另一码的前缀,绝不会错译特点:每一码都不是另一码的前缀,绝不会错译! ! 称为前缀码称为前缀码将将将将 HuffmanHuffman树树 与与 Huffman Huffman编码编码 挂钩挂钩309例例2 2(严题集(严题集6.266.26):假设用于通信的电文仅由假设用于通信的电文仅由8 8个字母个字母 a, b, c, d, e, f, g, ha, b, c, d, e, f, g, h 构成,它们在电文中出现的概率分构成,它们在电文中出现的概率分别为别为 0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10 0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10,试为这试为这8 8个字母设计哈夫曼编码。个字母设计哈夫曼编码。如果用如果用0 07 7的二进制编码方案的二进制编码方案又如何?又如何?霍夫曼霍夫曼编码的基本思想是:编码的基本思想是:概率大的字符用短码,概率小的用概率大的字符用短码,概率小的用长码。由于长码。由于霍夫曼树的霍夫曼树的WPLWPL最小,最小,说明编码所需要的比特数最说明编码所需要的比特数最少。这种编码已广泛应用于网络通信中。少。这种编码已广泛应用于网络通信中。解:解:先将概率放大先将概率放大100100倍,以方便构造哈夫曼树。倍,以方便构造哈夫曼树。权值集合权值集合 w=7, 19, 2, 6, 32, 3, 21, 10 w=7, 19, 2, 6, 32, 3, 21, 10,按哈夫曼树构造规则(合并、删除、替换),可得到哈夫曼树。按哈夫曼树构造规则(合并、删除、替换),可得到哈夫曼树。310w4=19, 21, w4=19, 21, 28,28, 32 32为清晰起见,重新排序为为清晰起见,重新排序为: :w=2, 3, 6, 7, 10, 19, 21, 32w=2, 3, 6, 7, 10, 19, 21, 322 23 35 56 6w1=w1=5,5, 6, 7, 10, 19, 21, 32 6, 7, 10, 19, 21, 32w2=7, 10, w2=7, 10, 11,11, 19, 21, 32 19, 21, 32w3=w3=11, 17,11, 17, 19, 21, 32 19, 21, 32111110107 717172828212119194040w5=w5=28,28,32,32,4040 32326060w6=w6=40,6040,60 w7=w7=100100 100100b bc ca ad de eg gf fh h哈夫曼树哈夫曼树 311对应的哈夫曼编码(左对应的哈夫曼编码(左0右右1):):2 23 35 56 6111110107 7323217172828212140406060100100b bc ca ad de eg gf fh h0 00 00 00 00 01 11 11 11 11 11 11 10 00 0符符编码编码频率频率a0.07b0.19c0.02d0.06e0.32f0.03g0.21h0.10符符编码编码频率频率a0.07b0.19c0.02d0.06e0.32f0.03g0.21h0.10Huffman码的码的WPL2(0.19+0.32+0.21) + 4(0.07+0.06+0.10) +5(0.02+0.03) =1.44+0.92+0.25=2.61 WPL3(0.19+0.32+0.21+0.07+0.06+0.10+0.02+0.03)=3 11001100000011110111101110111010101111111111010111011101000001010011100101110111二进制码二进制码二进制码二进制码312另一种结果表示:另一种结果表示:313例例3 3(实验二方案(实验二方案3 3):设字符集为设字符集为2626个英文字母,其出个英文字母,其出现频度如下表所示。现频度如下表所示。注:若圆满实现了此方案,平时成绩将以满分计。注:若圆满实现了此方案,平时成绩将以满分计。51481156357203251频度频度zyxwvut t字符字符11611882380频度频度p21fq15gr47hsonmlkj字符字符5710332221364186频度频度ie edcba空格空格空格空格字符字符先建哈夫曼树,再利用此树对报文先建哈夫曼树,再利用此树对报文“This program is my favorite”进行编码和译码。进行编码和译码。要求编程实现:要求编程实现:314提示提示1 1:霍夫曼树中各结点的结构霍夫曼树中各结点的结构可以定义为如下可以定义为如下5 5个分量:个分量:charweightparentlchildRchild将整个将整个霍夫曼树的霍夫曼树的结点结点存储在一个数组中:存储在一个数组中:HT1.n;HT1.n; 将结点的将结点的编码编码存储在存储在HC1.nHC1.n中。中。提示提示3 3:霍夫曼树如何构造?霍夫曼树如何构造?构造好之后又如何求得构造好之后又如何求得各结点对应的霍夫曼编码?各结点对应的霍夫曼编码?算法参见教材算法参见教材P147P147。提示提示2 2:霍夫曼树的霍夫曼树的存储结构存储结构可采用可采用顺序存储顺序存储结构:结构:实验二补充材料中的方案二程序;实验二补充材料中的方案二程序;喻信星空喻信星空FTPFTP网站上的网站上的“数据结构数据结构”演示程序演示程序参考参考参考参考资料资料资料资料315二叉树小结1 1、定义和性质、定义和性质2 2、存储结构、存储结构3 3、遍历、遍历4 4、线索化、线索化:线索树:线索树顺序结构顺序结构链式结构链式结构二叉链表二叉链表三叉链表三叉链表先序线索树先序线索树中序线索树中序线索树后序线索树后序线索树树二叉树二叉树森林中序遍历中序遍历后序遍历后序遍历先序遍历先序遍历霍夫曼树霍夫曼树霍夫曼编码霍夫曼编码316 完成第完成第6 6章自测卷有关内容,算法设章自测卷有关内容,算法设计题一定要写出思路。计题一定要写出思路。作作 业业317void iter_inorder(tree_pointer node) int top= -1; /* initialize stack */ tree_pointer stackMAX_STACK_SIZE; for (;) for (; node; node=node-left_child) add(&top, node);/* add to stack */ node= delete(&top); /* delete from stack */ if (!node) break; /* empty stack */ printf(“%D”, node-data); node = node-right_child; 时间复杂度时间复杂度O(n)附:中序遍历迭代算法附:中序遍历迭代算法(利用堆栈利用堆栈)318void level_order(tree_pointer ptr)/* level order tree traversal */ int front = rear = 0; tree_pointer queueMAX_QUEUE_SIZE; if (!ptr) return; /* empty queue */ addq(front, &rear, ptr); for (;) ptr = deleteq(&front, rear);+ * E * D / C A B附:层序遍历算法附:层序遍历算法(利用队列利用队列)319数据结构课程的内容数据结构课程的内容多对多多对多(m:n)3207.1 7.1 基本术语基本术语7.2 7.2 存储结构存储结构7.3 7.3 图的遍历图的遍历7.4 7.4 图的其他运算图的其他运算7.5 7.5 图的应用图的应用第第7 7章章 图图3217.1 7.1 图的基本术语图的基本术语图:图:记为记为 G( V, E ) 其中:其中:V 是是G的顶点集合,是有穷非空集;的顶点集合,是有穷非空集;E 是是G的边的边集合,是有穷集。集合,是有穷集。问:问:当当E(G)E(G)为空时,图为空时,图G G存在否?存在否?答:答:还存在!但此时图还存在!但此时图G G只有顶点而没有边。只有顶点而没有边。有向图:有向图:无向图:无向图:完全图:完全图:图图G G中的每条边都是有方向的;中的每条边都是有方向的;图图G G中的每条边都是无方向的;中的每条边都是无方向的;图图G G任意两个顶点都有一条边相连接;任意两个顶点都有一条边相连接;vv若若若若 n n 个顶点的无向图有个顶点的无向图有个顶点的无向图有个顶点的无向图有 n n( (n n-1)/2 -1)/2 条边条边条边条边, , 称为称为称为称为无向完全图无向完全图无向完全图无向完全图vv若若若若 n n 个顶点的有向图有个顶点的有向图有个顶点的有向图有个顶点的有向图有n(n-1) n(n-1) 条边条边条边条边, , 称为称为称为称为有向完全图有向完全图有向完全图有向完全图V=vertex E=edge322证明: 完全有向图有完全有向图有完全有向图有完全有向图有n(n-1)n(n-1)条边条边条边条边。证明:证明:证明:证明:若是完全有向图,则顶点若是完全有向图,则顶点若是完全有向图,则顶点若是完全有向图,则顶点1 1必与所有其他顶点各有必与所有其他顶点各有必与所有其他顶点各有必与所有其他顶点各有2 2条连条连条连条连线,即有线,即有线,即有线,即有2(n-1)2(n-1)条边,条边,条边,条边, 顶点顶点顶点顶点2 2有有有有2(n-2)2(n-2)条边,条边,条边,条边,顶点,顶点,顶点,顶点n-1n-1有有有有2 2条条条条边边边边, ,顶点顶点顶点顶点n n有有有有0 0条边条边条边条边. .总边数总边数总边数总边数2( n-12( n-1 n-2 n-21 10)=2(n-1+0)n/2= 0)=2(n-1+0)n/2= n(n-1)n(n-1)完全无向图有完全无向图有完全无向图有完全无向图有n n( (n n-1)/2 -1)/2 条边条边条边条边。证明:证明:证明:证明:若是完全无向图,则顶点若是完全无向图,则顶点若是完全无向图,则顶点若是完全无向图,则顶点1 1必与所有其他顶点各有必与所有其他顶点各有必与所有其他顶点各有必与所有其他顶点各有1 1条连条连条连条连线,即有线,即有线,即有线,即有n-1n-1条边,顶点条边,顶点条边,顶点条边,顶点2 2有有有有n-2n-2条边,条边,条边,条边,顶点,顶点,顶点,顶点n-1n-1有有有有1 1条边条边条边条边, ,顶顶顶顶点点点点n n有有有有0 0条边条边条边条边. .总边数总边数总边数总边数 n-1 n-1 n-2 n-21 10=0=(n-1+0)n/2= n-1+0)n/2= n(n-1)/2n(n-1)/2 323例:例:判断下列判断下列4种图形各属什么类型?种图形各属什么类型?无向无向无向图无向图(树树)有向图有向图有向有向n n( (n n-1)/2 -1)/2 条边条边条边条边n n( (n n-1) -1) 条边条边条边条边G1G1的顶点集合为的顶点集合为V(G1)V(G1)=0,1,2,3=0,1,2,3边集合为边集合为E(G1)E(G1)=(0,1),(0,2),(0,3),(1,2),(1,3),(2,3)=(0,1),(0,2),(0,3),(1,2),(1,3),(2,3)完全图完全图完全图完全图324稀疏图:稠密图: 设有两个图设有两个图 G(V, E) 和和 G(V, E)。若。若 V V 且且 E E, 则称则称 图图G 是是 图图G 的子图。的子图。子 图:边较少的图。通常边数边较少的图。通常边数= E = 1/2 E = 1/2TD(vTD(vi i) ) 答:答:是树!而且是一棵有向树!是树!而且是一棵有向树!327生成树:生成树:是一个是一个是一个是一个极小连通子图极小连通子图极小连通子图极小连通子图,它含有图中全部顶点,但只,它含有图中全部顶点,但只,它含有图中全部顶点,但只,它含有图中全部顶点,但只 有有有有n n-1-1条边条边条边条边。 如果在生成树上添加如果在生成树上添加如果在生成树上添加如果在生成树上添加1 1条边,必定构成一个环。条边,必定构成一个环。条边,必定构成一个环。条边,必定构成一个环。 若图中有若图中有若图中有若图中有n n个顶点,却少于个顶点,却少于个顶点,却少于个顶点,却少于n n-1-1条边,必为非连通图。条边,必为非连通图。条边,必为非连通图。条边,必为非连通图。生成森林:生成森林:由若干棵生成树组成,含全部顶点,但构成这些由若干棵生成树组成,含全部顶点,但构成这些由若干棵生成树组成,含全部顶点,但构成这些由若干棵生成树组成,含全部顶点,但构成这些 树的边是最少的。树的边是最少的。树的边是最少的。树的边是最少的。328简单路径:路径上各顶点路径上各顶点 v v1 1,v,v2 2,.,v,.,vm m 均不互相重复。均不互相重复。回 路:例:例:若路径上第一个顶点若路径上第一个顶点 v v1 1 与最后一个顶点与最后一个顶点v vm m 重合,重合,则称这样的路径为则称这样的路径为回路或环回路或环。路径:在图在图在图在图 G G(V, E) (V, E) 中中中中, , 若从顶点若从顶点若从顶点若从顶点 vi vi 出发出发出发出发, , 沿一些边经过一些沿一些边经过一些沿一些边经过一些沿一些边经过一些顶点顶点顶点顶点 vp1, vp2, , vpm vp1, vp2, , vpm,到达顶点,到达顶点,到达顶点,到达顶点vjvj。则称顶点序列。则称顶点序列。则称顶点序列。则称顶点序列 ( vi ( vi vp1 vp2 . vpm vj ) vp1 vp2 . vpm vj ) 为从顶点为从顶点为从顶点为从顶点vi vi 到顶点到顶点到顶点到顶点 vj vj 的路径。它经的路径。它经的路径。它经的路径。它经过的边过的边过的边过的边(vi, vp1)(vi, vp1)、(vp1, vp2)(vp1, vp2)、. .、(vpm, vj)(vpm, vj)应当是属应当是属应当是属应当是属于于于于E E的边。的边。的边。的边。路径长度:非带权图的路径长度是指此路径上非带权图的路径长度是指此路径上非带权图的路径长度是指此路径上非带权图的路径长度是指此路径上 边的条数边的条数边的条数边的条数;带权图的路径长度是指路径上带权图的路径长度是指路径上带权图的路径长度是指路径上带权图的路径长度是指路径上各边的权之和各边的权之和各边的权之和各边的权之和。329ADT Graph 数据对象数据对象V V:v v是具有相同特性的数据元素的集合,称为顶点集。是具有相同特性的数据元素的集合,称为顶点集。数据关系数据关系R R:R=VRR=VR;VR=VR= |v,wV |v,wV 且且 P(v,w)P(v,w), , 表示从表示从v v到到w w的弧,的弧, 谓词谓词P(v,w)P(v,w)定义了弧定义了弧的意义或信息的意义或信息 基本操作基本操作P P: CreatGraph ( &G, V,VR); 初始条件:初始条件:V V是图的顶点是图的顶点集集,VRVR是图中弧的集合。是图中弧的集合。 操作结果:操作结果:按按V V和和VRVR的定义构造图的定义构造图G G。 InsertVex ( &G, v); 初始条件:初始条件:图图G G存在,存在,v v和图中顶点有相同特征。和图中顶点有相同特征。 操作结果:操作结果:在图在图G G中添加新顶点。中添加新顶点。 (参见(参见P156-257P156-257) 图的抽象数据类型图的抽象数据类型注意:注意:V V 的的大小写含义大小写含义不同!不同!3307.2 7.2 图的存储结构图的存储结构图的特点:非线性结构(图的特点:非线性结构(m :n m :n )邻接表邻接表邻接多重表邻接多重表十字链表十字链表设计为邻接矩设计为邻接矩阵阵链式存储结构:链式存储结构:顺序存储结构:顺序存储结构: 无!无!(多个顶点,无序可言)(多个顶点,无序可言)但可但可用用数组数组描述元素间关系。描述元素间关系。可用多重链表可用多重链表重点介绍:重点介绍:邻接矩阵邻接矩阵( (数组数组) )表示法表示法邻接表邻接表( (链式链式) )表示法表示法331一、邻接矩阵(数组)表示法一、邻接矩阵(数组)表示法一、邻接矩阵(数组)表示法一、邻接矩阵(数组)表示法vv建立一个建立一个建立一个建立一个顶点表(记录各个顶点信息)顶点表(记录各个顶点信息)顶点表(记录各个顶点信息)顶点表(记录各个顶点信息)和一个和一个和一个和一个邻接矩阵(表邻接矩阵(表邻接矩阵(表邻接矩阵(表示各个顶点之间关系)示各个顶点之间关系)示各个顶点之间关系)示各个顶点之间关系)。vv设图设图设图设图 A = (A = (V V, , E E) ) 有有有有 n n 个顶点,则图的邻接矩阵是一个二维数个顶点,则图的邻接矩阵是一个二维数个顶点,则图的邻接矩阵是一个二维数个顶点,则图的邻接矩阵是一个二维数组组组组 A.EdgennA.Edgenn,定义为:定义为:定义为:定义为:v1v2v3v5v4v4A例例例例1 1:邻接矩阵:邻接矩阵:邻接矩阵:邻接矩阵:A.Edge =( v1 v2 v3 v4 v5 )v1v2v3v4v50 0 0 0 00 0 0 0 00 0 0 0 00 0 0 0 00 0 0 0 0分析分析分析分析1 1:无向图的邻接矩阵是无向图的邻接矩阵是无向图的邻接矩阵是无向图的邻接矩阵是对称对称对称对称的;的;的;的;分析分析分析分析2 2:顶点顶点顶点顶点i i 的的的的度度度度第第第第 i i 行行行行 ( (列列列列) ) 中中中中1 1 的个数;的个数;的个数;的个数;特别:特别:特别:特别:完全图的邻接矩阵中,对角元素为完全图的邻接矩阵中,对角元素为完全图的邻接矩阵中,对角元素为完全图的邻接矩阵中,对角元素为0 0,其余全,其余全,其余全,其余全1 1。0 1 0 1 01 0 1 0 10 1 0 1 11 0 1 0 10 1 1 1 00 1 0 1 01 0 1 0 10 1 0 1 11 0 1 0 10 1 1 1 0顶点表:顶点表:顶点表:顶点表:332例例2 :有向图的邻接矩阵有向图的邻接矩阵分析分析分析分析1 1 1 1:有向图的邻接矩阵有向图的邻接矩阵可能是不对称可能是不对称的。的。分析分析分析分析2 2 2 2:顶点的出度顶点的出度=第第i行元素之和行元素之和,OD( Vi )= A.Edge i j 顶点的入度顶点的入度=第第i列元素之和列元素之和。ID( Vi )= A.Edge j i 顶点的度顶点的度= =第第i i行元素之和行元素之和+ +第第i i列元素之和列元素之和, , 即:即:TD(Vi)=OD( Vi ) + ID( Vi )v1v2v3v4A A邻接矩阵:A.Edge =( v1 v2 v3 v4 )v1v2v3v40 0 0 00 0 0 0 0 0 0 0 0 0 0 0 注:注:在有向图的邻接矩阵中,在有向图的邻接矩阵中, 第第i i行含义:以结点行含义:以结点v vi i为尾的弧为尾的弧( (即出度边);即出度边); 第第i i列含义:以结点列含义:以结点v vi i为头的弧为头的弧( (即入度边)。即入度边)。顶点表:0 1 1 00 0 0 0 0 0 0 1 1 0 0 0 0 1 1 00 0 0 0 0 0 0 1 1 0 0 0 333 容易实现图的操作,如:求某顶点的度、判断容易实现图的操作,如:求某顶点的度、判断顶点之间是否有边(弧)、找顶点的邻接点等等。顶点之间是否有边(弧)、找顶点的邻接点等等。 n n个顶点需要个顶点需要n*nn*n个单元存储边个单元存储边( (弧弧););空间效率空间效率为为O(nO(n2 2) )。 对稀疏图而言尤其浪费空间。对稀疏图而言尤其浪费空间。特别讨论特别讨论 :网(即有权图)的邻接矩阵网(即有权图)的邻接矩阵定义为:定义为: A.Edge i j =Wij 或(或(vi, vj)VR 无边(弧)无边(弧)v1v2v3v4Nv5v65489755613以有向网为例:以有向网为例:邻接矩阵:邻接矩阵: N.Edge =( v1 v2 v3 v4 v5 v6 )邻接矩阵法邻接矩阵法优点:优点:邻接矩阵法邻接矩阵法缺点:缺点:顶点表:顶点表: 5 7 4 8 9 5 6 5 3 1 5 7 4 8 9 5 6 5 3 1 334注:注:用两个数组分别存储顶点表和邻接矩阵用两个数组分别存储顶点表和邻接矩阵#define INFINITY INT_MAX /最大值最大值#define MAX_VERTEX_NUM 20 /假设的最大顶点数假设的最大顶点数Typedef enum DG, DN, AG,AN GraphKind; /有向有向/ /无向图,有向无向图,有向/ /无向网无向网Typedef struct ArcCell /弧(边)弧(边)结点的定义结点的定义 VRType adj; /顶点间关系,无权图取顶点间关系,无权图取1 1或或0 0;有权图取权值类型;有权图取权值类型 InfoType *info; /该弧相关信息的指针该弧相关信息的指针ArcCell, AdjMatrix MAX_VERTEX_NUM MAX_VERTEX_NUM ;Typedef struct /图的定义图的定义VertexType vexs MAX_VERTEX_NUM ; /顶点表,用一维向量即可顶点表,用一维向量即可AdjMatrix arcs; /邻接矩阵邻接矩阵Int Vernum, arcnum; /顶点总数,弧(边)总数顶点总数,弧(边)总数GraphKind kind; /图的种类标志图的种类标志Mgraph; 图的邻接矩阵存储表示图的邻接矩阵存储表示(参见教材(参见教材P161)对于对于n n个顶点的图或网,空间效率个顶点的图或网,空间效率=O(n=O(n2 2) )335Status CreateUDN(Mgraph &G) /无向网的构造,用邻接矩阵表示无向网的构造,用邻接矩阵表示scanf(&G.vexnum, &G.arcnum, &IncInfo); /输入总顶点数,总弧数和信息输入总顶点数,总弧数和信息for(i=0;iG.vexnum,;+i) scanf(&G.vexsi );/);/输入顶点值,存入一维向量中输入顶点值,存入一维向量中for(i=0; iG.vexnum; +i) /对邻接矩阵对邻接矩阵n*nn*n个单元初始化,个单元初始化,adj=,info=NULL for(j=0;jG.vexnum;+j) G.arcsij=INFINITY, NULL; for(k=0;kG.arcnum;+k) /给邻接矩阵有关单元赋初值给邻接矩阵有关单元赋初值( (循环次数弧数循环次数弧数 scanf(&v1, &v2, &w); /输入弧的两顶点以及对应权值输入弧的两顶点以及对应权值 i=LocateVex(G,v1); j=LocateVex(G,v2); /找到两顶点在矩阵中的位置找到两顶点在矩阵中的位置(n(n次次? ? G.arcsij.adj=w; /输入对应权值输入对应权值 If(IncInfo) Input(*G.arcsij.info); /如果弧有信息则填入如果弧有信息则填入 G.arcsij = G.arcs j i; /无向网是对称矩阵无向网是对称矩阵 return OK; / CreateUDN/ CreateUDN 例:例:用邻接矩阵用邻接矩阵生成无向网生成无向网的算法的算法(参见教材(参见教材P162)对于对于n n个顶点个顶点e e条弧的网,条弧的网,建网时间效率建网时间效率 = O(n = O(n2 2+n+e*n)+n+e*n)336二、邻接表(链式)表示二、邻接表(链式)表示二、邻接表(链式)表示二、邻接表(链式)表示法法法法vv对每个顶点对每个顶点对每个顶点对每个顶点v vi i 建立一个建立一个建立一个建立一个单链表单链表单链表单链表,把与,把与,把与,把与v vi i有关联的有关联的有关联的有关联的边的信息边的信息边的信息边的信息(即(即(即(即度或出度边)度或出度边)度或出度边)度或出度边)链接链接链接链接起来,表中每个结点都设为起来,表中每个结点都设为起来,表中每个结点都设为起来,表中每个结点都设为3 3个域;个域;个域;个域;vv每个单链表还应当附设一个每个单链表还应当附设一个每个单链表还应当附设一个每个单链表还应当附设一个头结点头结点头结点头结点(设为(设为(设为(设为2 2个域),存个域),存个域),存个域),存v vi i信息;信息;信息;信息;adjvexnextarcinfodatafirstarc表结点表结点表结点表结点头结点头结点头结点头结点邻接点域,邻接点域,表表示示v vi i一个邻接一个邻接点的位置点的位置链域,链域,指向指向vivi下一个边下一个边或弧的结点或弧的结点数据域,数据域,与与边有关信息边有关信息(如权值)(如权值)数据域,存数据域,存储顶点储顶点vi 信信息息链域,链域,指向指向单链表的第单链表的第一个结点一个结点vv 每个单链表的每个单链表的每个单链表的每个单链表的头结点另外用顺序存储头结点另外用顺序存储头结点另外用顺序存储头结点另外用顺序存储结构存储。结构存储。结构存储。结构存储。337例例例例1 1:无向图的邻接表无向图的邻接表无向图的邻接表无向图的邻接表v1v2v3v5v4v4邻接表邻接表0123413341420例例例例2 2:有向图的邻接表有向图的邻接表有向图的邻接表有向图的邻接表v1v2v3v4V4V3V2V12301邻接表邻接表( (出边出边) )V4V3V2V13020逆邻接表逆邻接表( (入边入边) )注:注:邻接表不唯一,因各个边结点的链入顺序是任意的。邻接表不唯一,因各个边结点的链入顺序是任意的。邻接表不唯一,因各个边结点的链入顺序是任意的。邻接表不唯一,因各个边结点的链入顺序是任意的。v1v2v3v4v5231420338例例例例3 3:已知某网的邻接(出边)表,请画出该网络。已知某网的邻接(出边)表,请画出该网络。已知某网的邻接(出边)表,请画出该网络。已知某网的邻接(出边)表,请画出该网络。8064125当邻接表当邻接表的存储结的存储结构形成后,构形成后,图便唯一图便唯一确定!确定!339分析分析1 1: :对于对于n n个顶点个顶点e e条边的无向图,邻接表中除了条边的无向图,邻接表中除了n n个头结点外,个头结点外,只有只有2e2e个表结点个表结点, ,空间效率为空间效率为O(n+2e)O(n+2e)。若是稀疏图若是稀疏图(en(en2 2) ),则比邻接矩阵表示法,则比邻接矩阵表示法O(nO(n2 2) )省空间。省空间。邻接表存储法的特点:邻接表存储法的特点:分析分析2 2: :在有向图中,邻接表中除了在有向图中,邻接表中除了n n个头结点外,只有个头结点外,只有e e个表结点个表结点, ,空间效率为空间效率为O(n+e)O(n+e)。若是稀疏图,则比邻接矩阵表示法合适。若是稀疏图,则比邻接矩阵表示法合适。它其实是对邻接矩阵法的一种改进它其实是对邻接矩阵法的一种改进怎样计算无向图顶点的度?怎样计算无向图顶点的度?邻接表的邻接表的缺点:缺点:怎样计算有向图顶点的出度?怎样计算有向图顶点的出度?怎样计算有向图顶点的入度?怎样计算有向图顶点的入度?怎样计算有向图顶点怎样计算有向图顶点ViVi的度:的度:需遍历全表需遍历全表邻接表的邻接表的优点:优点:TD(Vi)TD(Vi)= =单链表中链接的结点个数单链表中链接的结点个数OD(Vi)单链出边表中链接的结点数单链出边表中链接的结点数I D( Vi ) 邻接点为邻接点为ViVi的弧个数的弧个数TD(Vi) = OD( Vi ) + I D( Vi )空间效率高;空间效率高;容易寻找顶点的邻接点;容易寻找顶点的邻接点;判断两顶点间是否有边或弧,需搜索两判断两顶点间是否有边或弧,需搜索两结点对应的单链表,没有邻接矩阵方便。结点对应的单链表,没有邻接矩阵方便。340讨论:讨论:邻接表与邻接矩阵有什么异同之处?邻接表与邻接矩阵有什么异同之处?1. 1. 联系:联系:邻接表中每个链表对应于邻接矩阵中的一行,邻接表中每个链表对应于邻接矩阵中的一行,链表中结点个数等于一行中非零元素的个数。链表中结点个数等于一行中非零元素的个数。2. 2. 区别:区别: 对于任一确定的无向图,邻接矩阵是唯一的(行列对于任一确定的无向图,邻接矩阵是唯一的(行列号与顶点编号一致),但邻接表不唯一(链接次序号与顶点编号一致),但邻接表不唯一(链接次序与顶点编号无关)。与顶点编号无关)。 邻接矩阵的空间复杂度为邻接矩阵的空间复杂度为O(nO(n2 2),),而邻接表的空间复而邻接表的空间复杂度为杂度为O(n+e)O(n+e)。3. 3. 用途:用途:邻接矩阵多用于稠密图的存储(邻接矩阵多用于稠密图的存储(e e接近接近n(n-n(n-1)/2)1)/2);而邻接表多用于稀疏图的存储(;而邻接表多用于稀疏图的存储(enen2 2) )341图的邻接表存储表示图的邻接表存储表示(参见教材(参见教材P163P163)#define MAX_VERTEX_NUM 20 /假设的最大顶点数假设的最大顶点数Typedef struct ArcNode int adjvex; /该弧所指向的顶点位置该弧所指向的顶点位置 struct ArcNode *nextarcs; /指向下一条弧的指针指向下一条弧的指针 InfoArc *info; /该弧相关信息的指针该弧相关信息的指针 ArcNode;Typedef struct VNode /顶点结构顶点结构 VertexType data; /顶点信息顶点信息 ArcNode * firstarc; /指向依附该顶点的第一条弧的指针指向依附该顶点的第一条弧的指针VNode, AdjList MAX_VERTEX_NUM ; Typedef struct /图结构图结构 AdjList vertics ; /应包含邻接表应包含邻接表 int vexnum, arcnum; /应包含顶点总数和弧总数应包含顶点总数和弧总数 int kind; /还应说明图的种类(用标志)还应说明图的种类(用标志)ALGraph; /图结构图结构空间效率为空间效率为O(n+2e)O(n+2e)或或O(n+e)O(n+e)时间效率为时间效率为O(n+e*n)O(n+e*n)342 它是它是有向图有向图的另一种链式结构。的另一种链式结构。 思路:思路:将邻接矩阵用链表存储,是邻接表、逆邻接表的结合。将邻接矩阵用链表存储,是邻接表、逆邻接表的结合。1 1、每条弧对应一个结点、每条弧对应一个结点( (称为弧结点,设称为弧结点,设5 5个域)个域)2 2、每个顶点也对应一个结点(称为顶点结点,设、每个顶点也对应一个结点(称为顶点结点,设3 3个域)个域)tailvexheadvexHlinktlinkinfo顶点结点顶点结点弧结点弧结点三、十字链表三、十字链表tailvex: 弧尾顶点位置弧尾顶点位置 headvex: 弧头顶点位置弧头顶点位置hlink: 弧头相同的下一弧位置弧头相同的下一弧位置tlink: 弧尾相同的下一弧位置弧尾相同的下一弧位置info: 弧信息弧信息 n n个顶点个顶点用顺序存储结构用顺序存储结构 data : 存储顶点信息。存储顶点信息。Firstin : 以顶点为弧头的第一条弧结点。以顶点为弧头的第一条弧结点。Firstout: 以顶点为弧尾的第一条弧结点。以顶点为弧尾的第一条弧结点。dataFirstinFirstout适用于有向图适用于有向图343#define MAX_VERTEX_NUM 20Typedef struct ArcBox /弧结点结构弧结点结构 int tailvex , headvex ; struct ArcBox * hlink , tlink; InfoType *info; ArcBox;Typedef struct VexNode /顶点结构顶点结构 VertexType data; ArcBox * firstin,*firstout;VexNode; Typedef struct VexNode xlist MAX_VERTEX_NUM ; /表头向量表头向量 int vexnum, arcnum;OLGraph; /图结构图结构十字链表存储结构描述:十字链表存储结构描述:3440v11v22v33v4v1v2v3v40 1022 023例:例:画出有向图的十字链表。画出有向图的十字链表。十字链表十字链表优点:优点:容易操作,如求顶点的入度、出度等。容易操作,如求顶点的入度、出度等。 空间复杂度与邻接表相同;建立的时间复杂度与邻接表相同。空间复杂度与邻接表相同;建立的时间复杂度与邻接表相同。303132数组下标不数组下标不属结点分量属结点分量345这是这是无向图无向图的另一种存储结构,当对边操作时,无向图应采用此种结的另一种存储结构,当对边操作时,无向图应采用此种结构存储。构存储。 1 1、每条边只对应一个结点(称为、每条边只对应一个结点(称为边结点边结点),设立),设立6 6个域个域; 2 2、每个顶点也对应一个结点(、每个顶点也对应一个结点(顶点结点顶点结点),设立),设立2 2个域;个域;markivexilinkjvexjlinkinfo边结点边结点四、邻接多重表四、邻接多重表mark:标志域,如处理过或搜索过。标志域,如处理过或搜索过。Ivex, jvex : 顶点域,边依附的两个顶点位置。顶点域,边依附的两个顶点位置。 ilink: 指向下一条依附顶点指向下一条依附顶点 i i 的边结点位置。的边结点位置。jlink: 指向下一条依附顶点指向下一条依附顶点 j j 的边结点位置。的边结点位置。info: 边信息,如权值等。边信息,如权值等。n n个顶点个顶点用顺序存储结构用顺序存储结构 data : 存储顶点信息。存储顶点信息。Firstedge : 依附顶点的第一条依附顶点的第一条边结点。边结点。dataFirstedge顶点结点顶点结点适用于适用于无向图无向图3460v11v22v33v44v501031214232434v1v2v3v5v4v4例:例:画出无向图的邻接多重表画出无向图的邻接多重表邻接多重表邻接多重表优点:优点:容易操作,如求顶点的度等。容易操作,如求顶点的度等。 空间复杂度与邻接表相同;空间复杂度与邻接表相同;建立的时间复杂度与邻接表相同。建立的时间复杂度与邻接表相同。数组下标不数组下标不属结点分量属结点分量347一、深度优先搜索二、广度优先搜索 7.3 图的遍历图的遍历遍历遍历定义:定义:从已给的连通图中某一顶点出发,沿着一些边访遍从已给的连通图中某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图中所有的顶点,且使每个顶点仅被访问一次,就叫做图图图图的遍历的遍历的遍历的遍历,它是,它是,它是,它是图的图的基本运算基本运算。遍历遍历实质:实质:找每个顶点的邻接点的过程。找每个顶点的邻接点的过程。图的图的特点:特点:图中可能存在图中可能存在回路回路,且图的任一顶点都可能与其它,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点到了曾经访问过的顶点。解决思路:解决思路:可设置一个可设置一个辅助数组辅助数组 visited n ,用来标记每个被,用来标记每个被访问过的顶点。它的初始状态为访问过的顶点。它的初始状态为0 0,在图的遍历过程中,在图的遍历过程中,一旦某一个顶点一旦某一个顶点i 被访问,就立即改被访问,就立即改 visited i为为1 1,防止,防止它被多次访问。它被多次访问。图常用的遍历:图常用的遍历:怎样避免重复访问?怎样避免重复访问?348一、深度优先搜索一、深度优先搜索一、深度优先搜索一、深度优先搜索( ( ( ( DFSDFSDFSDFS ) ) ) )基本思想:基本思想:仿树的先序遍历过程。仿树的先序遍历过程。Depth_First Searchv1v1v2v3v8v7v6v4v5DFS DFS 结果结果结果结果例例例例1 1 1 1:v2v4v8v5v3v6v7例例例例2 2 2 2:v2 v1 v3 v5 v2 v1 v3 v5 DFS DFS 结果结果结果结果v4 v4 v6 v6起点起点起点起点遍历步骤遍历步骤349深度优先搜索(遍历)步骤:深度优先搜索(遍历)步骤:详细归纳:详细归纳:E在访问图中某一起始顶点在访问图中某一起始顶点 v 后,由后,由 v 出发,访问出发,访问它的任一邻接它的任一邻接顶点顶点 w1;E再从再从 w1 出发,访问出发,访问与与 w1邻接邻接但还但还未被访问未被访问过的顶点过的顶点 w2;E然后再从然后再从 w2 出发,进行类似的访问,出发,进行类似的访问, E如此进行下去,直至到达所有的邻接顶点都被访问过如此进行下去,直至到达所有的邻接顶点都被访问过为止。为止。E接着,退回一步,接着,退回一步,退到前一次刚访问过的顶点退到前一次刚访问过的顶点,看是否还有其,看是否还有其它没有被访问的邻接顶点。它没有被访问的邻接顶点。 如果有,如果有,则访问此顶点,之后再从此顶点出发,进行与前述则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;类似的访问; 如果没有,如果没有,就就再退回一步再退回一步进行搜索。重复上述过程,直到连进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止。通图中所有顶点都被访问过为止。简单归纳:简单归纳:1)1)访问起始点访问起始点 v v; ;2)2)若若v v的第的第1 1个邻接点没访问过,深度遍历此邻接点;个邻接点没访问过,深度遍历此邻接点;3)3)若当前邻接点已访问过,再找若当前邻接点已访问过,再找v v的第的第2 2个邻接点重新遍历。个邻接点重新遍历。350讨论讨论讨论讨论1 1:计算机如何实现计算机如何实现计算机如何实现计算机如何实现DFSDFS?1 2345610 000002 20 0000030 0000040 0000050 0000060 0000000000012345601000011 100001 11 110001 11 11 10101 11 11 111 101 11 11 11 11 11DFS DFS 结果结果结果结果邻邻接接矩矩阵阵A辅助数组辅助数组 visited n 起点起点v2v1v3v5v2v1v3v5v4v4v6v6开辅助数组开辅助数组 visited n !例:例:1 23 456101 11 11 1002 21 1 00 01 1031 1 00 01 1041 1 00 001 1501 11 1 00060 001 100351讨论讨论讨论讨论2 2:在图的邻接表中如何进行在图的邻接表中如何进行在图的邻接表中如何进行在图的邻接表中如何进行DFSDFS?v0 v1 v2 v3v0 v1 v2 v3DFS DFS 结果结果结果结果00000123辅助数组辅助数组 visited n 1000110011101111例:例:照样借用照样借用visited n !起点起点0123注意:注意:在邻接表中,并非每个在邻接表中,并非每个链表元素(表结点)都被扫描链表元素(表结点)都被扫描到到, ,遍历速度很快。遍历速度很快。352深度优先遍历图的算法深度优先遍历图的算法: 课本课本P169页页Boolean visited MAX; / / 访问标志数组访问标志数组Status (*VisitFunc) (int v); /函数变量函数变量Void DFSTraverse( Graph G, Status (*Visit) (int v) / / 对图对图G G做深度优先遍历做深度优先遍历 VisitFunc = Visit; / / 使用全局变量使用全局变量VisitFunc,VisitFunc,使使DFSDFS不必设函数指针参数不必设函数指针参数 for (v=0; vG.vexnum; +v) visitedv = FALSE; / / 访问标志数组初始化访问标志数组初始化 for (v=0; v=0; w = NextAdjVex (G, v, w) ) if (!visitedw) DFS(G,w); / / 对对v v的尚未访问的邻接点的尚未访问的邻接点w w递归调用递归调用DFSDFS 354DFS DFS 算法效率分析算法效率分析算法效率分析算法效率分析: :(设图中有设图中有设图中有设图中有 n n 个顶点,个顶点,个顶点,个顶点,e e 条边条边条边条边)n如果用如果用邻接矩阵邻接矩阵来表示图,遍历图中每一个顶点来表示图,遍历图中每一个顶点都要都要从头扫描从头扫描该顶点所在行,因此遍历全部顶点该顶点所在行,因此遍历全部顶点所需的时间为所需的时间为O(n2)。n如果用如果用邻接表邻接表来表示图,虽然有来表示图,虽然有 2e 个表结点,但个表结点,但只需扫描只需扫描 e 个结点即可完成遍历,加上访问个结点即可完成遍历,加上访问 n个个头结点的时间,因此遍历图的时间复杂度为头结点的时间,因此遍历图的时间复杂度为O(n+e)。结论:结论:稠密图稠密图适于在邻接矩阵上进行深度遍历;适于在邻接矩阵上进行深度遍历;稀疏图稀疏图适于在邻接表上进行深度遍历。适于在邻接表上进行深度遍历。355二、广度优先搜索二、广度优先搜索二、广度优先搜索二、广度优先搜索( ( ( ( BFS BFS BFS BFS ) ) ) )基本思想:基本思想:仿树的层次遍历过程。仿树的层次遍历过程。Breadth_First Searchv1v1v2v3v8v7v6v4v5BFS BFS 结果结果结果结果例例例例1 1 1 1:v2v3v4v5v6v7v8例例例例2 2 2 2:v3 v3 BFS BFS 结果结果结果结果v4v4 v5 v5 起点起点遍历步骤遍历步骤起点起点v2 v1 v6 v2 v1 v6 v9 v8 v7v9 v8 v7356广度优先搜索(遍历)步骤:广度优先搜索(遍历)步骤:简单归纳:简单归纳:在访问了起始点在访问了起始点v v之后,依次访问之后,依次访问 v v的邻接点;的邻接点;然后再依次访问这些顶点中未被访问过的邻接点;然后再依次访问这些顶点中未被访问过的邻接点;直到所有顶点都被访问过为止直到所有顶点都被访问过为止。广度优先搜索是一种分层的搜索过程,每向前走一步可广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有回退的情况。能访问一批顶点,不像深度优先搜索那样有回退的情况。因此,广度优先搜索不是一个递归的过程,其算法也不因此,广度优先搜索不是一个递归的过程,其算法也不是递归的。是递归的。357讨论讨论1 1:计算机如何实现计算机如何实现BFSBFS?邻接表邻接表除辅助数组除辅助数组visited n 外,外,还需再开一辅助队列!还需再开一辅助队列!例:例:起点起点辅助队列辅助队列v2v2已访问过了已访问过了BFS BFS 遍历结果遍历结果遍历结果遍历结果入队!入队!初始初始f=n-1,r=0358讨论讨论2: BFS算法如何编程?算法如何编程?Void BFSTraverse( Graph G, Status (* Visit) (int v) ) for ( v=0; vG.vexnum; +v ) visitedv = FALSE ; InitQuene(Q); / / 置空的辅助队列置空的辅助队列Q Q for ( v=0; v=0; w=NextAdjVex(G,u,w) if (! Visited w ) / w/ w为为u u的尚未访问的邻接顶点的尚未访问的邻接顶点 Visited w = TRUE; Visit(w); EnQuene (Q,w); /if /while / if / BFSTraverse层次遍历应当用队列!层次遍历应当用队列!(见课本(见课本P170P170)359BFS BFS 算法效率分析算法效率分析算法效率分析算法效率分析: :DFSDFS与与与与BFSBFS之比较:之比较:之比较:之比较:空间复杂度相同,都是空间复杂度相同,都是O(n)(O(n)(借用了堆栈或队列);借用了堆栈或队列);时间复杂度只与存储结构(邻接矩阵或邻接表)有时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关。关,而与搜索路径无关。如果使用邻接表来表示图,则如果使用邻接表来表示图,则BFS循环的总时间代价为循环的总时间代价为 d0 + d1 + + dn-1 = O(e),其中的其中的 di 是顶点是顶点 i 的度的度。如果使用邻接矩阵,则如果使用邻接矩阵,则BFS对于每一个被访问到的顶点,对于每一个被访问到的顶点,都要循环检测矩阵中的整整一行(都要循环检测矩阵中的整整一行( n 个元素),总的个元素),总的时间代价为时间代价为O(n2)。(设图中有设图中有设图中有设图中有 n n 个顶点,个顶点,个顶点,个顶点,e e 条边条边条边条边)3607.4 7.4 图的其他运算图的其他运算1. 求图的生成树求图的生成树2. 求最小生成树求最小生成树3. 求最短路径求最短路径3611. 求图的生成树求图的生成树生成树的特征生成树的特征n n 个顶点的个顶点的连通网络连通网络的生成的生成 树有树有 n n 个顶点、个顶点、n-n-1 1 条边。条边。求生成树的方法求生成树的方法DFSDFS(深度优先搜索)和(深度优先搜索)和 BFS BFS(广度优先搜索)(广度优先搜索)对于对于连通图连通图, ,仅需从图中任一顶点出发仅需从图中任一顶点出发, ,进行进行DFSDFS或或BFS, BFS, 便可访问到图中所有顶点便可访问到图中所有顶点. .对于对于非连通图非连通图, ,需从多个顶点出发进行搜索需从多个顶点出发进行搜索, ,而每一次而每一次 从一个新的起始点出发进行搜索过程中从一个新的起始点出发进行搜索过程中 得到的顶点访问序得到的顶点访问序 列恰为其各个连通列恰为其各个连通 分量中的顶点集分量中的顶点集. .362连通图的生成树连通图的生成树: : 按照按照DFS/BFSDFS/BFS方式图中方式图中所有顶点所有顶点和遍和遍历图过程中历图过程中历经的边历经的边构成该图的构成该图的DFS/BFSDFS/BFS生成树生成树. . - - 教材教材P172 P172 算法算法7.87.8非连通图的生成森林非连通图的生成森林: : 每个每个连通分量连通分量中的顶点集和中的顶点集和遍历时走过的边一起构成若干棵遍历时走过的边一起构成若干棵生成树生成树, ,这些连通分这些连通分量的生成树组成非连通图的生成森林量的生成树组成非连通图的生成森林. . - - 教材教材P171 P171 算法算法7.77.7363连通图的深度优先生成树连通图的深度优先生成树Void DFSTree(Graph G, int v, CSTree &T) / 从第从第v个顶点出发深度优先遍历图个顶点出发深度优先遍历图G,建立以建立以T为根的生成树为根的生成树 visitedv = TRUE; first = TRUE; for ( w=FirstAdjVex(G,v); w=0; w=NextAdjVex(G,v,w) ) if (!visitedw) p = (CSTree) malloc (sizeof (CSNode); / 分配孩子结点分配孩子结点 *p = GetVex(G,w), NULL, NULL ; if ( first) 3642. 2. 求最小生成树求最小生成树目标:目标:在网的多个生成树中,寻找一个在网的多个生成树中,寻找一个各边权值之和最各边权值之和最小小的生成树。的生成树。应用:应用: n n个城市建网,如何选择个城市建网,如何选择n1n1条线路,使总费用最少?条线路,使总费用最少?vKruskal(克鲁斯卡尔克鲁斯卡尔)算法)算法vPrim(普里姆普里姆)算法)算法 算法:算法:任何一个无向连通图的最小生成树只有一种任何一个无向连通图的最小生成树只有一种归并边归并边归并顶点归并顶点365KruskalKruskal(克鲁斯卡尔)算法(克鲁斯卡尔)算法例例 :1 14 46 65 52 23 31 15 56 65 55 54 46 63 36 62 21 15 54 43 32 21 13 35 52 24 46 6(V1,V3)(V1,V3)(V1,V3)(V1,V3)(V4,V6)(V4,V6)(V4,V6)(V4,V6)(V2,V5)(V2,V5)(V2,V5)(V2,V5)(V3,V6)(V3,V6)(V3,V6)(V3,V6)(V2,V3)(V2,V3)(V2,V3)(V2,V3)366计算机内怎样实现计算机内怎样实现KruskalKruskal算法?算法?算法设计思路:算法设计思路:算法设计思路:算法设计思路:见教材见教材见教材见教材P175P175P175P175最后一段最后一段最后一段最后一段. . . .特点:特点:将将边边归并归并适于求适于求稀疏网稀疏网的最小生成树。的最小生成树。 故采用故采用邻接表邻接表作为图的存储表示。作为图的存储表示。367(1 1)初始状态:)初始状态: U =u0 ,( u0 V ),), TE= ,(2 2)从)从E中选择顶点分别属于中选择顶点分别属于U、V-U两个集合、且权值两个集合、且权值最小的边最小的边( u0, v0),将顶点将顶点v0归并到集合归并到集合U中中,边边(u0, v0)归并到归并到TE中中;(3 3)直到)直到U=V为止。此时为止。此时TETE中必有中必有n-1n-1条边,条边, T(V,TE)就是最小生成树。就是最小生成树。设:设:N =N =(V , EV , E)是个连通网,)是个连通网,另设另设U U为最小生成树的顶点集,为最小生成树的顶点集,TETE为最小生成树的边集。为最小生成树的边集。构造步骤构造步骤: :普利姆(普利姆(PrimPrim)算法)算法368例:例:1 14 46 65 52 23 31 15 56 65 55 54 46 63 36 62 23 36 64 42 25 51 1 注注 :在最小生成树的生成过程中,所选的边都是:在最小生成树的生成过程中,所选的边都是 一端在一端在V-U中,另一端在中,另一端在U中。中。V1- V3- V6- V4- V2- V5V1- V3- V6- V4- V2- V5369设计思路:设计思路:设计思路:设计思路: 增设一辅助数组增设一辅助数组Closedge n , 每个每个数组分量都数组分量都有两个域:有两个域:要求:要求:使使Colsedge i.lowcost = min( ( u ,vi ) ) u U 计算机内怎样实现计算机内怎样实现PrimPrim(普里姆)算法?(普里姆)算法? PrimPrim算法特点算法特点: : 将顶点归并将顶点归并,与边数无关,适于稠密网。,与边数无关,适于稠密网。 故采用故采用邻接矩阵邻接矩阵作为图的存储表示。作为图的存储表示。adjvexadjvexlowcostlowcostv vi i 在在U中的邻点中的邻点u uColsedge iV-U中顶点中顶点v vi iu与与vi之间对应的边权之间对应的边权从从u1un中挑中挑370vexlowcotvexlowcotvexlowcotvexlowcotvexlowcotVexlowcotV-UU65432vclosedge1423561655536426具体示例:具体示例:具体示例:具体示例:算法实现参见:算法实现参见:教材教材P175 P175 算法算法7.97.912,3,4,5,6161115111, 32, 4, 5, 60351536341,3,62, 4,5350623601,3,6,42, 535036001,3,6,4,25000023000001,3,6,4,2,513起点起点46245235123456显然,显然, Prim Prim算法的时间效率算法的时间效率O O(n n2 2)371一顶点到其一顶点到其余各顶点余各顶点3. 3. 求最短路径求最短路径两种常见的最短路径问题:两种常见的最短路径问题:一、一、 单源最短路径单源最短路径用用Dijkstra(迪杰斯特拉)(迪杰斯特拉)算法算法二、所有顶点间的最短路径二、所有顶点间的最短路径用用Floyd(弗洛伊德)(弗洛伊德)算法算法典型用途:典型用途:交通问题。如:城市交通问题。如:城市A A到城市到城市B B有多条线路,但每条有多条线路,但每条线路的交通费(或所需时间)不同,那么,线路的交通费(或所需时间)不同,那么,如何选择一条线路,如何选择一条线路,使总费用(或总时间)最少?使总费用(或总时间)最少?问题抽象:问题抽象:在在带权有向图带权有向图中中A A点(源点)到达点(源点)到达B B点(终点)的多点(终点)的多条路径中,寻找一条条路径中,寻找一条各边权值之和最小各边权值之和最小的路径,即最短路径。的路径,即最短路径。(注:最短路径与最小生成树不同,路径上不一定包含(注:最短路径与最小生成树不同,路径上不一定包含n n个顶点)个顶点)任意两顶任意两顶点之间点之间372一、单源最短路径一、单源最短路径 ( (Dijkstra算法算法) )目的:目的: 设一设一有向图有向图G=G=(V, EV, E),),已知各边的权值,以某指已知各边的权值,以某指定点定点v v0 0为源点,求从为源点,求从v v0 0到图的其余各点的最短路径。到图的其余各点的最短路径。限定各限定各边上的权值大于或等于边上的权值大于或等于0 0。例例1 1:源点源点从从FAFA的路径有的路径有4 4条:条: FA FA: 24 24 FBA FBA: 5 518=2318=23 FBCA FBCA:5+7+9=215+7+9=21 FDCAFDCA:25+12+9=3625+12+9=36又:又:从从FBFB的最短路径是哪条?的最短路径是哪条?从从FCFC的最短路径是哪条?的最短路径是哪条?373v0(v0, v1)10源点源点终点终点 最最 短短路路 径径路径长度路径长度(v0, v1, v2) (v0, v3, v2)(v0, v3)30 v1 v2 v3 v4100(v0, v4)(v0, v3, v4)(v0, v3, v2, v4)例例2: 6001234 5090 70讨论:讨论:计算机如何自动求出这些最短路径?计算机如何自动求出这些最短路径?(v0, v1, v2, v4)60374Dijkstra(迪杰斯特拉)算法(迪杰斯特拉)算法算法思想:算法思想:先找出从源点先找出从源点v v0 0到各终点到各终点v vk k的直达路径(的直达路径(v v0 0,v,vk k),即),即通过一条弧到达的路径。通过一条弧到达的路径。从这些路径中找出一条长度最短的路径(从这些路径中找出一条长度最短的路径(v v0 0,u,u), ,然后然后对其余各条路径进行适当调整:对其余各条路径进行适当调整:若在图中存在弧(若在图中存在弧(u,vu,vk k),且(),且(v v0 0,u,u)+ +(u,vu,vk k) (v v0 0,v,vk k), ,则以路径(则以路径(v v0 0,u,v,u,vk k)代替()代替(v v0 0,v,vk k)。)。在调整后的各条路径中,再找长度最短的路径,依此在调整后的各条路径中,再找长度最短的路径,依此类推。类推。总之,按路径总之,按路径“长度长度” ” 递增的次序来逐步产生最短路径递增的次序来逐步产生最短路径375算法描述:算法描述:(1 1)设设AnnAnn为有向网的带权邻接矩阵,为有向网的带权邻接矩阵,AijAij表示弧(表示弧(vi,vj ) )的权值,的权值,S S为已找到从源点为已找到从源点v v0 0出发的最短路径的终点集合,它的初出发的最短路径的终点集合,它的初始状态为始状态为 v v0 0 .辅助数组辅助数组distn为各终点当前找到的最短路径的长为各终点当前找到的最短路径的长度,它的初始值为度,它的初始值为: distiAAv v0 0 ,i,i /即邻接矩阵中第即邻接矩阵中第v v0 0行的权值行的权值(2 2)选择选择u u,使得,使得 distu distumindistw | wV-S mindistw | wV-S / w/ w是是S S集之外的顶点集之外的顶点 / distu / distu是从源点是从源点v v0 0到到S S集外所有顶点的弧中最短的一条集外所有顶点的弧中最短的一条 S= S uS= S u /将将u u加入加入S S集集(3 3)对于所有不在对于所有不在S S中的终点中的终点w w,若,若 distu+ Au,w distw distu+ Au,w distw / / 即即(v v0 0,u),u)(u,w)(v(u,w)(v0 0,w),w)则修改则修改distwdistw为:为: distw distw distu+ Au,w distu+ Au,w(4 4)重复操作(重复操作(2 2)、()、(3 3)共)共n-1n-1次,由此求得从次,由此求得从v0v0到各终点到各终点的最短路径。的最短路径。376(v0,v2)+ (v2,v3)(v0,v3)终终点点 从从v0到各终点的到各终点的dist值和最短路径值和最短路径v1v2v3v4v5vjS S之外的当前最之外的当前最短路径之顶点短路径之顶点60v0,v2,v350v0,v4,v330v0,v490v0,v4, v560v0,v4,v3,v55540312100603010102050sv0,v2v0 ,v2 ,v4v0 ,v2 ,v4 ,v3 v0 ,v2 ,v4 ,v3 ,v510v0,v2 30v0,v4100v0, v5例例3:v2v4v3v5100v0, v5012345distwdistw 0 1 2 3 4 5与最小生成树的不同点:路径可能是累加的!与最小生成树的不同点:路径可能是累加的!10v0,v250v0,v4,v330v0,v4377二、二、所有顶点之间的所有顶点之间的最短路径最短路径问题的提出:问题的提出:已知一个各边权值均大于已知一个各边权值均大于0 0的带权有的带权有向图,对每一对顶点向图,对每一对顶点 vi vj,希望求出希望求出vi 与与vj之间之间的最短路径和最短路径长度。的最短路径和最短路径长度。解决思路:解决思路:可以通过可以通过调用调用n n次次Dijkstra算法算法来完成,但时间复杂来完成,但时间复杂度为度为O(n3)。改进:改进:Floyd算法(略)算法(略) 3787.5 图的应用图的应用1.拓扑排序拓扑排序2.求关键路径求关键路径379 AOE网网(Activity On Edges)用用边边表示活动的网络表示活动的网络AOEAOE网定义网定义:如果在无环的带权有向图中,如果在无环的带权有向图中, 用用有向边表示一个有向边表示一个工程中的活动,用边上权值表示活动持续时间,用顶点表示工程中的活动,用边上权值表示活动持续时间,用顶点表示事件事件,则这样的有向图叫做用边表示活动的网络,简称,则这样的有向图叫做用边表示活动的网络,简称 AOE。有两种常用的活动网络(有两种常用的活动网络( Activity Network):): AOV网网(Activity On Vertices)用用顶点顶点表示活动的网络表示活动的网络AOVAOV网定义:网定义:若用有向图表示一个工程,在图中若用有向图表示一个工程,在图中用顶点表示活用顶点表示活动,用弧表示活动间的优先关系。动,用弧表示活动间的优先关系。Vi 必须先于活动必须先于活动Vj 进行。进行。则这样的有向图叫做用顶点表示活动的网络,简称则这样的有向图叫做用顶点表示活动的网络,简称 AOV。380一一. AOV网网络络用途:用途:我们经常用有向图来描述一个工程或系统的进行过我们经常用有向图来描述一个工程或系统的进行过程。一般来说,一个工程可以分为若干个子工程,只要完程。一般来说,一个工程可以分为若干个子工程,只要完成了这些子工程,就可以导致整个工程的完成。成了这些子工程,就可以导致整个工程的完成。例:例:AOV网络若用于教学计划的制定,可以解决:网络若用于教学计划的制定,可以解决:哪些课程是必须先修的,哪些课程是可以并行学习的。哪些课程是必须先修的,哪些课程是可以并行学习的。预备知识:预备知识:拓扑排序拓扑排序什么叫拓扑排序?什么叫拓扑排序?对一个有向无环图中的顶点排成一个具有前后次序的对一个有向无环图中的顶点排成一个具有前后次序的线性序列。线性序列。381 课程编号课程编号 课程名称课程名称先决条件先决条件C1C1程序设计基础程序设计基础无无C2C2离散数学离散数学C1C1C3C3数据结构数据结构C1,C2C1,C2C4C4汇编语言汇编语言C1C1C5C5语言的设计和分析语言的设计和分析C3,C4C3,C4C6C6计算机原理计算机原理C11C11 课程编号课程编号 课程名称课程名称先决条件先决条件C7C7编译原理编译原理C5,C3C5,C3C8C8操作系统操作系统C3,C6C3,C6C9C9高等数学高等数学无无C10C10线性代数线性代数C9C9C11C11普通物理普通物理C9C9C12C12数值分析数值分析C9,C10,C1C9,C10,C1C1C4C2C3C5C9C7C12C10C11C6C8例:例:382进行拓扑排序的方法:进行拓扑排序的方法:1.输入输入AOV网络。令网络。令 n 为顶点个数。为顶点个数。2. 在在AOV网络中网络中选一个没有直接前驱的顶点选一个没有直接前驱的顶点, 并输出之并输出之; 3. 从图中删去该顶点从图中删去该顶点, 同时删去所有它发出的有向边同时删去所有它发出的有向边;4. 重复以上重复以上 2、3 步步, 直到:直到:u全部顶点均已输出,拓扑有序序列形成,拓扑排序全部顶点均已输出,拓扑有序序列形成,拓扑排序完成;或:完成;或:u图中还有未输出的顶点,但已跳出处理循环。这说图中还有未输出的顶点,但已跳出处理循环。这说明图中还剩下一些顶点,它们都有直接前驱,再也明图中还剩下一些顶点,它们都有直接前驱,再也找不到没有前驱的顶点了。这时找不到没有前驱的顶点了。这时AOV网络中必定存网络中必定存在有向环。在有向环。拓扑排序算法:拓扑排序算法:重复选择重复选择没有直接前驱的顶点。没有直接前驱的顶点。 383V1V2V4V3V6V5(1)V1V2V4V3V5(2)V2V4V3V5(3)V2V3V5(4)V2V5(5)V2(6)例:例:384二二. AOE网络网络用途:用途:常用于大型工程的计划管理。常用于大型工程的计划管理。利用利用AOE网络可以网络可以 解决以下两个问题:解决以下两个问题: (1) 完成整个工程至少需要多少时间完成整个工程至少需要多少时间(假设网络中没有环假设网络中没有环)? (2) 为缩短完成工程所需的时间为缩短完成工程所需的时间, 应当加快哪些活动应当加快哪些活动? 或者说,哪些活动是影响工程进度的关键?或者说,哪些活动是影响工程进度的关键? 预备知识:预备知识:关键路径关键路径385问题问题1:由于在由于在AOEAOE网中有些活动可以并行地进行,故网中有些活动可以并行地进行,故完成工程的完成工程的最短时间最短时间是从开始点到完成点的最长路径是从开始点到完成点的最长路径的长度,即关键路径的长度。的长度,即关键路径的长度。基本术语:基本术语:1.1.源点:源点:入度为零的点(工程的开始点,只有一个)。入度为零的点(工程的开始点,只有一个)。 汇点:汇点:出度为零的点(工程的完成点,只有一个)。出度为零的点(工程的完成点,只有一个)。2.2.路径长度路径长度: :指路径上各活动持续时间之和,指路径上各活动持续时间之和, 不是路径上弧的数目。不是路径上弧的数目。3.3.关键路径:关键路径:路径长度最长的路径。路径长度最长的路径。 关键活动:关键活动:满足满足l(s) = e(s)l(s) = e(s)的活动的活动as as 。3864.4. 活动活动a as s的的最早最早开始时间开始时间e(s)e(s): 若该活动在弧若该活动在弧上,则上,则: : 活动活动a as s的的最迟最迟开始时间开始时间l(s)l(s): 在不推迟整个工程完成的前提下,该活动最迟在不推迟整个工程完成的前提下,该活动最迟必须开始进行的时间。必须开始进行的时间。 e(s) = Ve(i)e(s) = Ve(i) l(s) = Vl(j) dut (al(s) = Vl(j) dut (as s) )3875.5.事件事件vivi的最早发生时间的最早发生时间Ve(i)Ve(i): 从开始点从开始点V1V1到到ViVi的最长路径长度。的最长路径长度。 求法:求法: 从从ve(0)=0ve(0)=0开始开始向前向前递推递推 6.6.事件事件vivi的最迟发生时间的最迟发生时间Vl(i)Vl(i): 求法:求法:从从vl(n-1)=ve(n-1)vl(n-1)=ve(n-1)起起向后向后递推递推ve(j) = Max ve(i)+dut() Tve(j) = Max ve(i)+dut() T 注:注:T T是所有以第是所有以第j j个顶点为头的弧的集合。个顶点为头的弧的集合。 vl(i) = Min vl(j)-dut() S vl(i) = Min vl(j)-dut() S 注:注:S S是所有以第是所有以第i i个顶点为尾的弧的集合。个顶点为尾的弧的集合。388问题问题2:2: 因为关键路径上的所有活动都是关键活动,所因为关键路径上的所有活动都是关键活动,所 以提前完成非关键活动并不能加快工程的进度。以提前完成非关键活动并不能加快工程的进度。要在最短时间内完成一项工程,需要做以下工作:要在最短时间内完成一项工程,需要做以下工作: 1. 1. 画出该工程各个子工程的有向无环网络;画出该工程各个子工程的有向无环网络; 2. 2. 求出各个顶点(即时间)的求出各个顶点(即时间)的veve和和vlvl并找到关键路径;并找到关键路径; 3. 3. 根据各顶点的根据各顶点的veve和和vlvl值,求出每条弧值,求出每条弧s s上活动的上活动的e(s) e(s) 和和l(s)l(s),由此找到关键活动。,由此找到关键活动。举例:举例:P186 图图7.30_7.31实现:实现:P184_185389a2 = 2a3 = 2a4 = 3a5 = 4a1 = 3a6 = 3a7 = 2a8 = 1V1V2V3V4V5V6V1V3V4V6a2a5a7顶点顶点 vevl v1 0 v2v3v4v5v63266842678活动活动 ell-ea10a2a3a4a5a6a7a8033226610442567101103010举例:举例:390图图存储结构存储结构遍遍 历历邻接矩阵邻接矩阵邻邻 接接 表表十字链表十字链表邻接多重表邻接多重表深度优先搜索深度优先搜索DFSDFS广度优先搜索广度优先搜索DFSDFS无向图的应用无向图的应用无向图的应用无向图的应用应用应用应用应用图的连通分量图的连通分量图的生成树图的生成树图的生成树图的生成树最小生成树最小生成树Prim算法算法Kruskal算法算法有向有向有向有向(无环无环无环无环)图的应用图的应用图的应用图的应用最短路径最短路径Dijkstra算法算法Floyd算法算法(利用(利用DFSDFS)本章小结本章小结( (利用利用DFSDFS和和BFS)BFS)391数据结构课程的内容数据结构课程的内容3928.1 8.1 基本概念基本概念8.2 8.2 静态查找表静态查找表8.3 8.3 动态查找表动态查找表8.4 8.4 哈希表哈希表第第8 8章章 查找查找教材第教材第8 8、1111和和1212章省略,因操作系统课程会涉及。章省略,因操作系统课程会涉及。3938.1 8.1 基本概念基本概念若表中存在特定元素,称查找成功,应输出该记录;若表中存在特定元素,称查找成功,应输出该记录;否则,称查找不成功(也应输出失败标志或失败位置)否则,称查找不成功(也应输出失败标志或失败位置)查找表查找表查查 找找查找成功查找成功查找不成功查找不成功静态查找静态查找动态查找动态查找关键字关键字主关键字主关键字次关键字次关键字由同一类型的数据元素(或记录)构成的集合。由同一类型的数据元素(或记录)构成的集合。查询查询(Searching)特定元素特定元素是否在表中。是否在表中。只查找,不改变集合内的数据元素。只查找,不改变集合内的数据元素。既查找,又改变(增减)集合内的数据元素。既查找,又改变(增减)集合内的数据元素。记录中某个数据项的值,可用来识别一个记录记录中某个数据项的值,可用来识别一个记录 ( 预先确定的记录的某种标志预先确定的记录的某种标志 ) 可以可以唯一唯一标识一个记录的关键字标识一个记录的关键字例如例如“学号学号”例如例如“女女”是一种数据结构是一种数据结构识别若干记录的关键字识别若干记录的关键字394(2)对查找表常用的操作有)对查找表常用的操作有哪些?哪些? v查询某个查询某个“特定的特定的”数据元素是否在表中;数据元素是否在表中;v查询某个查询某个“特定的特定的”数据元素的各种属性;数据元素的各种属性;v在查找表中插入一元素;在查找表中插入一元素;v从查找表中删除一元素。从查找表中删除一元素。 (3) 有哪些查找方法?有哪些查找方法? 查找方法取决于表中数据的排列方式查找方法取决于表中数据的排列方式;讨论:讨论:(1)查找的过程是怎样的?)查找的过程是怎样的? 给定一个值给定一个值K K,在含有,在含有n n个记录的文件中进行搜索,寻找个记录的文件中进行搜索,寻找一个关键字值等于一个关键字值等于K K的记录,如找到则输出该记录,否则输出的记录,如找到则输出该记录,否则输出查找不成功的信息。查找不成功的信息。例如查字典例如查字典针对静态查找表和动态查找表的查找方法也有所不同针对静态查找表和动态查找表的查找方法也有所不同。“特定的特定的”=关键关键字字395(4)如何评估查找方法的优劣?)如何评估查找方法的优劣?明确:明确:查找的过程就是将给定的查找的过程就是将给定的K K值与文件中各记录的关键字值与文件中各记录的关键字项进行比较的过程。所以用比较次数的平均值来评估算法的优项进行比较的过程。所以用比较次数的平均值来评估算法的优劣。称为劣。称为平均查找长度平均查找长度(ASLASL:average search lengthaverage search length)。)。其中:其中:n是文件记录个数;是文件记录个数;P Pi i是查找第是查找第i i个记录的查找概率(通常取等概率个记录的查找概率(通常取等概率, ,即即P Pi i =1/n =1/n); ;C Ci i是找到第是找到第i i个记录时所经历的比较次数。个记录时所经历的比较次数。统计意义上的统计意义上的数学期望值数学期望值物理意义:物理意义:假设每一元素被查找的概率相同,则查找每假设每一元素被查找的概率相同,则查找每一元素所需的比较次数之总和再取平均,即为一元素所需的比较次数之总和再取平均,即为ASLASL。显然,显然,ASLASL值越小,时间效率越高。值越小,时间效率越高。 396针对静态查找表的查找算法主要有:针对静态查找表的查找算法主要有: 8.2 8.2 静态查找表静态查找表静态查找表的抽象数据类型参见教材静态查找表的抽象数据类型参见教材P216。一、一、顺序查找(线性查找)顺序查找(线性查找)二、二、折半查找(二分或对分查找)折半查找(二分或对分查找)三、三、静态树表的查找静态树表的查找四、四、分块查找(索引顺序查找)分块查找(索引顺序查找)397一、顺序查找(一、顺序查找( Linear search,又称线性查找又称线性查找 )(1)顺序表的机内存储结构:顺序表的机内存储结构:typedef struct ElemType *elem; /表基址,表基址,0 0号单元留空。表容量为全部元素号单元留空。表容量为全部元素 int length; /表长,即表中数据元素个数表长,即表中数据元素个数SSTable;顺序查找:即用逐一比较的办法顺序查找关键字,这显然是最顺序查找:即用逐一比较的办法顺序查找关键字,这显然是最直接的办法。直接的办法。 v对对顺序结构顺序结构如何线性查找?如何线性查找?见下页之例见下页之例或教材或教材P216P216;v对对单单链链表表结结构构如如何何线线性性查查找找?函函数数虽虽未未给给出出,但但也也很很容容易易编写;只要知道头指针编写;只要知道头指针headhead就可以就可以“顺藤摸瓜顺藤摸瓜”;v对对非线性树结构非线性树结构如何顺序查找如何顺序查找? ?可借助各种遍历操作!可借助各种遍历操作!398(2)算法的实现:算法的实现:技巧:技巧:把待查关键字把待查关键字keykey存入表头或表尾(俗称存入表头或表尾(俗称“哨兵哨兵”),),这样可以加快执行速度。这样可以加快执行速度。例:例:若将待查找的特定值若将待查找的特定值keykey存入存入顺序表的顺序表的首部首部(如(如0 0号单元)号单元),则顺序查找的实现方案为:,则顺序查找的实现方案为:从后向前从后向前逐个比较!逐个比较!int Search_Seq( SSTable ST , KeyType key ) /在顺序表在顺序表STST中,查找关键字与中,查找关键字与keykey相同的元素;若成功,返回其位相同的元素;若成功,返回其位置信息,否则返回置信息,否则返回0 0 ST.elem0.key =key; /设立哨兵,可免去查找过程中每一步设立哨兵,可免去查找过程中每一步都要检测是否查找完毕。当都要检测是否查找完毕。当n1000n1000时,查找时间将减少一半。时,查找时间将减少一半。 for( i=ST.length; ST.elem i .key!=key; - - i ); /不要用不要用for(i=n; i0; - -i) for(i=n; i0; - -i) 或或 for(i=1; i=n; i+) for(i=1; i=n; i+) return i; /若到达若到达0 0号单元才结束循环,说明不成功,返回号单元才结束循环,说明不成功,返回0 0值值(i=0)(i=0)。成功时则返回找到的那个元素的位置。成功时则返回找到的那个元素的位置i i。 / Search_Seq399返回特殊标志,例如返回空记录或空指针。前例中设立了返回特殊标志,例如返回空记录或空指针。前例中设立了“哨哨兵兵”,就是将关键字送入末地址,就是将关键字送入末地址ST.elemST.elem0 0.key.key使之结束并返回使之结束并返回 i=0i=0。讨论讨论 查找效率怎样计算?查找效率怎样计算?用平均查找长度用平均查找长度ASL衡量。衡量。讨论讨论 查不到怎么办?查不到怎么办?讨论讨论 如何计算如何计算ASL?分析:分析:查找第查找第1个元素所需的比较次数为个元素所需的比较次数为1;查找第查找第2个元素所需的比较次数为个元素所需的比较次数为2;查找第查找第n个元素所需的比较次数为个元素所需的比较次数为n;总计全部比较次数为:总计全部比较次数为:12n = (1+n)n/2未考虑查找不成功的未考虑查找不成功的情况:查找哨兵所需情况:查找哨兵所需的比较次数为的比较次数为n+1n+1这是查找成功的情况这是查找成功的情况若求某一个元素的平均查找次数,还应当除以若求某一个元素的平均查找次数,还应当除以n(等概率),(等概率),即:即: ASL(1n)/2 ,时间效率为,时间效率为 O(n)400二、折半查找(又称二分查找或对分查找)二、折半查找(又称二分查找或对分查找)优点:优点:算法简单,且对顺序结构或链表结构均适用。算法简单,且对顺序结构或链表结构均适用。缺点:缺点: ASL 太长,时间效率太低。太长,时间效率太低。这是一种容易想到的查找方法。这是一种容易想到的查找方法。先给数据排序先给数据排序(例如按升序排好),形成(例如按升序排好),形成有序表有序表,然后再将,然后再将keykey与正中元素相比,若与正中元素相比,若keykey小,则缩小至右半部内查找;再取其中小,则缩小至右半部内查找;再取其中值比较,每次缩小值比较,每次缩小1/21/2的范围,直到查找成功或失败为止。的范围,直到查找成功或失败为止。v对对顺序表结构顺序表结构如何编程实现折半查找算法?如何编程实现折半查找算法? 见下页之例见下页之例,或见教材(,或见教材(P219P219)v对对单链表结构单链表结构如何折半查找?如何折半查找? 无法实现!因全部元素的定位只能从头指针无法实现!因全部元素的定位只能从头指针headhead开始开始v对对非线性非线性(树树)结构结构如何折半查找?如何折半查找? 可借助二叉排序树来查找(属动态查找表形式)。可借助二叉排序树来查找(属动态查找表形式)。 如何改进?如何改进?讨论讨论 顺序查找的特点:顺序查找的特点:401 运算步骤运算步骤:(1) low =1,high =11 ,mid =6 (1) low =1,high =11 ,mid =6 ,待查范围是,待查范围是 1,11 1,11;(2) (2) 若若 ST.elemmid.key ST.elemmid.key key keykey,说明,说明keykey low ,midlow ,mid-1-1 , 则令:则令:high =mid1high =mid1; ;重算重算 mid mid ;(4)(4)若若 ST.elem mid .key ST.elem mid .key = key= key,说明查找成功,元素序号,说明查找成功,元素序号=mid;=mid;结束条件:结束条件: (1 1)查找成功)查找成功 : ST.elemmid.key = key ST.elemmid.key = key (2 2)查找不成功)查找不成功 : highlowhighlow (意即区间长度小于(意即区间长度小于0 0)解:解: 先设定先设定3个辅助标志个辅助标志: low,high,midlow,high,mid,折半查找举例:折半查找举例:LowLow指向待查元素指向待查元素所在区间的下界所在区间的下界highhigh指向待查元素所指向待查元素所在区间的上界在区间的上界midmid指向待查元素所在指向待查元素所在区间的中间位置区间的中间位置 已知如下已知如下11个元素的个元素的有序表有序表:(05 13 19 21 37 56 64 75 80 88 92), 请查找关键字为请查找关键字为21 和和85的数据元素。的数据元素。显然有:显然有:mid= (low+high)/2 此例作为自测卷的算法题之一,建议上机验证。此例作为自测卷的算法题之一,建议上机验证。此例作为自测卷的算法题之一,建议上机验证。此例作为自测卷的算法题之一,建议上机验证。402讨论讨论 若关键字不在表中,怎样得知和停止?若关键字不在表中,怎样得知和停止?典型标志是:当查找范围的上界典型标志是:当查找范围的上界下界时停止查找。下界时停止查找。讨论讨论 二分查找的效率(二分查找的效率(ASLASL)1次比较就查找成功的元素有次比较就查找成功的元素有1个个(20),即),即中间值;中间值;2次比较就查找成功的元素有次比较就查找成功的元素有2个个(21),即),即1/4处(或处(或3/4)处;)处;3次比较就查找成功的元素有次比较就查找成功的元素有4个个(22),即),即1/8处(或处(或3/8)处)处 4次比较就查找成功的元素有次比较就查找成功的元素有8个个(23),即),即1/16处(或处(或3/16)处)处 则第则第m次比较时查找成功的元素会有次比较时查找成功的元素会有(2m-1)个个;为方便起见,假设表中全部为方便起见,假设表中全部n个元素个元素 2m-1个,个,此时就不讨论第此时就不讨论第m次比较后还有剩余元素的情况了。次比较后还有剩余元素的情况了。全部比较总次数为全部比较总次数为120221322423m2m1 推推导导过过程程403平均每个数据的查找时间还要除以平均每个数据的查找时间还要除以n,所以:,所以:(详细推导过程见教材(详细推导过程见教材P221的附录的附录1)课堂练习课堂练习(多项选择):(多项选择):采用链式存贮结构采用链式存贮结构 记录的长度记录的长度128 采用顺序存贮结构采用顺序存贮结构 记录按关键字递增有序记录按关键字递增有序使用折半查找算法时,要求被查文件:使用折半查找算法时,要求被查文件:思考:思考:假设在有序线性表假设在有序线性表a20上进行折半查找,则上进行折半查找,则平均查找长度为平均查找长度为 。404查找过程可用查找过程可用二叉树二叉树描述:每个记录用一个结点表示;结点中值为描述:每个记录用一个结点表示;结点中值为该记录在表中位置,这个描述查找过程的二叉树称为判定树。该记录在表中位置,这个描述查找过程的二叉树称为判定树。n个元个元素的表的折半查找的判定树是唯一的,即:判定树由表中元素个数素的表的折半查找的判定树是唯一的,即:判定树由表中元素个数决定。决定。 找到有序表中任一记录的过程就是找到有序表中任一记录的过程就是:走了一条从根结点到与该记录:走了一条从根结点到与该记录相应的结点的路径。相应的结点的路径。 比较的关键字个数:比较的关键字个数:为该结点在判定树上的层次数。为该结点在判定树上的层次数。 查找成功时查找成功时比较的关键字个数最多不超过树的深度比较的关键字个数最多不超过树的深度 d : d = log2 n + 1 若所有结点的空指针域设置为一个指向一个方形结点的指针,称若所有结点的空指针域设置为一个指向一个方形结点的指针,称方形结点为判定树的外部结点;对应的,圆形结点为内部结点。方形结点为判定树的外部结点;对应的,圆形结点为内部结点。 查找不成功的过程查找不成功的过程 就是走了一条从根结点到外部结点的路径。就是走了一条从根结点到外部结点的路径。折半查找效率分析法折半查找效率分析法2(参见教材(参见教材P220):):405三、静态树表的查找三、静态树表的查找讨论讨论2:当有序表中各记录的当有序表中各记录的查找概率不相等查找概率不相等时,该如何查找时,该如何查找?首先要明确,此时首先要明确,此时用折半查找法未必最优用折半查找法未必最优!(参见教材(参见教材P222例)例)改进:改进:若将各记录概率看作是二叉树之权值,则转为研究带若将各记录概率看作是二叉树之权值,则转为研究带权路径长度最小的二叉树(类似最优二叉树)。权路径长度最小的二叉树(类似最优二叉树)。讨论讨论1:对对有序表有序表还有其他查找算法吗?还有其他查找算法吗?静态最优查找树算法静态最优查找树算法时间代价高;时间代价高;实用算法:实用算法:近似最优查找树(近似最优查找树(次优次优查找树)查找树) (参见教材(参见教材P222225)有,如斐波那契查找和插值查找等有,如斐波那契查找和插值查找等(参见教材(参见教材P221222)406四、分块查找(索引顺序查找)四、分块查找(索引顺序查找)这是一种顺序查找的另一种改进方法。这是一种顺序查找的另一种改进方法。先让数据先让数据分块有序分块有序,即分成若干子表,要求每个子表中的数,即分成若干子表,要求每个子表中的数值(用关键字更准确)都比后一块中数值小(但子表内部未必值(用关键字更准确)都比后一块中数值小(但子表内部未必有序)。有序)。然后将各子表中的最大关键字构成一个然后将各子表中的最大关键字构成一个索引表索引表,表中还要包,表中还要包含每个子表的起始地址(即头指针)。含每个子表的起始地址(即头指针)。索引表最大关键字起始地址2212138920334244382448605874498653第第1 1块块第第2 2块块第第3 3块块224886例:例:2248861713特点:块间有特点:块间有序,块内无序序,块内无序407查找步骤查找步骤分两步进行:分两步进行: 对索引表使用折半查找法(因为索引表是有序表);对索引表使用折半查找法(因为索引表是有序表); 确定了待查关键字所在的子表后,在子表内采用顺序确定了待查关键字所在的子表后,在子表内采用顺序查找法(因为各子表内部是无序表);查找法(因为各子表内部是无序表);查找效率:查找效率:ASL=LASL=Lb b+L+Lw w对索引表查找的对索引表查找的ASL对块内查找的对块内查找的ASLS为每块内部的记录个数,为每块内部的记录个数,n/s即块的数目即块的数目例如当例如当n=9n=9,s=3s=3时时,ASLASLbsbs= =3.53.5, ,而折半法为而折半法为3.13.1, ,顺序法为顺序法为5 5408特点:特点:一、一、二叉排序树的定义二叉排序树的定义二、二、二叉排序树的插入与删除二叉排序树的插入与删除三、三、二二叉排序树的查找分析叉排序树的查找分析四、平衡二叉树四、平衡二叉树8.3 8.3 动态查找表动态查找表表结构在查找过程中动态生成。表结构在查找过程中动态生成。要求:要求: 对于给定值对于给定值key,key,若表中存在其关键字等于若表中存在其关键字等于keykey的记录,的记录,则查找成功返回;则查找成功返回;否则插入关键字等于否则插入关键字等于否则插入关键字等于否则插入关键字等于key key key key 的记录。的记录。的记录。的记录。典型的动态表典型的动态表二叉排序树二叉排序树409一、二叉排序树的定义一、二叉排序树的定义(a)(b)练:练:下列下列2种图形中,哪个不是二叉排序树种图形中,哪个不是二叉排序树 ?-或是一棵空树;或者是具有如下性质的非空二叉树:或是一棵空树;或者是具有如下性质的非空二叉树: (1 1)左子树的所有结点均小于根的值;)左子树的所有结点均小于根的值; (2 2)右子树的所有结点均大于根的值;)右子树的所有结点均大于根的值; (3 3)它的左右子树也分别为二叉排序树。)它的左右子树也分别为二叉排序树。讨论:讨论:对对(a)(a)中序遍历后的结果是什么?中序遍历后的结果是什么?410二、二叉排序树的查找、插入与删除二、二叉排序树的查找、插入与删除将数据元素构造成二叉排序树的优点:将数据元素构造成二叉排序树的优点: 查找过程与顺序结构有序表中的查找过程与顺序结构有序表中的折半查找折半查找相似,查找效率高;相似,查找效率高; 中序遍历中序遍历此二叉树,将会得到一个关键字的有序序列此二叉树,将会得到一个关键字的有序序列(即实现(即实现了排序运算)了排序运算);一个无序序列可以通过构造一棵二叉排序树而变成一个有序序一个无序序列可以通过构造一棵二叉排序树而变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。列,构造树的过程即为对无序序列进行排序的过程。如果查找不成功,能够方便地将被查元素如果查找不成功,能够方便地将被查元素插入到二叉树的叶子插入到二叉树的叶子结点结点上,而且插入或删除时只需修改指针而不需移动元素。上,而且插入或删除时只需修改指针而不需移动元素。注:注:若若数据元素的数据元素的输入顺序不同,则得到的二叉排序树形态输入顺序不同,则得到的二叉排序树形态也不同!也不同!这种既查找又插入的过程称为这种既查找又插入的过程称为动态查找动态查找。二叉排序树既有类似于折半查找的特性,又采用了链表存储,二叉排序树既有类似于折半查找的特性,又采用了链表存储,它是动态查找表的一种适宜表示。它是动态查找表的一种适宜表示。41145452424535312129090如果待如果待查找的关键字序列输入顺序为:查找的关键字序列输入顺序为:(2424,5353, 45 45,4545,1212,2424,9090),),24245353454512129090查查找找成成功,功,返返回回查查找找成成功,功,返返回回讨论讨论1 1:二叉排序树的插入和查找操作:二叉排序树的插入和查找操作 则生成二叉排序则生成二叉排序树的过程为:树的过程为:例例:输入待查找的关键字序列输入待查找的关键字序列= =(4545,2424,5353,4545,1212,2424,9090)则生成的二叉排则生成的二叉排序树形态不同:序树形态不同:查查找找成成功,功,返返回回查查找找成成功,功,返返回回412二叉排序树的查找二叉排序树的查找&插入算法如何实现?插入算法如何实现?查找算法参见教材查找算法参见教材P228算法算法9.5(a);插入算法参见教材插入算法参见教材P228算法算法9.5(b)_9.6;一种一种“二合一二合一”的算法如下:的算法如下:413BiTree SearchBST(BiTree T,KeyType key) / 若查找成功,则返回指向该数据元素结点的指针,若查找成功,则返回指向该数据元素结点的指针, 否则返回空指针否则返回空指针 if ( (!T) | EQ(key,Tdata.key) ) return(T); / 查找结束查找结束 else if LT(key,Tdata.key) / 在左子树中继续查找在左子树中继续查找 return ( SearchBST(Tlchild, key) ); else return ( SearchBST(Trchild, key) ); / 在右子树中继续查找在右子树中继续查找 / SearchBST414Status SearchBST( BiTree T, KeyType key, BiTree f, BiTree &p) if (!T) p = f;return FALSE; / 查找不成功查找不成功 else if EQ (key, Tdata.key) p=T;return TRUE; / 查找成功查找成功 else if LT (key, Tdata.key) return SearchBST(Tlchild, key, T,p); / 在左子树中继续查找在左子树中继续查找 else return SearchBST(Trchild, key, T, p); / 在右子树中继续查找在右子树中继续查找 / SearchBST415Status InsertBST (BiTree &T, ElemType e) if ( !SearchBST(T, e.key, NULL, p) ) / 查找不成功查找不成功 s = (BiTree) malloc (sizeof (BiTNode); sdata = e;slchild = srchild = NULL;/ 建立新结点建立新结点 if (!p) T = s; / T为空树为空树 else if LT (e.key, pdata.key) plchild = s;/ 被插结点被插结点*s为左孩子为左孩子 else prchild = s; /被插结点被插结点*s为右孩子为右孩子 return TRUE; else return FALSE;/ 树中已有关键字相同的结点,不再插入树中已有关键字相同的结点,不再插入 / Insert BST416对于二叉排序树,删除树上一个结点相当于删除有序序列中对于二叉排序树,删除树上一个结点相当于删除有序序列中的一个记录,删除后仍需保持二叉排序树的特性。的一个记录,删除后仍需保持二叉排序树的特性。(对链表进行删除操作是很方便的)(对链表进行删除操作是很方便的)讨论讨论2 2:二叉排序树的删除操作:二叉排序树的删除操作如何删除一个结点?如何删除一个结点?假设:假设:*p*p表示被删结点的指针;表示被删结点的指针; P PL和和PR 分别表示分别表示*P*P的左、右孩子指针;的左、右孩子指针;*f*f表示表示*p*p的双亲结点指针;并假定的双亲结点指针;并假定*p*p是是*f*f的的左孩子左孩子;则可能有三种情况:;则可能有三种情况:PR 为为 * S * S 的右子树;的右子树; S SL 为为 Q( * S * S的双亲结点)右孩子的双亲结点)右孩子*p*p为叶子:为叶子: 删除此结点时,直接修改删除此结点时,直接修改*f*f指针域即可;指针域即可;*p*p只有一棵子树(或左或右):只有一棵子树(或左或右):令令P PL或或PR为为*f*f的左孩子即可;的左孩子即可;*p*p有两棵子树:情况最复杂有两棵子树:情况最复杂 417*p*p有两棵子树时,如何进行删除操作有两棵子树时,如何进行删除操作?分析:分析:设删除前的中序遍历序列为:设删除前的中序遍历序列为:. s p PR . /显然显然显然显然p p p p的直接前驱是的直接前驱是的直接前驱是的直接前驱是s s s s希望删除希望删除p p后,其它元素的相对位置不变。有两种解决方法:后,其它元素的相对位置不变。有两种解决方法:法法1 1:令令*p*p的左子树为的左子树为 *f*f的左子树,的左子树,*p*p的右子树为的右子树为*s*s的右子的右子树;树; /即即即即F FL L=P=PL L ; F; FR R=P=PR R ; ;法法2 2:令令*s*s代替代替*p *p / *s*s*s*s为为为为*p*p*p*p左子树最右下的叶子左子树最右下的叶子左子树最右下的叶子左子树最右下的叶子 见课本见课本见课本见课本P230 P230 P230 P230 图图图图9.99.99.99.9418F FC CCLS SSLQLPPRQPRF FC CCLS SSLQLPPRQ法法2:2:F FC CCLS SSLQLPPRQ法法1:1:例:例:请从下面的二叉排序树中删除结点请从下面的二叉排序树中删除结点P。S SSL4191) 1) 二叉排序树上查找某关键字等于给定值的结点过程,其实二叉排序树上查找某关键字等于给定值的结点过程,其实就是走了一条从根到该结点的路径。就是走了一条从根到该结点的路径。 比较的关键字次数比较的关键字次数此结点的层次数此结点的层次数; ; 最多的比较次数最多的比较次数树的深度(或高度),即树的深度(或高度),即 loglog2 2 n n +1+12) 2) 一棵二叉排序树的平均查找长度为:一棵二叉排序树的平均查找长度为: 三、二叉排序树的查找分析三、二叉排序树的查找分析其中:其中:n ni i 是每层结点个数;是每层结点个数; C Ci i 是结点所在层次数;是结点所在层次数;m m 为树深。为树深。最坏情况:最坏情况:即插入的即插入的n个元素从一开始就有序,个元素从一开始就有序, 变成单支树的形态!变成单支树的形态! 此时树的深度为此时树的深度为n ; ASL= (n+1)/2 此时查找效率与顺序查找情况相同。此时查找效率与顺序查找情况相同。420最好情况:最好情况:即:与折半查找中的判定树相同(形态比较均衡)即:与折半查找中的判定树相同(形态比较均衡)3)对有)对有 n 个关键字的二叉排序树的平均查找长度个关键字的二叉排序树的平均查找长度: 设每种树态出现概率相设每种树态出现概率相 同同 ,查找每个关键字也是等概率的。,查找每个关键字也是等概率的。则则ASL求解过程可推导。求解过程可推导。 (详见教材(详见教材P232)树的深度为:树的深度为: log 2n +1 ;ASL=log 2(n+1) 1 ;与折半查找相同。;与折半查找相同。结论:二叉排序树的结论:二叉排序树的ASL2(11/n)ln n421四、平衡二叉树四、平衡二叉树四、平衡二叉树四、平衡二叉树平衡二叉树又称平衡二叉树又称AVL树,它是具有如下性质的树,它是具有如下性质的二叉树:二叉树:为了方便起见,给每个结点附加一个为了方便起见,给每个结点附加一个数字数字,给,给出出该结点左子树与右子树的高度差该结点左子树与右子树的高度差。这个数字。这个数字称为结点的称为结点的平衡因子平衡因子balancebalance。这样,可以得到这样,可以得到AVL树的其它性质:树的其它性质:即即| |左子树深度左子树深度- -右子树深度右子树深度| 1| 1左、右子树是平衡二叉树;左、右子树是平衡二叉树;所有结点的左、右子树深度之差的绝对值所有结点的左、右子树深度之差的绝对值 1422(a) (a) 平衡树平衡树 (b) (b) 不平衡树不平衡树例:判断下列二叉树是否例:判断下列二叉树是否AVL树?树?v任一结点的平衡因子只能取:任一结点的平衡因子只能取:-1、0 或或 1;如果如果树中任意一个结点的平衡因子的绝对值大于树中任意一个结点的平衡因子的绝对值大于1,则这棵二叉树就失去平衡,不再是则这棵二叉树就失去平衡,不再是AVL树;树;v对于一棵有对于一棵有n个结点的个结点的AVL树,其树,其高度保持在高度保持在O(log2n)数量级,数量级,ASL也保持在也保持在O(log2n)量级。量级。0001 11 1-1-12 2 2 20001-1423如果在一棵如果在一棵AVL树中插入一个新结点,就有可能造树中插入一个新结点,就有可能造成失衡,此时必须成失衡,此时必须重新调整树的结构重新调整树的结构,使之恢复,使之恢复平衡。我们称调整平衡过程为平衡。我们称调整平衡过程为平衡旋转平衡旋转。现分别介绍这四种平衡旋转。现分别介绍这四种平衡旋转。平衡旋转可以归纳为四类:平衡旋转可以归纳为四类:v LL平衡旋转平衡旋转v RR平衡旋转平衡旋转v LR平衡旋转平衡旋转v RL平衡旋转平衡旋转424若在若在A的的左子树的左子树上插入左子树的左子树上插入结点,使结点,使A的平衡因子从的平衡因子从1增加至增加至2,需要进行一次,需要进行一次顺时针旋转顺时针旋转。(以以B为旋转轴)为旋转轴)A AB BC CA AB BC C若在若在A的的右子树的右子树上插入右子树的右子树上插入结点,使结点,使A的平衡因子从的平衡因子从-1增加增加至至-2,需要进行一次,需要进行一次逆时针旋转逆时针旋转。(以以B为旋转轴)为旋转轴)2 2)RRRR平衡旋转:平衡旋转:平衡旋转:平衡旋转:A AB BC CA AB BC C1 1)LLLL平衡旋转:平衡旋转:平衡旋转:平衡旋转:425若在若在A的的右子树的左子树上插入右子树的左子树上插入结结点,使点,使A的平衡因子从的平衡因子从-1增加至增加至-2,需要,需要先进行顺时针旋转,再逆先进行顺时针旋转,再逆时针旋转。时针旋转。(以插入的结点以插入的结点C为旋转轴)为旋转轴)4 4)RLRL平衡旋转:平衡旋转:平衡旋转:平衡旋转:A AB BC CA AB BC CA AB BC C若在若在A的的左子树的左子树的右右子树上插入子树上插入结点,使结点,使A的平衡因子从的平衡因子从1增加至增加至2,需要,需要先进行逆时针旋转,再先进行逆时针旋转,再顺时针旋转。顺时针旋转。(以插入的结点以插入的结点C为旋转轴)为旋转轴)A AB BC CA AB BC CA AB BC C3 3)LRLR平衡旋转:平衡旋转:平衡旋转:平衡旋转:这种调整规则可以保证二叉排序树的次序不变这种调整规则可以保证二叉排序树的次序不变4260 013130 037370 02424例:例:请将下面序列构成一棵请将下面序列构成一棵平衡二叉排序树平衡二叉排序树: ( 13,24,37,90,53)0 013130 03737-1-113130 02424-1-12424-2-2-2-21313需要需要RR平衡旋转平衡旋转(绕绕B逆转逆转,B为根)为根)0 09090-1-12424-1-137370 053531 19090-2-2-2-23737需要需要RL平衡旋转平衡旋转(绕绕C先顺后逆)先顺后逆)0 037370 090900 053530 037370 090900 05353 4278.4 8.4 哈希查找表哈希查找表一、一、哈希表的概念哈希表的概念二、哈希函数的构造方法哈希函数的构造方法三、冲突处理方法冲突处理方法四、四、哈希表的查找及分析哈希表的查找及分析428一、哈希表的概念一、哈希表的概念哈希表:哈希表:即散列存储结构。即散列存储结构。 散列法存储的基本思想:散列法存储的基本思想:建立关键码字与其存储位置的建立关键码字与其存储位置的对应对应关系,关系,或者说,或者说,由关键码的值决定数据的存储地址由关键码的值决定数据的存储地址。 优点:优点:查找速度极快(查找速度极快(O(1)O(1)), ,查找效率与元素个数查找效率与元素个数n n无关!无关!例例1:若将学生信息按如下方式存入计算机,如:若将学生信息按如下方式存入计算机,如:将将20010118102200101181020101的所有信息存入的所有信息存入VV0101 单元;单元;将将20010118102200101181020202的所有信息存入的所有信息存入VV0202 单元;单元;将将20010118102200101181023131的所有信息存入的所有信息存入V31V31单元。单元。欲查找学号为欲查找学号为20010118102200101181021616的信息,便可直接访问的信息,便可直接访问V16V16!429例例2 : 有有数数据据元元素素序序列列(14(14,2323,3939,9 9,2525,11)11),若若规规定定每个元素每个元素k k的存储地址的存储地址H H(k k)k k,请画出存储结构图。,请画出存储结构图。(注:(注:H H(k k)k k称为散列函数)称为散列函数)解:解:根据散列函数根据散列函数H H(k k)k k ,可知元素,可知元素1414应当存入地址为应当存入地址为1414的单元,元素的单元,元素2323应当存入地址为应当存入地址为2323的单元,的单元,对应散列存储表(哈希表)如下:对应散列存储表(哈希表)如下:讨论:如何进行散列查找?讨论:如何进行散列查找?根据存储时用到的散列函数根据存储时用到的散列函数H(k)H(k)表达式表达式,迅即可查到结果!,迅即可查到结果!例如,查找例如,查找key=9,key=9,则访问则访问H(9)=9H(9)=9号地址,若内容为号地址,若内容为9 9则成功;则成功;若查不到,应当设法返回一个特殊值,例如空指针或空记录。若查不到,应当设法返回一个特殊值,例如空指针或空记录。 地址地址 9111423242539内容内容14119232539缺点:空间效率低!缺点:空间效率低!缺点:空间效率低!缺点:空间效率低!430选取某个函数,依该函数按关键字计算元素的存储位置,选取某个函数,依该函数按关键字计算元素的存储位置,并按此存放;并按此存放;查找时,由同一个函数查找时,由同一个函数对给定值对给定值k k计算地址,计算地址,将将k k与地址单元中元素关键码进行比,确定查找是否成功。与地址单元中元素关键码进行比,确定查找是否成功。通常关键码的集合比哈希地址集合大得多,因而经通常关键码的集合比哈希地址集合大得多,因而经过哈希函数变换后,过哈希函数变换后,可能将不同的关键码映射到同可能将不同的关键码映射到同一个哈希地址上一个哈希地址上,这种现象称为,这种现象称为冲突冲突。若干术语若干术语 哈希方法哈希方法 ( (杂凑法杂凑法) ) 哈希函数哈希函数( (杂凑函数杂凑函数) ) 哈哈 希希 表表 ( (杂凑表杂凑表) ) 冲冲 突突哈希方法中使用的转换函数称为哈希方法中使用的转换函数称为哈希函数哈希函数( (杂杂凑函数凑函数) )按上述思想构造的表称为按上述思想构造的表称为哈希表哈希表( (杂凑表杂凑表) ) 431有有6个元素的关键码分别为:(个元素的关键码分别为:(14,23,39,9,25,11)。)。选取关键码与元素位置间的函数为选取关键码与元素位置间的函数为H(k)=k mod 7通过哈希函数对通过哈希函数对6 6个元素建立哈希表:个元素建立哈希表:253923914冲突现象举例:冲突现象举例:冲突现象举例:冲突现象举例:6个元素用个元素用7个个地址应该足够地址应该足够!H(14)=14%7=011H(25)=25%7=4H(11)=11%7=4有冲突!有冲突!有冲突!有冲突! 0 1 2 3 4 5 6432所以,所以,所以,所以,哈希方法必须解决以下两个问题:哈希方法必须解决以下两个问题:哈希方法必须解决以下两个问题:哈希方法必须解决以下两个问题:1 1)构造好的哈希函数构造好的哈希函数构造好的哈希函数构造好的哈希函数(a a)所选函数尽可能)所选函数尽可能简单简单,以便提高转换速度;,以便提高转换速度;(b b)所选函数对关键码计算出的地址,应在)所选函数对关键码计算出的地址,应在哈希地址集哈希地址集中中大致大致均匀均匀分布,以减少空间浪费。分布,以减少空间浪费。2 2 2 2)制定一个好的解决冲突的方案)制定一个好的解决冲突的方案)制定一个好的解决冲突的方案)制定一个好的解决冲突的方案查查找找时时,如如果果从从哈哈希希函函数数计计算算出出的的地地址址中中查查不不到到关关键键码码,则则应当依据解决冲突的规则,有规律地查询其它相关单元。应当依据解决冲突的规则,有规律地查询其它相关单元。在哈希查找方法中,冲突是不可能避免的,只能在哈希查找方法中,冲突是不可能避免的,只能在哈希查找方法中,冲突是不可能避免的,只能在哈希查找方法中,冲突是不可能避免的,只能尽可能减少。尽可能减少。尽可能减少。尽可能减少。433二、哈希函数的构造方法哈希函数的构造方法常用的哈希函数构造方法有:常用的哈希函数构造方法有:1.1.直接定址法直接定址法直接定址法直接定址法 2.2.除留余数法除留余数法除留余数法除留余数法 3.3.数字分析法数字分析法数字分析法数字分析法 4.4.平方取中法平方取中法平方取中法平方取中法 5.5.折叠法折叠法折叠法折叠法 6.6.随机数法随机数法随机数法随机数法 要求一:要求一:n n个数据原仅占用个数据原仅占用n n个地址,个地址,虽然散列查找是以空间换时间,但仍虽然散列查找是以空间换时间,但仍希望散列的地址空间尽量小。希望散列的地址空间尽量小。要求二:要求二:无论用什么方法存储,目无论用什么方法存储,目的都是尽量均匀地存放元素,以避免的都是尽量均匀地存放元素,以避免冲突。冲突。434Hash(key) = akey + b ( (a、b为常数为常数) )优点:优点:以关键码以关键码keykey的某个线性函数值为哈希地址,的某个线性函数值为哈希地址,不会产生冲突不会产生冲突. .缺点:缺点:要占用连续地址空间,空间效率低。要占用连续地址空间,空间效率低。 例:例:关键码集合为关键码集合为100,300,500,700,800,900, 选取哈希函数为选取哈希函数为Hash(key)=key/100, 则存储结构(哈希表)如下:则存储结构(哈希表)如下:0 1 2 3 4 5 6 7 8 99008007005003001001 1 1 1、直接定址法、直接定址法、直接定址法、直接定址法435Hash(key)=key mod p (p (p是一个整数是一个整数) )特点:特点:以关键码除以以关键码除以p的余数作为哈希地址。的余数作为哈希地址。关键:关键:如何选取合适的如何选取合适的p?技巧:技巧:若设计的哈希表长为若设计的哈希表长为m,则一般取,则一般取pm且为且为质数质数 (也可以是不包含小于(也可以是不包含小于20质因子的合数)。质因子的合数)。自练:自练:自测卷简答题第自测卷简答题第4 4小题小题 2 2 2 2、除留余数法、除留余数法、除留余数法、除留余数法436特点:特点:某关键字的某几位组合成哈希地址。所选的位应当某关键字的某几位组合成哈希地址。所选的位应当是:各种符号在该位上出现的频率大致相同。是:各种符号在该位上出现的频率大致相同。 3 4 7 0 5 2 4 3 4 9 1 4 8 73 4 8 2 6 9 63 4 8 5 2 7 03 4 8 6 3 0 53 4 9 8 0 5 83 4 7 9 6 7 13 4 7 3 9 1 9例:例:有一组(例如有一组(例如80个)关键码,其样式如下:个)关键码,其样式如下:3 3 3 3、数字分析法、数字分析法、数字分析法、数字分析法讨论:讨论: 第第1 1、2 2位均是位均是“3“3和和4”4”,第,第3 3位位也只有也只有“ 7“ 7、8 8、9”9”,因此,这几,因此,这几位不能用,位不能用,余下四位分布较均匀余下四位分布较均匀,可作为哈希地址选用。可作为哈希地址选用。位号:位号: 若哈希地址取两位(因元素仅若哈希地址取两位(因元素仅8080个)个),则可取这四位中的任意两位组合成,则可取这四位中的任意两位组合成哈希地址,也可以取其中两位与其它哈希地址,也可以取其中两位与其它两位叠加求和后,取低两位作哈希地两位叠加求和后,取低两位作哈希地址。址。437特点:特点:对关键码平方后,按哈希表大小,取中间的若干位对关键码平方后,按哈希表大小,取中间的若干位作为哈希地址。作为哈希地址。理由:理由:因为中间几位与数据的每一位都相关。因为中间几位与数据的每一位都相关。 例:例:25892589的平方值为的平方值为67029216702921,可以取中间的,可以取中间的029029为地址。为地址。 5 5 5 5、折叠法、折叠法、折叠法、折叠法 特特点点:将将关关键键码码自自左左到到右右分分成成位位数数相相等等的的几几部部分分(最最后后一一部部分分位位数数可可以以短短些些),然然后后将将这这几几部部分分叠叠加加求求和和,并并按按哈哈希表表长,取后几位作为哈希地址。希表表长,取后几位作为哈希地址。适用于:适用于:每一位上各符号出现概率大致相同的情况。每一位上各符号出现概率大致相同的情况。法法1 1:移位法移位法 将各部分的最后一位对齐相加。将各部分的最后一位对齐相加。法法2 2:间界叠加法间界叠加法从一端向另一端沿分割界来回折叠后从一端向另一端沿分割界来回折叠后, ,最后一位对齐相加。最后一位对齐相加。 例:例:元素元素42751896, 42751896, 用法用法1 1: 4274275185189696= =10411041 用法用法2 2: 427427 518 518 9696 724+518+69 = 724+518+69 =131113114 4 4 4、平方取中法、平方取中法、平方取中法、平方取中法4386 6 6 6、随机数法、随机数法、随机数法、随机数法Hash(key) = random ( key ) (random (random为随机函数为随机函数) )适用于:适用于:关键字长度不等的情况。造表和查找都很方便。关键字长度不等的情况。造表和查找都很方便。 执行速度(即计算哈希函数所需时间);执行速度(即计算哈希函数所需时间); 关键字的长度;关键字的长度; 哈希表的大小;哈希表的大小; 关键字的分布情况;关键字的分布情况; 查找频率。查找频率。小结:构造哈希函数的原则:小结:构造哈希函数的原则:小结:构造哈希函数的原则:小结:构造哈希函数的原则:439三、冲突处理方法冲突处理方法常见的冲突处理方法有:常见的冲突处理方法有:1.1.开放定址法(开地址法)开放定址法(开地址法) 2.2.链地址法(拉链法)链地址法(拉链法) 3.3.再哈希法(双哈希函数法)再哈希法(双哈希函数法)4.4.建立一个公共溢出区建立一个公共溢出区 4401 1 1 1、开放定址法(开地址法)、开放定址法(开地址法)、开放定址法(开地址法)、开放定址法(开地址法) 设计思路:设计思路:有冲突时就去寻找下一个空的哈希地址,有冲突时就去寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总能找只要哈希表足够大,空的哈希地址总能找到,并将数据元素存入。到,并将数据元素存入。 具体实现:具体实现:Hi=(Hash(key)+di) mod m ( 1i m ) 其中:其中: Hash(key)为哈希函数为哈希函数 m为哈希表长度为哈希表长度 di 为增量序列为增量序列 1,2,m-1,且,且di=i (1)(1)(1)(1)线性探测法线性探测法线性探测法线性探测法含义:一旦冲突,就找附近(下一个)空地址存入。含义:一旦冲突,就找附近(下一个)空地址存入。含义:一旦冲突,就找附近(下一个)空地址存入。含义:一旦冲突,就找附近(下一个)空地址存入。441关键码集为关键码集为 47,7,29,11,16,92,22,8,3,设:设:哈希表表长为哈希表表长为m=m=11; 哈希函数为哈希函数为Hash(key)=key mod 11; 拟用拟用线性探测法线性探测法线性探测法线性探测法处理冲突。建哈希表如下:处理冲突。建哈希表如下:解释:解释: 47、7(以以及及11、16、92)均均是是由由哈哈希希函函数数得得到到的的没没有有冲冲突突的的哈希地址;哈希地址;0 1 2 3 4 5 6 7 8 9 10477 例:例:291116922283 Hash(29)=7,哈希地址有冲突,需寻找下一个空的哈希地址:,哈希地址有冲突,需寻找下一个空的哈希地址:由由H1=(Hash(29)+1) mod 11=8,哈希地址哈希地址8为空,因此将为空,因此将29存入。存入。 另外,另外,22、8、3同样在哈希地址上有冲突,也是由同样在哈希地址上有冲突,也是由H1找到空找到空的哈希地址的。的哈希地址的。其中其中其中其中3 3 3 3 还连续移动了两次(二次聚集)还连续移动了两次(二次聚集)还连续移动了两次(二次聚集)还连续移动了两次(二次聚集)442线性探测法的优点:线性探测法的优点:只要哈希表未被填满,保证能只要哈希表未被填满,保证能找到一个空地址单元存放有冲突的元素;找到一个空地址单元存放有冲突的元素;线性探测法的缺点:线性探测法的缺点:可能使第可能使第i个哈希地址的同义词个哈希地址的同义词存入第存入第i+1个哈希地址,这样本应存入第个哈希地址,这样本应存入第i+1个个哈希地址的元素变成了第哈希地址的元素变成了第i+2个哈希地址的同个哈希地址的同义词,义词, 因此,可能出现很多元素在相邻的哈希地址因此,可能出现很多元素在相邻的哈希地址上上“堆积堆积”起来,大大降低了查找效率。起来,大大降低了查找效率。解决方案:解决方案:可采用可采用二次探测法二次探测法或或伪随机探测法伪随机探测法,以,以改善改善“堆积堆积”问题。问题。 讨论:讨论:443仍举上例,改用二次探测法处理冲突,建表如下:仍举上例,改用二次探测法处理冲突,建表如下: 0 1 2 3 4 5 6 7 8 9 10112234792167298 注:注:只有只有3 3这个关键码的冲突处理与上例不同,这个关键码的冲突处理与上例不同,Hash(3)=3,哈希地址上冲突,由,哈希地址上冲突,由H1=(Hash(3)+12) mod 11=4,仍然冲突;,仍然冲突;H2=(Hash(3)-12) mod 11=2,找到空的哈希地址,存入。找到空的哈希地址,存入。 (2) (2) (2) (2) 二次探测法二次探测法二次探测法二次探测法Hi=(Hash(key)di) mod m其中:其中:Hash(key)Hash(key)为哈希函数为哈希函数 m为哈希表长度,为哈希表长度,m要求是某个要求是某个4k+3的质数的质数; di为增量序列为增量序列 12,-12,22,-22,q2 (3) (3) (3) (3) 若若若若didididi伪随机序列,就称为伪随机探测法伪随机序列,就称为伪随机探测法伪随机序列,就称为伪随机探测法伪随机序列,就称为伪随机探测法4442 2 2 2、链地址法、链地址法、链地址法、链地址法( ( ( (拉链法)拉链法)拉链法)拉链法)基本思想:基本思想:将具有相同哈希地址的记录链成一个单链表,将具有相同哈希地址的记录链成一个单链表,m m个个哈希地址就设哈希地址就设m m个单链表个单链表,然后用一个数组将,然后用一个数组将m个单个单链表的表头指针存储起来,形成一个动态的结构。链表的表头指针存储起来,形成一个动态的结构。 设设 47, 7, 29, 11, 16, 92, 22, 8, 3, 50, 37, 89 的哈希函的哈希函数为:数为:Hash(key)=key mod 11,用用拉拉链链法法处处理理冲冲突突,则则建建表表如右图所示。如右图所示。 例:例: 0 1 2 3 4 5 6 7 8 91022118934737922971650810 注:注:有冲突的元素可以插有冲突的元素可以插在表尾在表尾, ,也可以插在表头也可以插在表头4453 3 3 3、再哈希法(双哈希函数法)、再哈希法(双哈希函数法)、再哈希法(双哈希函数法)、再哈希法(双哈希函数法)Hi=RHi(key) i=1, 2, ,kRHi均是不同的哈希函数,当产生冲突时就计算另一个均是不同的哈希函数,当产生冲突时就计算另一个哈希函数,直到冲突不再发生。哈希函数,直到冲突不再发生。优点:优点:不易产生聚集;不易产生聚集;缺点:缺点:增加了计算时间。增加了计算时间。4. 4. 4. 4. 建立一个公共溢出区建立一个公共溢出区建立一个公共溢出区建立一个公共溢出区 思路:思路:除设立哈希基本表外,除设立哈希基本表外,另设立一个溢出向量表另设立一个溢出向量表。所有关键字和基本表中关键字为同义词的记录,不所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的地址是什么,一旦发生冲突,都管它们由哈希函数得到的地址是什么,一旦发生冲突,都填入溢出表。填入溢出表。446四、哈希表的查找及分析四、哈希表的查找及分析四、哈希表的查找及分析四、哈希表的查找及分析明确:明确:散列函数没有散列函数没有“万能万能”通式,要根据元素集合的特通式,要根据元素集合的特性而分别构造。性而分别构造。 算法:算法:见教材见教材P259 P259 算法算法9.17_189.17_18讨论:讨论:哈希查找的速度是否为真正的哈希查找的速度是否为真正的O O(1 1)?)?不不是是。由由于于冲冲突突的的产产生生,使使得得哈哈希希表表的的查查找找过过程程仍仍然然要要进进行比较,仍然要以平均查找长度行比较,仍然要以平均查找长度ASLASL来衡量。来衡量。一一般般地地,ASLASL依依赖赖于于哈哈希希表表的的装装填填因因子子 , ,它它标标志志着着哈哈希希表表的的装满程度。装满程度。 越大,表中记录数越多,说明表装得越满,发生冲突越大,表中记录数越多,说明表装得越满,发生冲突的可能性就越大,查找时比较次数就越多。的可能性就越大,查找时比较次数就越多。00 11447讨论:讨论:2 2)“冲突冲突”是不是特别讨厌?是不是特别讨厌?答:答:不一定!正因为有冲突,使得文件加密后无法破译不一定!正因为有冲突,使得文件加密后无法破译(不可逆,是单向散列函数,可用于数字签名)。(不可逆,是单向散列函数,可用于数字签名)。利用了哈希表性质:利用了哈希表性质:源文件稍稍改动,会导致哈希表变源文件稍稍改动,会导致哈希表变动很大。动很大。1 1) 散列存储的查找效率到底是多少?散列存储的查找效率到底是多少?P261P261答:答:ASLASL与装填因子与装填因子 有关!既不是严格的有关!既不是严格的O(1)O(1),也不是,也不是O(n)O(n)应尽量选择一个应尽量选择一个合适的合适的 ,以降低,以降低ASLASL的长度的长度448本章小结本章小结重点:重点:顺序查找、二分查找、分块查找、顺序查找、二分查找、分块查找、二叉排序树上查找、二叉排序树上查找、B树以及散列树以及散列表上查找的基本思想和算法实现。表上查找的基本思想和算法实现。难点:难点:二叉排序树的删除算法二叉排序树的删除算法 B树的插入、删除树的插入、删除449数据结构课程的内容数据结构课程的内容4509.1 9.1 概述概述9.2 9.2 插入排序插入排序9.3 9.3 交换排序交换排序9.4 9.4 选择排序选择排序9.5 9.5 归并排序归并排序9.6 9.6 基数排序基数排序第第9 9章章 内部排序内部排序4519.1 9.1 概述概述1. 什么是排序?什么是排序? 将一组杂乱无章的将一组杂乱无章的数据数据按一定的按一定的规律规律顺次排列起顺次排列起来。来。 2. 排序的目的是什么?排序的目的是什么?存放在数据表中存放在数据表中按关键字排序按关键字排序3.排序算法的好坏如何衡量?排序算法的好坏如何衡量?时间效率时间效率排序速度(即排序所花费的全部比较次数)排序速度(即排序所花费的全部比较次数)空间效率空间效率占内存辅助空间的大小占内存辅助空间的大小稳稳定定性性若若两两个个记记录录A A和和B B的的关关键键字字值值相相等等,但但排排序序后后A A、B B的先后次序保持不变,则称这种排序算法是稳定的。的先后次序保持不变,则称这种排序算法是稳定的。 便于查找!便于查找!4524. 什么叫内部排序?什么叫外部排序?什么叫内部排序?什么叫外部排序? 若待排序记录都在内存中,称为内部排序;若待排序记录都在内存中,称为内部排序;若待排序记录一部分在内存,一部分在外存,则若待排序记录一部分在内存,一部分在外存,则称为外部排序。称为外部排序。注:注:外部排序时,要将数据分批调入内存来排序,中间外部排序时,要将数据分批调入内存来排序,中间结果还要及时放入外存,显然外部排序要复杂得多。结果还要及时放入外存,显然外部排序要复杂得多。 5.待排序记录在内存中怎样存储和处理?待排序记录在内存中怎样存储和处理? 顺序顺序排序排序排序时直接移动记录;排序时直接移动记录; 链表链表排序排序排序时只移动指针;排序时只移动指针; 地址地址排序排序排序时先移动地址,最后再移动记录。排序时先移动地址,最后再移动记录。注:注:地址排序地址排序中可以增设一维数组来专门存放记录的地址。中可以增设一维数组来专门存放记录的地址。453注:注:大多数排序算法都是针对顺序表结构的大多数排序算法都是针对顺序表结构的( (便于直接移动元素便于直接移动元素) )6. 6. 顺序存储(顺序表)的抽象数据类型如何表示?顺序存储(顺序表)的抽象数据类型如何表示?Typedef struct /定义每个记录(数据元素)的结构定义每个记录(数据元素)的结构 KeyType key ; /关键字关键字 InfoType otherinfo; /其它数据项其它数据项RecordType ;Typedef struct /定义顺序表的结构定义顺序表的结构 RecordType r MAXSIZE +1 ; /存储顺序表的向量存储顺序表的向量 /r0/r0一般作哨兵或缓冲区一般作哨兵或缓冲区 int length ; /顺序表的长度顺序表的长度SqList ;# define MAXSIZE 20 /设记录不超过设记录不超过2020个个typedef int KeyType ; /设关键字为整型量(设关键字为整型量(intint型)型)4547. 内部排序的算法有哪些?内部排序的算法有哪些?按排序的规则不同,可分为按排序的规则不同,可分为5类:类:插入排序插入排序交换排序(重点是快速排序)交换排序(重点是快速排序)选择排序选择排序归并排序归并排序基数排序基数排序d关键字的位数关键字的位数(长度长度)按排序算法的时间复杂度不同,可分为按排序算法的时间复杂度不同,可分为3类:类:简单的排序算法:时间效率低,简单的排序算法:时间效率低,O(n2)先进的排序算法先进的排序算法: 时间效率高,时间效率高,O( nlog2n )基数排序算法:时间效率高,基数排序算法:时间效率高,O( dn)4559.2 9.2 插入排序插入排序插入排序的基本思想是:插入排序的基本思想是:插入排序的基本思想是:插入排序的基本思想是:插入排序有多种具体实现算法:插入排序有多种具体实现算法: 1) 直接插入排序直接插入排序 2) 折半插入排序折半插入排序 3) 表插入排序表插入排序 4) 希尔排序希尔排序 每步将一个待排序的对象,每步将一个待排序的对象,每步将一个待排序的对象,每步将一个待排序的对象,按其关键码大小,按其关键码大小,按其关键码大小,按其关键码大小,插入到前面插入到前面插入到前面插入到前面已经排好序的一组对象已经排好序的一组对象已经排好序的一组对象已经排好序的一组对象的适当位置的适当位置的适当位置的适当位置上上上上,直到对象全部插入为止。,直到对象全部插入为止。,直到对象全部插入为止。,直到对象全部插入为止。简言之,边插入边排序,保证子序列中随时都是排好序的。简言之,边插入边排序,保证子序列中随时都是排好序的。简言之,边插入边排序,保证子序列中随时都是排好序的。简言之,边插入边排序,保证子序列中随时都是排好序的。4561) 1) 直接插入排序直接插入排序直接插入排序直接插入排序新元素插入到哪里?新元素插入到哪里?例例1 1:关键字序列关键字序列T=(13,6,3,31,9,27,5,11),), 请写出直接插入排序的中间过程序列。请写出直接插入排序的中间过程序列。【13】, 6, 3, 31, 9, 27, 5, 11【6, 13】, 3, 31, 9, 27, 5, 11【3, 6, 13】, 31, 9, 27, 5, 11【3, 6, 13,31】, 9, 27, 5, 11【3, 6, 9, 13,31】, 27, 5, 11【3, 6, 9, 13,27, 31】, 5, 11【3, 5, 6, 9, 13,27, 31】, 11【3, 5, 6, 9, 11,13,27, 31】 在已形成的在已形成的有序表中线性查找有序表中线性查找,并在,并在适当位置插入,把原来位置上的元素向后适当位置插入,把原来位置上的元素向后顺移顺移。最简单的排序法!最简单的排序法!最简单的排序法!最简单的排序法!457例例2 2:关键字序列关键字序列T= (21,25,49,25*,16,08),),请写出直接插入排序的具体实现过程。请写出直接插入排序的具体实现过程。* *表示后一个表示后一个2525i i=1=121212525494925*25*161608080 1 2 3 4 5 6暂暂存存2121i i=2=2i i=3=3i i=5=5i i=4=4i i=6=62525252525494949494925*25*25*494916161625*25*0808084949解:解:假设该序列已存入一维数组假设该序列已存入一维数组V7V7中,将中,将V0V0作为缓冲或作为缓冲或暂存单元(暂存单元(TempTemp)。则程序执行过程为:)。则程序执行过程为:21212525494925*25*2121初态:初态:1616494925*25*2525212116160808完成!时间效率:时间效率: O(nO(nO(nO(n2 2 2 2) ) ) )因为在因为在最坏情况下最坏情况下,所有元素的比较,所有元素的比较次数总和为(次数总和为(0 01 1n-1)O(nn-1)O(n2 2) )。其他情况。其他情况下还要加上移动元素的次数。下还要加上移动元素的次数。 空间效率:空间效率:O O O O(1 1 1 1)因为仅占用因为仅占用1 1个缓冲单元个缓冲单元算法的稳定性:算法的稳定性:稳定稳定稳定稳定因为因为25*25*排序后仍然在排序后仍然在2525的后面。的后面。 对应程序参见教材对应程序参见教材P265P265。458Void InsertSort(SqList &L) / 对顺序表对顺序表L做直接插入排序做直接插入排序 for ( i=2; i=L.length; +i ) if ( LT(L.ri.key,L.ri-1.key) ) / “,需将需将L.ri插入有序子表插入有序子表 L.r0 = L.ri; / 复制为哨兵复制为哨兵 L.ri = L.ri-1; for( j=i-2; LT(L.r0.key,L.ri.key);-j ) L.r j+1 = L.r j; / 记录后移记录后移 L.r j+1 = L.r0; / 插入到正确位置插入到正确位置 / InsertSort459n n若设待排序的对象个数为若设待排序的对象个数为若设待排序的对象个数为若设待排序的对象个数为n n,则算法需要进行则算法需要进行则算法需要进行则算法需要进行n n- -1 1次插入。次插入。次插入。次插入。n n最好情况下最好情况下最好情况下最好情况下,排序前对象已经按关键码大小从,排序前对象已经按关键码大小从,排序前对象已经按关键码大小从,排序前对象已经按关键码大小从小到大有序,每趟只需与前面的有序对象序列小到大有序,每趟只需与前面的有序对象序列小到大有序,每趟只需与前面的有序对象序列小到大有序,每趟只需与前面的有序对象序列的的的的最后一个对象最后一个对象最后一个对象最后一个对象的关键码比较的关键码比较的关键码比较的关键码比较 1 1 1 1 次,对象不需次,对象不需次,对象不需次,对象不需要移动要移动要移动要移动 。因此,总的关键码比较次数为。因此,总的关键码比较次数为。因此,总的关键码比较次数为。因此,总的关键码比较次数为n n-1-1。直接插入排序的算法分析直接插入排序的算法分析直接插入排序的算法分析直接插入排序的算法分析460最坏情况下最坏情况下最坏情况下最坏情况下,第,第,第,第i i趟插入时,第趟插入时,第趟插入时,第趟插入时,第i i个对象必须与个对象必须与个对象必须与个对象必须与前面前面前面前面i-1i-1个对象都做关键码比较,并且每做个对象都做关键码比较,并且每做个对象都做关键码比较,并且每做个对象都做关键码比较,并且每做 1 1 1 1 次比较就要做次比较就要做次比较就要做次比较就要做 1 1 1 1 次数据移动。则总的关键码次数据移动。则总的关键码次数据移动。则总的关键码次数据移动。则总的关键码比较次数比较次数比较次数比较次数KCNKCN和对象移动次数和对象移动次数和对象移动次数和对象移动次数RMNRMN分别为分别为分别为分别为461 若待排序对象序列中出现各种可能排列的概率相同,若待排序对象序列中出现各种可能排列的概率相同,若待排序对象序列中出现各种可能排列的概率相同,若待排序对象序列中出现各种可能排列的概率相同,则可取上述最好情况和最坏情况的平均情况。在平均则可取上述最好情况和最坏情况的平均情况。在平均则可取上述最好情况和最坏情况的平均情况。在平均则可取上述最好情况和最坏情况的平均情况。在平均情况下的关键码情况下的关键码情况下的关键码情况下的关键码比较次数比较次数比较次数比较次数和对象和对象和对象和对象移动次数移动次数移动次数移动次数约为约为约为约为 n n2 2/4/4。因此,直接插入排序的因此,直接插入排序的因此,直接插入排序的因此,直接插入排序的时间复杂度时间复杂度时间复杂度时间复杂度为为为为 o(o(n n2 2) )。 直接插入排序是一种直接插入排序是一种直接插入排序是一种直接插入排序是一种稳定的稳定的稳定的稳定的排序方法。排序方法。排序方法。排序方法。4622 2) 折半插入排序折半插入排序折半插入排序折半插入排序优点:优点:比较的次数大大减少。比较的次数大大减少。时时间间效效率率:虽虽然然比比较较次次数数大大大大减减少少,可可惜惜移移动动次次数数并并未未减减少少,所以排序效率仍为所以排序效率仍为O(nO(n2 2) ) 。空间效率:空间效率: O O(1 1)稳定性:稳定性:稳定稳定 对应程序见教材对应程序见教材对应程序见教材对应程序见教材P267P267(仅用于顺序表)(仅用于顺序表)(仅用于顺序表)(仅用于顺序表)新元素插入到哪里新元素插入到哪里? 在已形成的在已形成的有序表中折半查找有序表中折半查找,并在适,并在适当位置插入,把原来位置上的元素向后当位置插入,把原来位置上的元素向后顺移顺移。463Void BInsertSort (SqList &L) / 折半插入排序折半插入排序 for ( i=2;i=L.length;+i ) L.r0 = L.r i ; / 将将L.r i 暂存到暂存到L.r0 low=1;high=i-1; while (low=high+1;-j) L.r j+1 = L.r j;/ 记录后移记录后移 L.r high+1 = L.r 0; / 插入插入 / for / BInsertSort464折半插入排序的算法分析折半插入排序的算法分析折半插入排序的算法分析折半插入排序的算法分析折半查找比顺序查找快,所以折半插入排序折半查找比顺序查找快,所以折半插入排序折半查找比顺序查找快,所以折半插入排序折半查找比顺序查找快,所以折半插入排序就平均性能来说比直接插入排序要快。就平均性能来说比直接插入排序要快。就平均性能来说比直接插入排序要快。就平均性能来说比直接插入排序要快。在插入第在插入第在插入第在插入第 i i 个对象时,需要经过个对象时,需要经过个对象时,需要经过个对象时,需要经过 loglog2 2i i +1 +1 次关键码比较,才能确定它应插入的位置。次关键码比较,才能确定它应插入的位置。次关键码比较,才能确定它应插入的位置。次关键码比较,才能确定它应插入的位置。折半插入排序是一个折半插入排序是一个折半插入排序是一个折半插入排序是一个稳定的稳定的稳定的稳定的排序方法。排序方法。排序方法。排序方法。465讨论:讨论:若记录是链表结构,用直接插入排序行否?折半插入若记录是链表结构,用直接插入排序行否?折半插入排序呢?排序呢?答:答:直接插入不仅可行,而且还无需移动元素,时间效率更直接插入不仅可行,而且还无需移动元素,时间效率更高!高!但链表无法但链表无法但链表无法但链表无法“折半折半折半折半”! 折半插入排序的改进折半插入排序的改进折半插入排序的改进折半插入排序的改进2-2-路插入排序见教材路插入排序见教材路插入排序见教材路插入排序见教材P267P267。 (1 1)基本思想:)基本思想:)基本思想:)基本思想: P267P267 (2 2)举例:)举例:)举例:)举例:P268 P268 图图图图10.210.2 (3 3)算法分析:)算法分析:)算法分析:)算法分析:移动记录的次数约为移动记录的次数约为移动记录的次数约为移动记录的次数约为n n2 2/8 /8 2- 2-路插入排序路插入排序路插入排序路插入排序只能只能只能只能减少移动记录的次数,而减少移动记录的次数,而减少移动记录的次数,而减少移动记录的次数,而不能不能不能不能绝对绝对绝对绝对避免移动记录。避免移动记录。避免移动记录。避免移动记录。= 若希望在排序过程中不移动记录,只有改变存储结构,进若希望在排序过程中不移动记录,只有改变存储结构,进若希望在排序过程中不移动记录,只有改变存储结构,进若希望在排序过程中不移动记录,只有改变存储结构,进行行行行表插入排序表插入排序表插入排序表插入排序。4663 3)表插入排序)表插入排序)表插入排序)表插入排序基本思想基本思想基本思想基本思想:在顺序存储结构中,给每个记录增开一个指针分在顺序存储结构中,给每个记录增开一个指针分在顺序存储结构中,给每个记录增开一个指针分在顺序存储结构中,给每个记录增开一个指针分量,在排序过程中将指针内容逐个修改为已经整理(排序)量,在排序过程中将指针内容逐个修改为已经整理(排序)量,在排序过程中将指针内容逐个修改为已经整理(排序)量,在排序过程中将指针内容逐个修改为已经整理(排序)过的后继记录地址。过的后继记录地址。过的后继记录地址。过的后继记录地址。优点:优点:优点:优点:在排序过程中不移动元素,只修改指针。在排序过程中不移动元素,只修改指针。在排序过程中不移动元素,只修改指针。在排序过程中不移动元素,只修改指针。回忆:回忆: 链表排序链表排序排序时只移动指针;排序时只移动指针; 地址排序地址排序排序时先移动地址,最后再移动记录。排序时先移动地址,最后再移动记录。此方法具有链表排序和地址排序的特点。此方法具有链表排序和地址排序的特点。此方法具有链表排序和地址排序的特点。此方法具有链表排序和地址排序的特点。4671例:例:关键字序列关键字序列 T=(21,25,49,25*,16,08),), 请写出表插入排序的具体实现过程。请写出表插入排序的具体实现过程。解:解:假设该序列(结构类型假设该序列(结构类型) )已存入一维数组已存入一维数组V7V7中,将中,将V0V0作为表头结点。则作为表头结点。则算法执行过程算法执行过程为:为:i 关键字关键字 Vi.key指针指针 Vi.link0 MaxNum1212253494 25*516608指向第指向第1 1个元素个元素指向头结点指向头结点初态初态初态初态i=1i=1i=1i=1i=2i=2i=2i=2i=3i=3i=3i=3i=4i=4i=4i=4i=5i=5i=5i=5i=6i=6i=6i=6034 45 5 6 65 503 31 102* *表示后一个表示后一个2525468Void Arrange(SLinkListType &SL) p=SL.r0.next; /p指示第一个记录的当前位置指示第一个记录的当前位置 for ( i=1;i=SL.length;+i ) / SL.r1.i-1中记录已按关键字有序排列中记录已按关键字有序排列 while (pi) p = Sl.rp.next; / 找到第找到第i个记录,并用个记录,并用p指示其在指示其在SL中当前位置中当前位置 q = SL.rp.next; / q指示尚未调整的表尾指示尚未调整的表尾 if ( p!=i ) SL.rp SL.ri;/ 交换记录,使第交换记录,使第i个记录到位个记录到位 SL.ri.next = p;/ 指向被移走的记录,使得以后可由指向被移走的记录,使得以后可由while循环找回循环找回 / if p = q; / p指示尚未调整的表尾,为第指示尚未调整的表尾,为第i+1个记录作准备个记录作准备 / for / Arrange469表插入排序算法分析:表插入排序算法分析: 无需移动记录,只需修改无需移动记录,只需修改2n次指针值。但由于比较次次指针值。但由于比较次数没有减少,故数没有减少,故时间效率时间效率仍为仍为O(n2) 。 空间效率空间效率肯定低肯定低,因为增开了指针分量(但在运算过,因为增开了指针分量(但在运算过程中没有用到更多的辅助单元)。程中没有用到更多的辅助单元)。 稳定性:稳定性:25和和25*排序前后次序未变,排序前后次序未变,稳定稳定。讨论:讨论:此算法得到的只是一个有序链表,查找记录时只此算法得到的只是一个有序链表,查找记录时只能满足顺序查找方式。能满足顺序查找方式。改进:改进:可以根据表中指针线索,很快对所有记录重排,可以根据表中指针线索,很快对所有记录重排,形成真正的有序表(顺序存储方式),从而能满足形成真正的有序表(顺序存储方式),从而能满足折半查找方式。折半查找方式。具体实现见教材具体实现见教材P269。4704 4)希尔()希尔()希尔()希尔(shellshell)排序)排序)排序)排序(又称缩小增量排序(又称缩小增量排序(又称缩小增量排序(又称缩小增量排序)基本思想基本思想基本思想基本思想:先将整个待排记录序列分割成若干子序列:先将整个待排记录序列分割成若干子序列:先将整个待排记录序列分割成若干子序列:先将整个待排记录序列分割成若干子序列, , , ,分分分分别进行直接插入排序,待整个序列中的记录别进行直接插入排序,待整个序列中的记录别进行直接插入排序,待整个序列中的记录别进行直接插入排序,待整个序列中的记录“基本有序基本有序基本有序基本有序”时,再对全体记录进行一次直接插入排序。时,再对全体记录进行一次直接插入排序。时,再对全体记录进行一次直接插入排序。时,再对全体记录进行一次直接插入排序。技巧:技巧:技巧:技巧:子序列的构成不是简单地子序列的构成不是简单地子序列的构成不是简单地子序列的构成不是简单地“逐段分割逐段分割逐段分割逐段分割”,而是将,而是将,而是将,而是将相隔某个增量相隔某个增量相隔某个增量相隔某个增量dkdkdkdk的记录组成一个子序列的记录组成一个子序列的记录组成一个子序列的记录组成一个子序列, , , ,让增量让增量让增量让增量dkdkdkdk逐趟缩逐趟缩逐趟缩逐趟缩短(例如依次取短(例如依次取短(例如依次取短(例如依次取5,3,15,3,15,3,15,3,1),直到),直到),直到),直到dkdkdkdk1 1 1 1为止。为止。为止。为止。优点:优点:优点:优点:让关键字值小的元素能很快前移,且序列若基本让关键字值小的元素能很快前移,且序列若基本让关键字值小的元素能很快前移,且序列若基本让关键字值小的元素能很快前移,且序列若基本有序时,再用直接插入排序处理,时间效率会高很多。有序时,再用直接插入排序处理,时间效率会高很多。有序时,再用直接插入排序处理,时间效率会高很多。有序时,再用直接插入排序处理,时间效率会高很多。47138例:例:关键字序列关键字序列 T=(49,38,65,97, 76, 13, 27, 49*,55, 04),),请写出希尔排序的具体实现过程。请写出希尔排序的具体实现过程。0123456789104938659776132749*5504初初 态:态:第第1趟趟 (dk=5)第第2趟趟 (dk=3)第第3趟趟 (dk=1)4913134938276549*975576042738 65 49*9755135576045513270427044949*4949*763876 65 65 9797551327044949*3876 65 9713 27 0449* 76 97 算法分析:算法分析:开始时开始时dk 的值较大,子序列中的对象较少,排序速度的值较大,子序列中的对象较少,排序速度较快;随着排序进展,较快;随着排序进展,dk 值逐渐变小,子序列中对象个数值逐渐变小,子序列中对象个数逐渐变多,由于前面工作的基础,大多数对象已基本有序,逐渐变多,由于前面工作的基础,大多数对象已基本有序,所以排序速度仍然很快。所以排序速度仍然很快。riri472void ShellSort(SqList &L,int dlta ,int t) /按增量序列按增量序列dlta0t-1对顺序表对顺序表L作作Shell排序排序 for(k=0;kt;+k) ShellSort(L,dltak); /增量为增量为dltak的一趟插入排序的一趟插入排序 / ShellSort时间效率:时间效率:空间效率:空间效率:O O O O(1 1 1 1)因为仅占用因为仅占用1 1个缓冲单元个缓冲单元算法的算法的稳定性:稳定性:不稳定不稳定不稳定不稳定因为因为4949* *排序后却到了排序后却到了4949的前面的前面希尔排序算法(主程序)希尔排序算法(主程序)希尔排序算法(主程序)希尔排序算法(主程序)参见教材参见教材P272P272O(O(O(O(n n1.251.25)O O O O(1.61.6n n1.251.25)经验公式经验公式dkdk值依次装在值依次装在dltatdltat中中473附:希尔排序附:希尔排序附:希尔排序附:希尔排序算法分析算法分析算法分析算法分析对对对对特特特特定定定定的的的的待待待待排排排排序序序序对对对对象象象象序序序序列列列列,可可可可以以以以准准准准确确确确地地地地估估估估算算算算关关关关键键键键码码码码的的的的比比比比较较较较次次次次数数数数和和和和对对对对象象象象移移移移动动动动次次次次数数数数。但但但但想想想想要要要要弄弄弄弄清清清清关关关关键键键键码码码码比比比比较较较较次次次次数数数数和和和和对对对对象象象象移移移移动动动动次次次次数数数数与与与与增增增增量量量量选选选选择择择择之之之之间间间间的的的的依依依依赖赖赖赖关关关关系系系系,并并并并给给给给出出出出完完完完整整整整的的的的数数数数学学学学分分分分析析析析,还还还还没没没没有人能够做到。有人能够做到。有人能够做到。有人能够做到。KnuthKnuth利利利利用用用用大大大大量量量量的的的的实实实实验验验验统统统统计计计计资资资资料料料料得得得得出出出出,当当当当n n很很很很大大大大时时时时,关关关关键键键键码码码码平平平平均均均均比比比比较较较较次次次次数数数数和和和和对对对对象象象象平平平平均均均均移移移移动动动动次次次次数数数数大大大大约约约约在在在在 n n1.251.25 到到到到 1.61.6n n1.251.25 的的的的范范范范围围围围内内内内。这这这这是是是是在在在在利利利利用用用用直直直直接接接接插插插插入入入入排排排排序序序序作作作作为为为为子子子子序序序序列列列列排排排排序序序序方方方方法法法法的的的的情情情情况况况况下下下下得得得得到的。到的。到的。到的。474void ShellInsert(SqList &L,int dk) for(i=dk+1;i=L.length; + i) if(ri.key 0 &(r0.keyrj.key); j=j-dk) rj+dk=rj; rj+dk=r0; 希尔排序算法(其中某一趟的排序操作)希尔排序算法(其中某一趟的排序操作)希尔排序算法(其中某一趟的排序操作)希尔排序算法(其中某一趟的排序操作)参见教材参见教材P272P272/对顺序表对顺序表L进行一趟增量为进行一趟增量为dk的的Shell排序,排序,dk为步长因子为步长因子/开始将开始将ri 插入有序增量子表插入有序增量子表/暂存在暂存在r0/关键字较大的记录在子表中后移关键字较大的记录在子表中后移/在本趟结束时将在本趟结束时将ri插入到正确位置插入到正确位置475课堂练习:课堂练习:1. 欲将序列(欲将序列(Q, H, C, Y, P, A, M, S, R, D, F, X)中的关键码按)中的关键码按字母升序重排,则字母升序重排,则初始步长为初始步长为4的希尔排序一趟的结果是?的希尔排序一趟的结果是?答:答:原始序列:原始序列: Q, H, C, Y, P, A, M, S, R, D, F, X shellshell一趟后:一趟后:2. 以以关关键键字字序序列列(256,301,751,129,937,863,742,694,076,438)为为例例,分分别别写写出出执执行行以以下下算算法法的的各各趟趟排排序序结结束束时时,关关键键字字序序列列的的状状态态,并并说说明明这这些些排排序序方方法法中中,哪哪些些易易于于在在链链表表(包包括各种单、双、循环链表)上实现?括各种单、双、循环链表)上实现? 直接插入排序直接插入排序 希尔排序(取希尔排序(取dk=5,3,1)P,Q,R,A,D,H,C,F,M,S,X ,Y答:答:显然,直接插入排序方法易于在链表上实现;但希尔排显然,直接插入排序方法易于在链表上实现;但希尔排序方法因为是按增量选择记录,不易于在链表上实现。序方法因为是按增量选择记录,不易于在链表上实现。 两种排序方法的中间状态分别描述如后:两种排序方法的中间状态分别描述如后:476原始序列:原始序列: 256256,301301,751751,129129,937937,863863,742742,694694,076076,438438 256256,301301 ,751751,129129,937937,863863,742742,694694,076076,438438 256256,301301,751751 ,129129,937937,863863,742742,694694,076076,438438 129129,256256,301301,751751 ,937937,863863,742742,694694,076076,438438 129129,256256,301301,751751,937937 ,863863,742742,694694,076076,438438 129129,256256,301301,751751,863863,937937 ,742742,694694,076076,438438 129129,256256,301301,742742,751751,863863,937937 ,694694,076076,438438 129129,256256,301301,694694,742742,751751,863863,937937 ,076076,438438 076076,129129,256256,301301,694694,742742,751751,863863,937937 ,438438 076076,129129,256256,301301,438438,694694,742742,751751,863863,937937 第第1趟趟第第2趟趟第第3趟趟第第4趟趟第第5趟趟第第6趟趟第第7趟趟第第8趟趟第第9趟趟477原始序列:原始序列: 256256,301301,751751,129129,937937,863863,742742,694694,076076,438438( (取取取取dk=5,3,1)dk=5,3,1)256256,301301,751751,129129,937937,863863,742742,694694,076076,438438256256,301301,751751,129129,937937,863863,742742,694694,076076,438438256256,301301,694694,129129,937937,863863,742742,751751,076076,438438256256,301301,694694,076076,937937,863863,742742,751751,129129,438438256256,301301,694694,076076,438438,863863,742742,751751,129129,937937第第1趟趟dk=5第第2趟趟dk=3dk=3第第3趟趟dk=1dk=1256256,301301,694694,076076,438438,863863,742742,751751,129129,937937256256,301301,694694,076076,438438,863863,742742,751751,129129,937937076076,301301,694694,256256,438438,863863,742742,751751,129129,937937076076,301301,694694,256256,438438,863863,742742,751751,129129,937937076076,301301,694694,256256,438438,863863,742742,751751,129129,937937076076,301301,129129,256256,438438,694694,742742,751751,863863,937937076076,301301,129129,256256,438438,694694,742742,751751,863863,937937076076,301301,129129,256256,438438,694694,742742,751751,863863,937937076076,129129,256256,301301,438438,694694,742742,751751,863863,9379374789.3 9.3 交换排序交换排序 两两比较待排序记录的关键两两比较待排序记录的关键两两比较待排序记录的关键两两比较待排序记录的关键码,如果发生逆序(即排列顺序与排序后的次序正好码,如果发生逆序(即排列顺序与排序后的次序正好码,如果发生逆序(即排列顺序与排序后的次序正好码,如果发生逆序(即排列顺序与排序后的次序正好相反),则交换之,直到所有记录都排好序为止。相反),则交换之,直到所有记录都排好序为止。相反),则交换之,直到所有记录都排好序为止。相反),则交换之,直到所有记录都排好序为止。交换排序的主要算法有:交换排序的主要算法有: 1) 冒泡排序冒泡排序 2) 快速排序快速排序交换排序的交换排序的交换排序的交换排序的基本思想基本思想基本思想基本思想是:是:是:是:479 1) 冒泡排序冒泡排序冒泡排序冒泡排序基本思路:基本思路:每趟不断将记录两两比较,并按每趟不断将记录两两比较,并按“前小后大前小后大”(或(或“前大后小前大后小”)规则交换。)规则交换。优点:优点:每趟结束时,不仅能挤出一个最大值到最后面位置,每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素;一旦下趟没有交换发还能同时部分理顺其他元素;一旦下趟没有交换发生,还可以提前结束排序。生,还可以提前结束排序。前提:前提:顺序存储结构顺序存储结构 例:例:关键字序列关键字序列 T=(21 T=(21,2525,4949,2525* *,1616,0808),请写出),请写出冒泡排序的具体实现过程。冒泡排序的具体实现过程。21,25,49, 25*,16, 0821,25,25*,16, 08 , 4921,25, 16, 08 ,25*,4921,16, 08 ,25, 25*,4916,08 ,21, 25, 25*,4908,16, 21, 25, 25*,49初态:初态:第第1趟趟第第2趟趟第第3趟趟第第4趟趟第第5趟趟480冒泡排序的算法分析冒泡排序的算法分析时间效率:时间效率:时间效率:时间效率:O O O O(n n n n2 2 2 2) ) ) ) 因为要考虑最坏情况因为要考虑最坏情况因为要考虑最坏情况因为要考虑最坏情况空间效率:空间效率:空间效率:空间效率:O O O O(1 1 1 1) 只在交换时用到一个缓冲单元只在交换时用到一个缓冲单元只在交换时用到一个缓冲单元只在交换时用到一个缓冲单元稳稳稳稳 定定定定 性:性:性:性: 稳定稳定稳定稳定 25252525和和和和25252525* * * *在排序前后的次序未改变在排序前后的次序未改变在排序前后的次序未改变在排序前后的次序未改变详细分析:详细分析:最好情况:最好情况:初始初始排列已经排列已经有序有序,只执行一趟起泡,做,只执行一趟起泡,做 n- -1 次次 关键码比较,不移动对象。关键码比较,不移动对象。最坏情形:最坏情形:初始初始排列排列逆序逆序,算法要执行算法要执行n-1 1趟起泡,第趟起泡,第i趟趟 (1 i n) 做了做了n- i 次关键码比较,执行了次关键码比较,执行了n-i 次对象交次对象交 换。此时的比较总次数换。此时的比较总次数KCN和记录移动次数和记录移动次数RMN为:为:4812 2 2 2) 快速排序快速排序快速排序快速排序 从待排序列中任取一个元素从待排序列中任取一个元素 ( (例如取第一个例如取第一个) ) 作为作为中心,所有比它小的元素一律前放,所有比它大的元素一中心,所有比它小的元素一律前放,所有比它大的元素一律后放,形成左右两个子表;然后再对各子表重新选择中律后放,形成左右两个子表;然后再对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个。心元素并依此规则调整,直到每个子表的元素只剩一个。此时便为有序序列了。此时便为有序序列了。基本思想:基本思想:优点:优点:因为每趟可以确定不止一个元素的位置,而且呈指数增因为每趟可以确定不止一个元素的位置,而且呈指数增加,所以特别快!加,所以特别快! 前提:前提:顺序存储结构顺序存储结构 482( ),设以首元素为枢轴中心设以首元素为枢轴中心例例1 1:关键字序列关键字序列 T=(21 T=(21,2525,4949,2525* *,1616,0808),请写出快速排序的算法步骤。),请写出快速排序的算法步骤。21, 25, 49, 25*,16, 08初态:初态:第第1趟:趟:第第2趟:趟:第第3趟:趟:讨论:讨论:讨论:讨论:1. 1. 1. 1. 这种不断划分子表的过程,计算机如何自动实现?这种不断划分子表的过程,计算机如何自动实现?这种不断划分子表的过程,计算机如何自动实现?这种不断划分子表的过程,计算机如何自动实现?2. “2. “2. “2. “快速排序快速排序快速排序快速排序”是否真的比任何排序算法都快?是否真的比任何排序算法都快?是否真的比任何排序算法都快?是否真的比任何排序算法都快?08,16,21,25, 25*,(49)2116,08,( )25,25*,49(08),16,21,25,(25*,49)483讨论讨论讨论讨论1.1.1.1.这种不断划分子表的过程,计算机如何自动实现?这种不断划分子表的过程,计算机如何自动实现?这种不断划分子表的过程,计算机如何自动实现?这种不断划分子表的过程,计算机如何自动实现?编程时:编程时:每一趟的子表的形成是采用从两头向中间交替式逼近法;每一趟的子表的形成是采用从两头向中间交替式逼近法;由于每趟中对各子表的操作都相似,主程序可采用递归算法。由于每趟中对各子表的操作都相似,主程序可采用递归算法。见教材见教材P275int int PartitionPartitionPartitionPartition(SqList &L,(SqList &L,int lowint low, ,int highint high) ) /一趟快排一趟快排/交换子表交换子表 rlowhigh rlowhigh的记录,使支点(枢轴)记录到位,并返回其位置。的记录,使支点(枢轴)记录到位,并返回其位置。返回时,在支点之前的记录均不大于它,支点之后的记录均不小于它。返回时,在支点之前的记录均不大于它,支点之后的记录均不小于它。 r0=r0=rlowrlow; ; /以子表的首记录作为支点记录,放入以子表的首记录作为支点记录,放入r0r0单元单元(续下页)(续下页)一趟快速排序算法(针对一个子表的操作)一趟快速排序算法(针对一个子表的操作)484pivotkey=pivotkey=rlow.keyrlow.key; ; /取支点的关键码存入取支点的关键码存入pivotkeypivotkey变量变量while(low high)while(low high) /从表的两端交替地向中间扫描从表的两端交替地向中间扫描while(while(lowhighlow=pivotkeyrhigh.key=pivotkey ) ) - -high;- -high;- -high;- -high; rlow=rhigh; rlow=rhigh; /将比支点小的记录交换到低端;将比支点小的记录交换到低端;while(while(lowhighlowhigh & & rlow.key=pivotkeyrlow.key=pivotkey) ) + +low;+ +low;+ +low;+ +low; rhigh=rlow; rhigh=rlow; /将比支点大的记录交换到高端;将比支点大的记录交换到高端; rlow=r0; rlow=r0; /支点记录到位;支点记录到位;return low; return low; /返回支点记录所在位置。返回支点记录所在位置。 /PartitionPartitionPartitionPartition485Low=high=Low=high=Low=high=Low=high=3 3 3 3,本趟停止,将本趟停止,将本趟停止,将本趟停止,将支点定位并返回位置信息支点定位并返回位置信息支点定位并返回位置信息支点定位并返回位置信息例例2:关键字序列关键字序列 T=(21,25,49,25*,16,08),),请写出快速排序算法的一趟实现过程。请写出快速排序算法的一趟实现过程。ri0123456初态初态21254925*1608第第1趟趟highhighlowlow210825164925*321pivotkey=pivotkey=212108251649( 08 ,16 ) 21 ( 25* , 49, 25 )25252525* * * *跑到了前面,跑到了前面,跑到了前面,跑到了前面,不稳定不稳定不稳定不稳定!486j从高端从高端扫描扫描寻找小于寻找小于pivot的元素的元素i从低端从低端扫描扫描寻找大于寻找大于pivot的元素的元素i=low; j=high;r0=rlow; pivot=rlow.key;i ji =pivot-j;ri = rj;i j &ri.key=pivot-i;rj = ri;ri = r0;return ok;Y YY YY YN NN NN N一趟快速排序算法流程图一趟快速排序算法流程图一趟快速排序算法流程图一趟快速排序算法流程图487void QSort ( SqList &L, int low, int high ) if ( low 1/对顺序表对顺序表L中的子序列中的子序列r lowhigh 作快速排序作快速排序/一趟快排,将一趟快排,将r 一分为二一分为二/在左子区间进行递归快排,直到长度为在左子区间进行递归快排,直到长度为1/在右子区间进行递归快排,直到长度为在右子区间进行递归快排,直到长度为1/QSort新的新的lowvoid void Q QuickuickSort Sort ( ( SqList SqList &L) &L) QSort QSort (L,(L, 1, L.length ); ); 对顺序表对顺序表对顺序表对顺序表L L进行快速进行快速进行快速进行快速排序的操作函数为:排序的操作函数为:排序的操作函数为:排序的操作函数为:488例例3:以关键字序列(以关键字序列(256,301,751,129,937,863,742,694,076,438)为例,写出执行快速算法的)为例,写出执行快速算法的各趟各趟排排序结束时,关键字序列的状态。序结束时,关键字序列的状态。原始序列:原始序列: 256256,301301,751751,129129,937937,863863,742742,694694,076076,438438第第1趟趟第第2趟趟第第3趟趟第第4趟趟256256,301301,751751,129129,937937,863863,742742,694694,076076,438438076076076076,129129,256256256256,751751751751,937937,863863,742742,694694,301301,438438要求模拟算法实现步骤要求模拟算法实现步骤256256256256076076301301129129751751256256256256076076076076,129129,256256256256,438438,301301,694694,742742,694694,863863,937937751751751751076076076076,129129129129,256256256256,438438438438,301301,694694,742742,751751751751,863863863863,937937076076076076,129129129129,256256256256,301301,301301,694694,742742,751751751751,863863863863,937937438438438438076076076076,129129129129,256256256256,301301301301,438438438438,694694694694,742742,751751751751,863863863863,937937937937时间效率:时间效率:O(nlogO(nlogO(nlogO(nlog2 2 2 2n) n) n) n) 因为每趟确定的元素呈指数增加因为每趟确定的元素呈指数增加因为每趟确定的元素呈指数增加因为每趟确定的元素呈指数增加空间效率:空间效率:O O O O(loglogloglog2 2 2 2n n n n)因为算法的递归性,要用到栈空间因为算法的递归性,要用到栈空间因为算法的递归性,要用到栈空间因为算法的递归性,要用到栈空间稳稳 定定 性:性: 不稳定不稳定不稳定不稳定 因为可选任一元素为支点。因为可选任一元素为支点。因为可选任一元素为支点。因为可选任一元素为支点。489快速排序算法详细分析:快速排序算法详细分析:快速排序算法详细分析:快速排序算法详细分析:可以证明,函数可以证明,函数quicksort的平均计算时间也是的平均计算时间也是O(nlog2n)。实实验结果表明:就平均计算时间而言,快速排序是我们所讨论验结果表明:就平均计算时间而言,快速排序是我们所讨论的所有内排序方法中最好的一个的所有内排序方法中最好的一个。快速排序是递归的,需要有一个栈存放每层递归调用时的指快速排序是递归的,需要有一个栈存放每层递归调用时的指针和参数针和参数(新的(新的lowlow和和highhigh)。最大递归调用层次数与递归树最大递归调用层次数与递归树的深度一致,理想情况为的深度一致,理想情况为 log2(n+1)log2(n+1) 。因此,要求存储开销因此,要求存储开销为为 o(log2n)o(log2n)。最好情况:最好情况:如果每次划分对一个对象定位后,该对象的左侧如果每次划分对一个对象定位后,该对象的左侧子序列与右侧子序列的长度相同,则下一步将是对两个长度子序列与右侧子序列的长度相同,则下一步将是对两个长度减半的子序列进行排序,这是最理想的情况。此时,快速排减半的子序列进行排序,这是最理想的情况。此时,快速排序的趟数最少。序的趟数最少。490最坏情况:最坏情况:即待排序对象序列已经按其关键码从小到大即待排序对象序列已经按其关键码从小到大排好序的情况下,排好序的情况下,其递归树成为单支树其递归树成为单支树,每次划分只得到,每次划分只得到一个比上一次少一个对象的子序列。这样,必须经过一个比上一次少一个对象的子序列。这样,必须经过 n-1 趟才能把所有对象定位,而且第趟才能把所有对象定位,而且第 i 趟需要经过趟需要经过 n-i 次关键次关键码比较才能找到第码比较才能找到第 i 个对象的安放位置,总的关键码比较个对象的安放位置,总的关键码比较次数将达到次数将达到n n2 2/2/2 快速排序是一个快速排序是一个不稳定的不稳定的排序方法排序方法491讨论讨论2.2. “快速排序快速排序”是否真的比任何排序算法都是否真的比任何排序算法都快?快?设每个子表的支点都在中间(比较均衡),则:设每个子表的支点都在中间(比较均衡),则:第第1 1趟比较,趟比较,可以确定可以确定1 1个个元素的位置;元素的位置;第第2 2趟比较(趟比较(2 2个子表),个子表),可以再确定可以再确定2 2个个元素的位置;元素的位置;第第3 3趟比较(趟比较(4 4个子表),个子表),可以再确定可以再确定4 4个个元素的位置;元素的位置;第第4 4趟比较(趟比较(8 8个子表),个子表),可以再确定可以再确定8 8个个元素的位置;元素的位置; 只需只需 loglog2 2n n 1 1趟便可排好序。趟便可排好序。基本上是!因为每趟可以确定的数据元素是呈指数增加的!基本上是!因为每趟可以确定的数据元素是呈指数增加的!而且,每趟需要比较和移动的元素也呈指数下降,加上编程而且,每趟需要比较和移动的元素也呈指数下降,加上编程时使用了交替逼近技巧,更进一步减少了移动次数,所以速度特时使用了交替逼近技巧,更进一步减少了移动次数,所以速度特别快。别快。教材教材P276P276有证明:快速排序的平均排序效率为有证明:快速排序的平均排序效率为O(nlogO(nlog2 2n)n);但最坏情况但最坏情况( (例如已经有序例如已经有序) )下仍为下仍为O(nO(n2 2),),改进措施见改进措施见P277P277。4929.4 9.4 选择排序选择排序选择排序有多种具体实现算法:选择排序有多种具体实现算法: 1) 简单选择排序简单选择排序 2) 锦标赛排序锦标赛排序 3) 堆排序堆排序选择排序的选择排序的选择排序的选择排序的基本思想基本思想基本思想基本思想是:是:是:是:每一趟在后面每一趟在后面每一趟在后面每一趟在后面n-in-i 个待排记录中个待排记录中个待排记录中个待排记录中选取关键字最小的记录作为有序序列中的第选取关键字最小的记录作为有序序列中的第选取关键字最小的记录作为有序序列中的第选取关键字最小的记录作为有序序列中的第i i 个记录。个记录。个记录。个记录。4931 1)简单选择排序)简单选择排序)简单选择排序)简单选择排序思思思思路路路路简简简简单单单单:每每每每经经经经过过过过一一一一趟趟趟趟比比比比较较较较就就就就找找找找出出出出一一一一个个个个最最最最小小小小值值值值,与与与与待待待待排序列最前面的位置互换即可。排序列最前面的位置互换即可。排序列最前面的位置互换即可。排序列最前面的位置互换即可。首首首首先先先先,在在在在n n个个个个记记记记录录录录中中中中选选选选择择择择最最最最小小小小者者者者放放放放到到到到r1r1位位位位置置置置;然然然然后后后后,从从从从剩剩剩剩余余余余的的的的n-1n-1个个个个记记记记录录录录中中中中选选选选择择择择最最最最小小小小者者者者放放放放到到到到r2r2 位位位位置置置置;如如如如此此此此进进进进行行行行下下下下去去去去,直到全部有序为止。直到全部有序为止。直到全部有序为止。直到全部有序为止。优点:优点:优点:优点:实现简单实现简单实现简单实现简单缺点:缺点:缺点:缺点:每趟只能确定一个元素,表长为每趟只能确定一个元素,表长为每趟只能确定一个元素,表长为每趟只能确定一个元素,表长为n n n n时需要时需要时需要时需要n-1n-1n-1n-1趟趟趟趟前提:前提:前提:前提:顺序存储结构顺序存储结构顺序存储结构顺序存储结构 494例:例:关键字序列关键字序列T= T= (2121,2525,4949,2525* *,1616,0808),请),请给出简单选择排序的具体实现过程。给出简单选择排序的具体实现过程。原始序列:原始序列: 21,25,49,25*,16,08第第1趟趟第第2趟趟第第3趟趟第第4趟趟第第5趟趟08,25,49,25*,16,2108,16, 49,25*,25,2108,16, 21,25*,25,4908,16, 21,25*,25,4908,16, 21,25*,25,49时间效率:时间效率: O(nO(nO(nO(n2 2 2 2) ) ) )虽移动次数较少,但比较次数仍多。虽移动次数较少,但比较次数仍多。 空间效率:空间效率:O O O O(1 1 1 1)交换时用到一个暂存单元!交换时用到一个暂存单元!算法的稳定性:算法的稳定性:不稳定不稳定不稳定不稳定因为排序时,因为排序时,25*25*到了到了2525的前面。的前面。最小值最小值 0808 与与r1r1交换位置交换位置495简单选择排序的算法如下:简单选择排序的算法如下:简单选择排序的算法如下:简单选择排序的算法如下:(亦可参见教材(亦可参见教材P276P276)Void SelectSort(SqList &L ) for (i=1; iL.length; +i) j = SelectMinKey(L,i); if( i!=j ) ri rj; /SelectSort/对顺序表对顺序表L L作简单选择排序作简单选择排序/选择第选择第i i小的记录,并交换到位小的记录,并交换到位/在在rriiL.L.lengthlength 中选择中选择keykey最小的记录最小的记录/与第与第i i个记录交换个记录交换讨论:讨论:能否利用(能否利用(或记忆或记忆)首趟的)首趟的n-1n-1次比较所得次比较所得信息,从而尽量减少后续比较次数呢?信息,从而尽量减少后续比较次数呢? 答:答:能!能!请看请看锦标赛排序锦标赛排序和和堆排序堆排序!4962 2) 锦标赛排序锦标赛排序锦标赛排序锦标赛排序 (又称树形选择排序又称树形选择排序)基本思想:基本思想:基本思想:基本思想:与体育比赛时的淘汰赛类似。与体育比赛时的淘汰赛类似。与体育比赛时的淘汰赛类似。与体育比赛时的淘汰赛类似。 首先对首先对首先对首先对 n n 个记录的关键字进行两两比较,得到个记录的关键字进行两两比较,得到个记录的关键字进行两两比较,得到个记录的关键字进行两两比较,得到 n n/2/2 个个个个优胜者优胜者优胜者优胜者( ( ( (关键字小者关键字小者关键字小者关键字小者) ) ) ),作为第一步比较的结果保留下来。然,作为第一步比较的结果保留下来。然,作为第一步比较的结果保留下来。然,作为第一步比较的结果保留下来。然后在这后在这后在这后在这 n n/2/2 个较小者之间再进行两两比较,个较小者之间再进行两两比较,个较小者之间再进行两两比较,个较小者之间再进行两两比较,如此重复,如此重复,如此重复,如此重复,直到选出最小关键字的记录为止。直到选出最小关键字的记录为止。直到选出最小关键字的记录为止。直到选出最小关键字的记录为止。优点:优点:优点:优点:减少比较次数,加快排序速度减少比较次数,加快排序速度减少比较次数,加快排序速度减少比较次数,加快排序速度缺点:缺点:缺点:缺点:空间效率低空间效率低空间效率低空间效率低例:例:关键字序列关键字序列T= (21,25,49,25*,16,08,63),),请给出锦标赛排序的具体实现过程。请给出锦标赛排序的具体实现过程。4970808Winner Winner ( (胜者胜者胜者胜者) )212108080808636325*25*212121212525494925*25*161608086363r1注:注:为便于自动处理,建议每个记录多开两个特殊分量:为便于自动处理,建议每个记录多开两个特殊分量:keyotherinfoIndex(结点位置编号)结点位置编号)Tag(是否参加比较)(是否参加比较)初态:初态:初态:初态:补足补足补足补足2 2 2 2k k k k( k=( k=( k=( k= loglogloglog2 2 2 2n n n n ) ) ) )个叶子结点个叶子结点个叶子结点个叶子结点第一趟:第一趟:第一趟:第一趟:498第二趟:第二趟:第二趟:第二趟:0808212108080808636325*25*212121212525494925*25*161608086363161616161616r2Winner Winner ( (胜者胜者胜者胜者) )求次小值求次小值1616时时, ,只需比较只需比较2 2 2 2次,次,即只比较即只比较 loglogloglog2 2 2 2n n n n -1 -1 -1 -1次。次。次。次。令其令其TagTag0,0,不参与比较不参与比较499令其令其TagTag0,0,不参与比较不参与比较第三趟:第三趟:第三趟:第三趟:1616212116161616636325*25*212121212525494925*25*161608086363r3Winner Winner ( (胜者胜者胜者胜者) )636321215000808212108080808636325*25*212121212525494925*25*16160808636363632121第四趟:第四趟:第四趟:第四趟:r4Winner Winner ( (胜者胜者胜者胜者) )2525252525255010808212108080808636325*25*212121212525494925*25*16160808636316161616161663632121252525252525第五趟:第五趟:第五趟:第五趟:r5Winner Winner ( (胜者胜者胜者胜者) )25*25*25*25*5020808212108080808636325*25*212121212525494925*25*1616080863631616161616166363212125252525252525*25*25*25*第六趟:第六趟:第六趟:第六趟:r6Winner Winner ( (胜者胜者胜者胜者) )4949494949495030808212108080808636325*25*212121212525494925*25*1616080863631616161616166363212125252525252525*25*25*25*494949494949第七趟:第七趟:第七趟:第七趟:r7Winner Winner ( (胜者胜者胜者胜者) )6363504算法分析:算法分析:算法分析:算法分析:锦标赛排序构成的树是满锦标赛排序构成的树是满锦标赛排序构成的树是满锦标赛排序构成的树是满( ( ( (完全)二叉树,其深度为完全)二叉树,其深度为完全)二叉树,其深度为完全)二叉树,其深度为 loglog2 2n n +1 +1,其中其中其中其中 n n 为待排序元素个数。为待排序元素个数。为待排序元素个数。为待排序元素个数。时间复杂度:时间复杂度:时间复杂度:时间复杂度:O(O(n nloglog2 2n n) ) n个记录各自比较约个记录各自比较约log2n次次空间效率:空间效率:空间效率:空间效率: O(O(n n) 胜者树的附加内结点共有胜者树的附加内结点共有n-1个!个!稳定性:稳定性:稳定性:稳定性:稳定稳定稳定稳定 左右结点相同者左为先左右结点相同者左为先讨论:讨论:讨论:讨论: 在简单选择排序过程中,每当我们从表中选出在简单选择排序过程中,每当我们从表中选出在简单选择排序过程中,每当我们从表中选出在简单选择排序过程中,每当我们从表中选出最小最小最小最小元素之元素之元素之元素之后,再选后,再选后,再选后,再选次最小次最小次最小次最小元素时,必须把表中剩余元素再扫描一次。元素时,必须把表中剩余元素再扫描一次。元素时,必须把表中剩余元素再扫描一次。元素时,必须把表中剩余元素再扫描一次。这样,同一个元素会被扫描多次,浪费!这样,同一个元素会被扫描多次,浪费!这样,同一个元素会被扫描多次,浪费!这样,同一个元素会被扫描多次,浪费! 能否利用上次扫描的结果定出下一次的选择结果呢?能否利用上次扫描的结果定出下一次的选择结果呢?能否利用上次扫描的结果定出下一次的选择结果呢?能否利用上次扫描的结果定出下一次的选择结果呢? 答:答:答:答:能!能!能!能!请看请看请看请看堆排序堆排序堆排序堆排序算法算法算法算法n =2n =2k k = = 叶子总数叶子总数5053 3) 堆排序堆排序堆排序堆排序1. 1. 1. 1. 什么是堆?什么是堆?什么是堆?什么是堆?堆的定义:堆的定义:堆的定义:堆的定义:设有设有设有设有n n个元素的序列个元素的序列个元素的序列个元素的序列 k k1 1,k k2 2,k kn n,当且仅,当且仅,当且仅,当且仅当满足下述关系之一时,称之为堆。当满足下述关系之一时,称之为堆。当满足下述关系之一时,称之为堆。当满足下述关系之一时,称之为堆。 k ki i k k2i2ik ki i k k2i+12i+1k ki i k k2i2ik ki i k k2i+12i+1或者或者或者或者i=1, 2, n/2i=1, 2, n/2解释:解释:解释:解释:如果让满足以上条件的元素序列如果让满足以上条件的元素序列如果让满足以上条件的元素序列如果让满足以上条件的元素序列 (k k1 1,k k2 2,k kn n)顺次排成一棵顺次排成一棵顺次排成一棵顺次排成一棵完全二叉树,完全二叉树,完全二叉树,完全二叉树,则此树的特点是:则此树的特点是:则此树的特点是:则此树的特点是: 树中所有结点的值均大于(或小于)其左右孩子,此树的树中所有结点的值均大于(或小于)其左右孩子,此树的树中所有结点的值均大于(或小于)其左右孩子,此树的树中所有结点的值均大于(或小于)其左右孩子,此树的根结点根结点根结点根结点(即堆顶)(即堆顶)(即堆顶)(即堆顶)必最大(或最小)。必最大(或最小)。必最大(或最小)。必最大(或最小)。2. 2. 2. 2. 怎样建堆?怎样建堆?怎样建堆?怎样建堆?3. 3. 3. 3. 怎样堆排序?怎样堆排序?怎样堆排序?怎样堆排序?506080825254646494958586767234561(大根堆)(大根堆)91918585666676765858676723456155557例:例:例:例:有序列有序列T1=(08, 25, 49, 46, 58, 67)和序列)和序列T2=(91, 85, 76, 66, 58, 67, 55),判断它们是否判断它们是否 “堆堆”?(小根堆)(小根堆)(小顶堆)(小顶堆) (最小堆)(最小堆)(大顶堆)(大顶堆)(最大堆)(最大堆)507步骤:步骤:步骤:步骤:从最后一个从最后一个从最后一个从最后一个非终端结点非终端结点非终端结点非终端结点开始往前逐步调整,让每个双开始往前逐步调整,让每个双开始往前逐步调整,让每个双开始往前逐步调整,让每个双亲大于(或小于)子女,直到根结点为止。亲大于(或小于)子女,直到根结点为止。亲大于(或小于)子女,直到根结点为止。亲大于(或小于)子女,直到根结点为止。2121252525*25*494916160808123456例:例:关键字序列关键字序列T= (21T= (21,2525,4949,2525* *,1616,0808),请建),请建大根堆大根堆。2. 2. 2. 2. 怎样建堆?怎样建堆?怎样建堆?怎样建堆?解:解:为便于理解,先将原始序列画成完全二叉树的形式:为便于理解,先将原始序列画成完全二叉树的形式:完全二叉树的第一个非终端结点完全二叉树的第一个非终端结点完全二叉树的第一个非终端结点完全二叉树的第一个非终端结点编号必为编号必为编号必为编号必为 n/2n/2n/2n/2 !( ( ( (性质性质性质性质5)5)5)5)注:注:终端结点(即叶子)没有任何子女,无需单独调整。终端结点(即叶子)没有任何子女,无需单独调整。2121i=3:i=3: 49大于大于08,不必调整;,不必调整;i=2:i=2: 25大于大于25*和和16,也不必调整;,也不必调整;i=1:i=1: 21小于小于25和和49,要调整!,要调整!4949而且而且21还应当向下比较!还应当向下比较!508Void HeapAdjust(HeapType &H, int s,int m) / rc = H.rs; for ( j=2*s; j=m; j*=2) / 沿沿key较大的孩子结点向下筛选较大的孩子结点向下筛选 if ( j0; - i ) HeapAdjust(H,i, H.length ); /初始堆初始堆 for ( i = H.length; i 1; -i) H.r1 H.ri; /交换,要借用交换,要借用temp HeapAdjust( H, 1,i-1 ); /重建最大堆重建最大堆 堆排序的算法堆排序的算法堆排序的算法堆排序的算法参见教材参见教材参见教材参见教材P281-282P281-282这是针对结点这是针对结点i i 的堆调整函数的堆调整函数(也是建堆函(也是建堆函数)数),每次调用耗时,每次调用耗时O(logO(log2 2n)n)516附附附附1 1:基于初始堆:基于初始堆:基于初始堆:基于初始堆进行堆排序的算法步骤:进行堆排序的算法步骤:进行堆排序的算法步骤:进行堆排序的算法步骤:堆的第一个对象堆的第一个对象堆的第一个对象堆的第一个对象V V00具有最大的关键码,将具有最大的关键码,将具有最大的关键码,将具有最大的关键码,将V V00与与与与V V n n 对调,对调,对调,对调,把具有最大关键码的对象交换到最后,再对前面的把具有最大关键码的对象交换到最后,再对前面的把具有最大关键码的对象交换到最后,再对前面的把具有最大关键码的对象交换到最后,再对前面的n n-1-1个对个对个对个对象,使用堆的调整算法,重新建立堆。结果具有次最大关象,使用堆的调整算法,重新建立堆。结果具有次最大关象,使用堆的调整算法,重新建立堆。结果具有次最大关象,使用堆的调整算法,重新建立堆。结果具有次最大关键码的对象又上浮到堆顶,即键码的对象又上浮到堆顶,即键码的对象又上浮到堆顶,即键码的对象又上浮到堆顶,即V V00位置。位置。位置。位置。 再对调再对调再对调再对调V V00和和和和V V n-n-11,调用对前调用对前调用对前调用对前n n-2-2个对象重新调整,个对象重新调整,个对象重新调整,个对象重新调整,如如如如此反复,最后得到全部排序好的对象序列。此反复,最后得到全部排序好的对象序列。此反复,最后得到全部排序好的对象序列。此反复,最后得到全部排序好的对象序列。517比较左右孩比较左右孩子大小,子大小,j指指向大者向大者比较大孩子比较大孩子与与rc的大小的大小若大向上浮若大向上浮rc = H.rs; j = 2s; jm &H.rj.keyH.rj+1.key+ j; / 指向右兄弟指向右兄弟j H.rj.keyH.rs=H.rj; s=j;j=2*j; /指向左孩子指向左孩子NNNYYYH.rsm中除中除rs外,其他具有堆特征外,其他具有堆特征现调整现调整rs的值的值 ,使,使H.rsm为堆为堆附附附附2 2:算法流程算法流程算法流程算法流程518堆排序算法分析:堆排序算法分析:堆排序算法分析:堆排序算法分析:时间效率:时间效率:时间效率:时间效率: O(O(n nloglog2 2n n) )。因为整个排序过程中需因为整个排序过程中需因为整个排序过程中需因为整个排序过程中需要调用要调用要调用要调用n n-1-1次次次次HeapAdjust( )算法,而算法本身耗算法,而算法本身耗算法,而算法本身耗算法,而算法本身耗时为时为时为时为loglog2 2n n;空间效率:空间效率:空间效率:空间效率:O(1)O(1)。仅在第二个仅在第二个仅在第二个仅在第二个forfor循环中交换记循环中交换记循环中交换记循环中交换记录时用到一个临时变量录时用到一个临时变量录时用到一个临时变量录时用到一个临时变量temptemptemptemp。稳定性:稳定性:稳定性:稳定性: 不稳定。不稳定。不稳定。不稳定。优点:优点:优点:优点:对小文件效果不明显,但对大文件有效。对小文件效果不明显,但对大文件有效。对小文件效果不明显,但对大文件有效。对小文件效果不明显,但对大文件有效。5199.5 9.5 归并排序归并排序归并排序的基本思想是:归并排序的基本思想是:归并排序的基本思想是:归并排序的基本思想是:将两个(或以上)的有序将两个(或以上)的有序将两个(或以上)的有序将两个(或以上)的有序表组成新的有序表。表组成新的有序表。表组成新的有序表。表组成新的有序表。更实际的意义:更实际的意义:更实际的意义:更实际的意义:可以把一个长度为可以把一个长度为可以把一个长度为可以把一个长度为n n 的无序序列看成是的无序序列看成是的无序序列看成是的无序序列看成是 n n 个长度为个长度为个长度为个长度为 1 1 的有序子序列的有序子序列的有序子序列的有序子序列 ,首先做两两归并,得到,首先做两两归并,得到,首先做两两归并,得到,首先做两两归并,得到 n n / 2/ 2 个长度为个长度为个长度为个长度为 2 2 的子序列的子序列的子序列的子序列 ;再做两两归并,;再做两两归并,;再做两两归并,;再做两两归并,如此重复,直,如此重复,直,如此重复,直,如此重复,直到最后得到一个长度为到最后得到一个长度为到最后得到一个长度为到最后得到一个长度为 n n 的有序序列。的有序序列。的有序序列。的有序序列。例:例:例:例:关键字序列关键字序列T= (21,25,49,25*,93,62,72,08,37,16,54),请给出归并排序的具体实),请给出归并排序的具体实现过程。现过程。520lenlen=1=1lenlen=2=2lenlen=4=4lenlen=8=8lenlen=16=16整个归并排序仅需整个归并排序仅需整个归并排序仅需整个归并排序仅需 loglog2 2n n 趟趟趟趟521一趟归并排序算法一趟归并排序算法一趟归并排序算法一趟归并排序算法: (: (两路有序并为一路两路有序并为一路两路有序并为一路两路有序并为一路) ) 参见教材参见教材参见教材参见教材P283P283void MergeMerge (SR,&TR,i, m, n) / 将将有序的有序的SRim和和SRm+1n归并为归并为有序的有序的TRin for(k=i , j=m+1; i=m & j=ni=m & j=n; +k ) if ( SRi= SRj )TRk=SRi+; else TRk=SRj+; / 将将SR中记录由小到大地并入中记录由小到大地并入TR if (i=m) TRkn=SRim; / 将剩余的将剩余的SRim复制到复制到TR if (j=n) TRkn=SRjn; / 将剩余的将剩余的SRjn复制到复制到TR / Merge522 void MSort MSort (SR,&TR1,s, t) / 将无序的将无序的SRst归并排序为归并排序为TR1st if ( s=t )TR1s=SRs; / 当当len=1时返回时返回 else m=(s+t)/2; / 将将SR st平分为平分为SR sm和和SR m+1t MSort MSort (SR,&TR2,s, m); / 将将SR 一分为二一分为二, 2分为分为4 / 递归地将递归地将SR sm归并为有序的归并为有序的TR2sm MSortMSort (SR,&TR2,m+1, t ); / 递归地将递归地将SR m+1t归并为有序的归并为有序的TR2m+1t MergeMerge(TR2, TR1, s, m, t ); / 将将TR2 sm和和TR2 m+1t归并到归并到TR1 st / MSort递归形式的两路归并排序算法递归形式的两路归并排序算法递归形式的两路归并排序算法递归形式的两路归并排序算法: : 参见教材参见教材参见教材参见教材P284P284 ( (一路无序变为有序一路无序变为有序一路无序变为有序一路无序变为有序) )简言之,先由简言之,先由“长长”无序变成无序变成“短短”有有序,序, 再从再从“短短”有序归并为有序归并为“长长”有有序。序。初次调用时为(初次调用时为(L, L, 1, length)523归并排序算法分析:归并排序算法分析:归并排序算法分析:归并排序算法分析: 时间效率:时间效率:时间效率:时间效率: O(O(n nloglog2 2n n) )一一趟趟归归并并排排序序的的操操作作是是:调调用用n/2h次次算算法法merge将将SR1.n中中前前后后相相邻邻且且长长度度为为h的的有有序序段段进进行行两两两两归归并并,得得到到前前后后相相邻邻长长度度为为2h的的有有序序段段,并并存存放放在在TR1.n中中,整整个个归归并并排排序序需需要要进行进行log2n 趟趟,所以算法总的时间复杂度为所以算法总的时间复杂度为O(nlog2n)。 空间效率:空间效率:空间效率:空间效率: O(O(n n) ) 因因为为需需要要一一个个与与原原始始序序列列同同样样大大小小的的辅辅助助序序列列(TR)。这这正正是此算法的缺点。是此算法的缺点。 稳定性:稳定性:稳定性:稳定性:稳定稳定稳定稳定5249.6 9.6 基数排序基数排序 (Radix Sort)要讨论的问题:要讨论的问题:要讨论的问题:要讨论的问题:1. 1. 什么是什么是什么是什么是“ “多关键字多关键字多关键字多关键字” ”排序?实现方法?排序?实现方法?排序?实现方法?排序?实现方法?2. 2. 单逻辑关键字怎样单逻辑关键字怎样单逻辑关键字怎样单逻辑关键字怎样“ “按位值按位值按位值按位值” ”排序?排序?排序?排序?基数排序的基本思想是:基数排序的基本思想是:基数排序的基本思想是:基数排序的基本思想是:借助多关键字排序的思想对单逻辑关键字进行排借助多关键字排序的思想对单逻辑关键字进行排借助多关键字排序的思想对单逻辑关键字进行排借助多关键字排序的思想对单逻辑关键字进行排序。即:用关键字序。即:用关键字序。即:用关键字序。即:用关键字不同的位值不同的位值不同的位值不同的位值进行排序。进行排序。进行排序。进行排序。5251. 1. 什么是什么是什么是什么是“ “多关键字多关键字多关键字多关键字” ”排序?实现方排序?实现方排序?实现方排序?实现方法?法?法?法?例例例例1 1:对一副扑克牌该如何排序?对一副扑克牌该如何排序?对一副扑克牌该如何排序?对一副扑克牌该如何排序? 若规定花色和面值的顺序关系为:若规定花色和面值的顺序关系为:若规定花色和面值的顺序关系为:若规定花色和面值的顺序关系为: 花色花色花色花色: 面值:面值:面值:面值:2 3 4 5 6 7 8 9 10 J Q K A2 3 4 5 6 7 8 9 10 J Q K A 则则则则可以可以可以可以先按花色先按花色先按花色先按花色排序,花色相同者排序,花色相同者排序,花色相同者排序,花色相同者再按面值再按面值再按面值再按面值排序;排序;排序;排序; 也可以先按面值排序,面值相同者再按花色排序。也可以先按面值排序,面值相同者再按花色排序。也可以先按面值排序,面值相同者再按花色排序。也可以先按面值排序,面值相同者再按花色排序。例例例例2 2 2 2:职工分房该如何排序?职工分房该如何排序?职工分房该如何排序?职工分房该如何排序? 华工规定:华工规定:华工规定:华工规定:先以总分先以总分先以总分先以总分排序(职称分工龄分);排序(职称分工龄分);排序(职称分工龄分);排序(职称分工龄分);总分相同者,总分相同者,总分相同者,总分相同者,再按配偶总分再按配偶总分再按配偶总分再按配偶总分排序,其次按配偶职称、排序,其次按配偶职称、排序,其次按配偶职称、排序,其次按配偶职称、工龄、人口工龄、人口工龄、人口工龄、人口等等排序。等等排序。等等排序。等等排序。以上两例都是典型的多关键字排序!526多关键字排序的实现方法通常有两种:多关键字排序的实现方法通常有两种:多关键字排序的实现方法通常有两种:多关键字排序的实现方法通常有两种:最高位优先法最高位优先法最高位优先法最高位优先法MSD (Most Significant Digit first)MSD (Most Significant Digit first)例:例:例:例:对一副扑克牌该如何排序?对一副扑克牌该如何排序?对一副扑克牌该如何排序?对一副扑克牌该如何排序?答:答:答:答:若规定若规定若规定若规定花色为第一花色为第一花色为第一花色为第一关键字(高位),关键字(高位),关键字(高位),关键字(高位),面值为第二面值为第二面值为第二面值为第二关键字关键字关键字关键字(低位),则使用(低位),则使用(低位),则使用(低位),则使用MSDMSD和和和和LSDLSD方法都可以达到排序目的。方法都可以达到排序目的。方法都可以达到排序目的。方法都可以达到排序目的。MSDMSD方法的思路:方法的思路:方法的思路:方法的思路:先设立先设立先设立先设立4 4个花色个花色个花色个花色“ “箱箱箱箱” ”,将全部牌按花色分,将全部牌按花色分,将全部牌按花色分,将全部牌按花色分别归入别归入别归入别归入4 4个箱内(每个箱中有个箱内(每个箱中有个箱内(每个箱中有个箱内(每个箱中有1313张牌);然后对每个箱中的牌张牌);然后对每个箱中的牌张牌);然后对每个箱中的牌张牌);然后对每个箱中的牌按面值进行插入排序(或其它稳定算法)。按面值进行插入排序(或其它稳定算法)。按面值进行插入排序(或其它稳定算法)。按面值进行插入排序(或其它稳定算法)。LSDLSD方法的思路:方法的思路:方法的思路:方法的思路:先按面值分成先按面值分成先按面值分成先按面值分成1313堆(每堆堆(每堆堆(每堆堆(每堆4 4张牌),然后对张牌),然后对张牌),然后对张牌),然后对每堆中的牌按花色进行排序(用插入排序等稳定的算法)。每堆中的牌按花色进行排序(用插入排序等稳定的算法)。每堆中的牌按花色进行排序(用插入排序等稳定的算法)。每堆中的牌按花色进行排序(用插入排序等稳定的算法)。想一想:用哪种方法更快些想一想:用哪种方法更快些?最低位优先法最低位优先法最低位优先法最低位优先法LSD (Least Significant Digit first)LSD (Least Significant Digit first)5272. 2. 单逻辑单逻辑单逻辑单逻辑关键字怎样关键字怎样关键字怎样关键字怎样“ “按位值按位值按位值按位值” ”排序?排序?排序?排序?设设设设n n n n 个记录的序列为:个记录的序列为:个记录的序列为:个记录的序列为:V0, V1, , Vn-1 V0, V1, , Vn-1 V0, V1, , Vn-1 V0, V1, , Vn-1 ,可以把每,可以把每,可以把每,可以把每个记录个记录个记录个记录Vi Vi Vi Vi 的单关键码的单关键码的单关键码的单关键码 Ki Ki Ki Ki 看成是一个看成是一个看成是一个看成是一个d d d d元组(元组(元组(元组(Ki1, Ki2, Ki1, Ki2, Ki1, Ki2, Ki1, Ki2, , Kid, Kid, Kid, Kid),则其中的每一个分量),则其中的每一个分量),则其中的每一个分量),则其中的每一个分量Kij ( 1Kij ( 1Kij ( 1Kij ( 1 j j j j d ) d ) d ) d ) 也也也也可看成是一个关键字。可看成是一个关键字。可看成是一个关键字。可看成是一个关键字。4 4注注注注1 1 1 1: K K K Ki i i i1 1 1 1最高位最高位最高位最高位,K K K Ki i i id d d d最低位;最低位;最低位;最低位;K K K Ki i i i共有共有共有共有d d d d位,可看成位,可看成位,可看成位,可看成d d d d元组;元组;元组;元组;注注注注2 2: 每个分量每个分量每个分量每个分量K K K Ki i i ij j j j (1 (1 j j d d ) ) 有有有有radixradix种取值,则称种取值,则称种取值,则称种取值,则称radixradix为为为为基数基数基数基数。2626(9, 8, 4)(9, 8, 4)(0, 1, , 9)(0, 1, , 9)(a, b, , za, b, , z)(d, i, a, n)(d, i, a, n)3 31010例例例例1 1:关键码关键码关键码关键码984984可以看成是可以看成是可以看成是可以看成是 元组;基数元组;基数元组;基数元组;基数radixradix 为为为为 。例例例例2 2:关键码关键码关键码关键码diandian可以看成是可以看成是可以看成是可以看成是 元组;基数元组;基数元组;基数元组;基数radixradix 为为为为 。思路:思路:思路:思路:528因为有分组,故此算法需递归实现。讨论:讨论:讨论:讨论:是借用是借用MSDMSD方式来排序呢,还是借用方式来排序呢,还是借用LSDLSD方式方式?例:例:初始关键字序列初始关键字序列T=T=(32, 13, 27, 32*, 1932, 13, 27, 32*, 19,3333),请),请分别用分别用MSDMSD和和LSDLSD进行排序,并讨论其优缺点。进行排序,并讨论其优缺点。法法1(MSD):):原始序列:原始序列:32, 13, 27, 32*, 19, 33 先按高位先按高位K K K Ki i i i1 1 1 1 排序:排序:(13, 19), 27, (32, 32*,33) 再按低位再按低位K K K Ki i i i2 2 2 2 排序排序 : 13, 19 , 27, 32, 32*, 33法法2(LSD):): 原始序列:原始序列: 32, 13, 27, 32*, 19 ,33 先按低位先按低位K K K Ki i i i2 2 2 2排序:排序: 32, 32*, 13, 33, 27, 19 再按高位再按高位K K K Ki i i i1 1 1 1排序:排序: 13, 19 , 27, 32, 32*, 33无需分组,易编程实现!529例例例例:T=T=(0202,7777,7070,5454,6464,2121,5555,1111),用),用),用),用LSDLSD排序。排序。排序。排序。分析:分析:分析:分析:各关键字可视为各关键字可视为各关键字可视为各关键字可视为2 2元组元组元组元组;每位的取值范围是:每位的取值范围是:每位的取值范围是:每位的取值范围是:0-90-9;即;即;即;即基数基数基数基数radixradix 10 10 。因此,特设置因此,特设置因此,特设置因此,特设置1010个队列,并编号为个队列,并编号为个队列,并编号为个队列,并编号为0-90-9。1155216454707702原始序列原始序列原始序列原始序列1 12 23 34 45 56 67 78 8先对低位扫描先对低位扫描先对低位扫描先对低位扫描出队出队出队出队0 01 12 23 34 45 56 67 78 89 91010个队列个队列个队列个队列计算机怎样实现计算机怎样实现计算机怎样实现计算机怎样实现LSDLSD算法?算法?算法?算法?分配分配分配分配过程过程过程过程收集收集收集收集过程过程过程过程下一步下一步下一步下一步77556454021121701 12 23 34 45 56 67 78 8出队后序列出队后序列出队后序列出队后序列775554,6421,117002又称散列过程!又称散列过程!5300 01 12 23 34 45 56 67 78 89 9再次入队再次入队再次入队再次入队再次出队再次出队再次出队再次出队再对高位扫描再对高位扫描再对高位扫描再对高位扫描小结:小结:小结:小结:排序时经过了反复的排序时经过了反复的“分配分配”和和“收集收集”过程。当对关过程。当对关键字所有的位进行扫描排序后,整个序列便从无序变为有序键字所有的位进行扫描排序后,整个序列便从无序变为有序了。了。77556454021121701 12 23 34 45 56 67 78 8出队后序列出队后序列出队后序列出队后序列70,776454,55211102再次再次再次再次分配分配分配分配再次再次再次再次收集收集收集收集7770645554211102再次出队后序列再次出队后序列再次出队后序列再次出队后序列这种这种这种这种LSDLSDLSDLSD排序方法称为:排序方法称为:排序方法称为:排序方法称为: 基数排序基数排序基数排序基数排序531讨论:讨论:讨论:讨论:所用所用队列队列是顺序结构,浪费空间,能否改用是顺序结构,浪费空间,能否改用链式结构链式结构?用链队列来实现基数排序用链队列来实现基数排序用链队列来实现基数排序用链队列来实现基数排序链式基数排序链式基数排序链式基数排序链式基数排序实现思路:实现思路:实现思路:实现思路: 针对针对针对针对 d d 元组元组元组元组中的每一位分量,把原始中的每一位分量,把原始中的每一位分量,把原始中的每一位分量,把原始链表链表链表链表中中中中的所有记录的所有记录的所有记录的所有记录, , 按按按按K K K Ki i i ij j j j的取值,让的取值,让的取值,让的取值,让 j j = = d d, , d d- - - -1, , 11, , 1, 先先先先“ “分配分配分配分配” ”到到到到radixradix个个个个链队列链队列链队列链队列中去;中去;中去;中去; 然后再按各然后再按各然后再按各然后再按各链队列链队列链队列链队列的顺序,依次把记录从的顺序,依次把记录从的顺序,依次把记录从的顺序,依次把记录从链队列链队列链队列链队列中中中中“ “收集收集收集收集” ”起来;起来;起来;起来; 分别用这种分别用这种分别用这种分别用这种“ “分配分配分配分配” ”、“ “收集收集收集收集” ”的运算逐趟进行排序;的运算逐趟进行排序;的运算逐趟进行排序;的运算逐趟进行排序; 在最后一趟在最后一趟在最后一趟在最后一趟“ “分配分配分配分配” ”、“ “收集收集收集收集” ” 完成后,所有记录就完成后,所有记录就完成后,所有记录就完成后,所有记录就按其关键码的值从小到大排好序了。按其关键码的值从小到大排好序了。按其关键码的值从小到大排好序了。按其关键码的值从小到大排好序了。能!能!532 请实现以下关键字序列的请实现以下关键字序列的请实现以下关键字序列的请实现以下关键字序列的链式基数排序链式基数排序链式基数排序链式基数排序:T=T=T=T=(614614614614,738738738738,921921921921,485485485485,637637637637, 101 101 101 101,215215215215,530530530530,790790790790,306306306306)例例例例: : : :614614921921485485637637738738101101215215530530790790306306第一趟分配第一趟分配第一趟分配第一趟分配e0 e1 e2 e3 e4 e5 e6 e7 e8 e9614738921485637101215530790306f0 f1 f2 f3 f4 f5 f6 f7 f8 f9原始序列链表:原始序列链表:原始序列链表:原始序列链表:r0(从最低位(从最低位(从最低位(从最低位 i i = 3= 3开始排序,开始排序,开始排序,开始排序,f 是队首指针,是队首指针,是队首指针,是队首指针,e 为队尾指针)为队尾指针)为队尾指针)为队尾指针)第一趟收集第一趟收集第一趟收集第一趟收集(让队尾指针(让队尾指针(让队尾指针(让队尾指针ei 链接到下一非空队首指针链接到下一非空队首指针链接到下一非空队首指针链接到下一非空队首指针fi+1 即可)即可)即可)即可)530790921101614485215306637738r0533第一趟收集的结果:第一趟收集的结果:第一趟收集的结果:第一趟收集的结果:e0 e1 e2 e3 e4 e5 e6 e7 e8 e9614738921485637101215530790306f0 f1 f2 f3 f4 f5 f6 f7 f8 f9第二趟分配第二趟分配第二趟分配第二趟分配(按次低位(按次低位(按次低位(按次低位 i i = 2 = 2 )530790921101614485215306637738第二趟收集第二趟收集第二趟收集第二趟收集(让队尾指针(让队尾指针(让队尾指针(让队尾指针ei 链接到下一非空队首指针链接到下一非空队首指针链接到下一非空队首指针链接到下一非空队首指针fi+1 )530790921101614485215306637738r0r0534第二趟收集的结果:第二趟收集的结果:第二趟收集的结果:第二趟收集的结果:530790921101614485215306637738e0 e1 e2 e3 e4 e5 e6 e7 e8 e9614738921485637101215530790306f0 f1 f2 f3 f4 f5 f6 f7 f8 f9第三趟分配第三趟分配第三趟分配第三趟分配(按最高位(按最高位(按最高位(按最高位 i i = 1 = 1 )第三趟收集第三趟收集第三趟收集第三趟收集(让队尾指针(让队尾指针(让队尾指针(让队尾指针ei 链接到下一非空队首指针链接到下一非空队首指针链接到下一非空队首指针链接到下一非空队首指针fi+1 )530790921101614485215306637738r0r0排序结束!排序结束!排序结束!排序结束!5351. 排序前后的排序前后的n个记录都用顺序表个记录都用顺序表r 存储,但建议增开存储,但建议增开n 个指个指针分量(转为静态链表形式);这样在记录重排时不必移针分量(转为静态链表形式);这样在记录重排时不必移动数据,只需修改各记录的链接指针即可。动数据,只需修改各记录的链接指针即可。2.在在radix个队列中,个队列中,每个队列都要设置两每个队列都要设置两 个指针:个指针: int f radix指示队头指示队头( f j 初始为空);初始为空); int e radix 指向队尾指向队尾(e j 不必单独初始化不必单独初始化);分配到同一队列的关键码要用链接指针链接起来。分配到同一队列的关键码要用链接指针链接起来。 (注:以上一共增开了(注:以上一共增开了n+2 radix个附加指针分量)个附加指针分量)3. 待排序记录存入待排序记录存入r 中,中, r0作为头结点;每个记录都包含作为头结点;每个记录都包含key分量、分量、othernifo分量和指针域分量和指针域intint next分量。分量。 另外,为能单独表示单关键码另外,为能单独表示单关键码key的各位,将的各位,将key改用向量改用向量key0d-1来表示之,这样:来表示之,这样: 第第p个记录的关键码的第个记录的关键码的第i位可表示为:位可表示为:rp.keyi; 第第p个记录的指针分量可表示为:个记录的指针分量可表示为: rp.next如何编程实现?如何编程实现?如何编程实现?如何编程实现?先作若干说明:先作若干说明:先作若干说明:先作若干说明:536基数排序算法分析基数排序算法分析 假假假假设设设设有有有有n n 个个个个记记记记录录录录, , 每每每每个个个个记记记记录录录录的的的的关关关关键键键键字字字字有有有有d d 位位位位,每每每每个个个个关关关关键键键键字字字字的的的的取取取取值值值值有有有有radixradix个个个个, , 则则则则需需需需要要要要radixradix个个个个队队队队列列列列, , 进进进进行行行行d d 趟趟趟趟“ “分分分分配配配配” ”与与与与“ “收集收集收集收集” ”。因此时间复杂度:。因此时间复杂度:。因此时间复杂度:。因此时间复杂度:O ( d ( n+radix ) )O ( d ( n+radix ) )。 基数排序需要增加基数排序需要增加基数排序需要增加基数排序需要增加n+2radixn+2radix个附加链接指针,空间效率低个附加链接指针,空间效率低个附加链接指针,空间效率低个附加链接指针,空间效率低 空间复杂度:空间复杂度:空间复杂度:空间复杂度:OO(radixradix). . 稳定性:稳定。稳定性:稳定。稳定性:稳定。稳定性:稳定。( (一直前后有序一直前后有序一直前后有序一直前后有序) )。用用用用途途途途:若若若若基基基基数数数数radixradix相相相相同同同同,对对对对于于于于记记记记录录录录个个个个数数数数较较较较多多多多而而而而关关关关键键键键码码码码位位位位数数数数较少的情况,使用链式基数排序较好。较少的情况,使用链式基数排序较好。较少的情况,使用链式基数排序较好。较少的情况,使用链式基数排序较好。特点:特点:特点:特点:不用比较和移动,改用分配和收集,时间效率高!不用比较和移动,改用分配和收集,时间效率高!不用比较和移动,改用分配和收集,时间效率高!不用比较和移动,改用分配和收集,时间效率高!537各种内部排序方法的比较各种内部排序方法的比较 (教材教材P289)排序方法排序方法排序方法排序方法 最好情况最好情况最好情况最好情况 平均时间平均时间平均时间平均时间 最坏情况最坏情况最坏情况最坏情况辅助存储辅助存储辅助存储辅助存储稳定性稳定性稳定性稳定性 简单排序简单排序 O(n)O(n2) O(n2) O(1) 稳定稳定 快速排序快速排序O(nlgn )O(nlgn) O(n2) O(lgn) 不稳定不稳定 堆排序堆排序 O(nlgn )O(nlgn ) O(nlgn) O(1)不稳定不稳定 归并排序归并排序 O(nlgn ) O(nlgn ) O(nlgn) O(n)稳定稳定基数排序基数排序O(d(n+rd)O(d(n+rd)O(d(n+rd)O(rd)稳定稳定 简单选择简单选择 O(n2) O(n2) O(n2) O(1) 不稳定不稳定 直接插入直接插入 O(n) O(n2) O(n2) O(1)稳定稳定 折半插入折半插入O(nlgn )O(n2)O(n2)O(1)稳定稳定冒泡冒泡 O(n) O(n2) O(n2) O(1)稳定稳定 538讨论:讨论:若初始记录基本无序,则选用哪些排序方法若初始记录基本无序,则选用哪些排序方法比较适合?若初始记录基本无序,则最好选用哪些排比较适合?若初始记录基本无序,则最好选用哪些排序方法?序方法?答:答:对基本有序的情况,可选用直接插入、堆排序、冒泡排对基本有序的情况,可选用直接插入、堆排序、冒泡排序、归并排序等方法;序、归并排序等方法; 在基本无序的情况下,最好选用快速排序、希尔排序。在基本无序的情况下,最好选用快速排序、希尔排序。想一想:想一想:能选用折半排序么?能选用折半排序么?539
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号