还剩58页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
数据结构与算法分析版C++欢迎来到数据结构与算法分析课程本课程将系统讲解数据结构的基本概念、实现方法以及相关算法分析,帮助您构建坚实的计算机科学基础通过学习版本的数据结构与算法,您将掌握编程中最关键的技能,提高解决C++复杂问题的能力,为今后的软件开发打下坚实基础本课程注重理论与实践相结合,将通过大量代码示例和编程练习帮助您加深理解让我们一起开始这段学习之旅,探索数据结构与算法的精彩世界!课程概述课程目标掌握核心数据结构概念及实现方法理解并应用算法分析技术评估程序效率使用语言实现各种数据结构和算法C++学习成果能够设计适合特定问题的数据结构能分析算法的时间和空间复杂度运用算法思维解决实际编程问题课程大纲基础概念与回顾C++线性结构数组、链表、栈、队列树结构与图结构查找与排序算法基本概念数据结构定义算法定义数据抽象数据结构是计算机中存储、组织数据算法是解决特定问题的一系列明确指数据抽象是分离数据的逻辑属性与实的方式数据结构是相互之间存在一令的有限集合一个好的算法应当具现细节的过程通过抽象,可以创建种或多种特定关系的数据元素的集合备确定性、有限性、可行性和输入适合问题的数据类型,而不必关心底/良好的数据结构选择可以提高算法的输出等特点算法是程序的灵魂,决层实现中的类是实现数据抽象C++效率定了程序的效率的主要机制算法分析基础时间复杂度空间复杂度大表示法O时间复杂度衡量算法执行所需的时间资源,空间复杂度评估算法在执行过程中所需的大表示法描述算法复杂度的渐近上界,关O通常用基本操作执行次数来表示它描述额外存储空间,仅包括除输入数据外所需注算法在输入规模趋向无穷大时的增长率了算法运行时间与输入规模之间的关系,的辅助空间它同样根据输入规模的函数常见的复杂度级别包括、、O1Olog n帮助我们预测算法在大规模数据上的性能来描述、、、等On On log n On²O2ⁿ表现在资源受限的环境中,空间复杂度与时间在比较算法时,我们主要考虑最高阶项,我们通常关注最坏情况下的时间复杂度,复杂度一样重要,需要合理平衡并忽略常数系数和低阶项以保证算法在任何输入下的性能下限基础回顾C++类和对象模板简介STL作为面向对象编程语言,通过类来实模板是中实现泛型编程的机制,允许标准模板库是的核心库之一,C++C++STL C++现数据抽象和封装类定义了数据成员和创建与类型无关的函数和类通过模板,提供了一系列容器、算法和迭代器STL成员函数,实现了数据和操作的结合对可以编写一次代码,适用于多种数据类型,中的容器如、、等都是泛vector listmap象是类的实例,具有类定义的属性和行为提高代码的复用性型数据结构的实现模板分为函数模板和类模板在数据结构学习对理解数据结构与算法有很大帮STL类的设计原则包括高内聚、低耦合、单实现中,模板技术尤为重要,使我们能够助,掌握的使用也能在实际编程中提STL一职责,以及面向接口编程良好的类设创建通用的容器类高效率在本课程中,我们将实现类似计能够提高代码的可维护性和可扩展性的数据结构STL线性表概述定义和特征基本操作线性表是具有相同数据类型的个数据元n包括初始化、插入、删除、查找、更新等素的有限序列,数据元素之间存在一对一操作,为线性表结构的功能基础的线性关系应用场景存储结构广泛应用于数据管理、记录存储、信息处主要有两种实现方式顺序存储结构(顺理等多种实际应用中序表)和链式存储结构(链表)线性表是最基础也是最常用的数据结构之一,它为许多复杂数据结构提供了基础理解线性表的概念和实现方法,对学习其他数据结构至关重要在接下来的课程中,我们将详细讨论线性表的不同实现方式及其操作算法顺序表定义和特点顺序表是用一段连续的存储单元依次存储线性表中的数据元素实现方法通常使用数组实现,需管理容量和实际元素数量基本操作包括插入、删除、查找、修改等,均需考虑边界条件顺序表的最大优势在于支持随机访问,可以在时间内访问任意位置的元素这使得顺序表在需要频繁按索引访问元素的场景中表现出色O1然而,顺序表在插入和删除操作时,可能需要移动大量元素,平均时间复杂度为此外,顺序表需要预先分配连续内存空间,当元素数量超过On初始容量时,需要进行扩容操作,这可能导致性能波动在实际应用中,的容器就是顺序表的一种实现,它提供了动态扩容的能力,使用起来更加灵活C++vector链表概述定义和特点链表类型链表是一种线性数据结构,其单链表每个节点只有一个指中的元素在逻辑上相邻,但物向下一节点的指针双链表理存储位置可以不连续每个每个节点有两个指针,分别指元素(称为节点)包含数据和向前一个和后一个节点循环指向下一个节点的指针链表链表最后一个节点指向第一的动态性使其在内存使用上更个节点,形成一个环这些不加灵活,不需要预先确定大小同类型适用于不同的应用场景基本操作链表的基本操作包括插入、删除、查找和遍历与顺序表相比,链表的插入和删除操作效率高(仅需调整指针,时间复杂度),但查找效O1率低(需要从头开始遍历,时间复杂度)On单链表实现节点结构单链表的节点包含两部分数据域和指针域数据域存储实际数据,指针域存储指向下一个节点的指针在中,通常使用结构体或类来定义节点C++template typenameTstruct Node{T data;Node*next;Nodeconst Td:datad,nextnullptr{}};插入操作在单链表中插入节点需要修改指针,使新节点的指向原位置的节点,并让前一节点的指向新节next next点头部插入最简单,只需两步新节点的指向原头节点,然后更新头指针next中间位置插入需要先遍历到目标位置的前一节点,再调整指针完成插入时间复杂度为,但实际插On入操作只需O1删除操作删除节点同样需要调整指针,让待删除节点的前一节点直接指向待删除节点的下一节点,然后释放待删除节点的内存需特别注意边界情况,如删除头节点或尾节点的特殊处理在中,还需要注意内存管理,防止内存泄C++漏双链表实现节点结构双链表的节点比单链表多一个指向前驱节点的指针典型的实现如下C++template typenameTstruct DNode{T data;DNode*prev;DNode*next;DNodeconst Td:datad,prevnullptr,nextnullptr{}};插入操作双链表的插入需要同时调整前后两个节点的指针以在节点后插入新节点为例p x
1.x-next=p-next;
2.x-prev=p;
3.if p-next p-next-prev=x;
4.p-next=x;删除操作删除双链表的节点需要改变其前后节点的指针p
1.if p-prev p-prev-next=p-next;
2.if p-next p-next-prev=p-prev;
3.delete p;双链表虽然占用更多内存空间,但它支持双向遍历,可以快速访问前后节点,这在某些应用中非常有价值特别是在需要频繁删除当前节点或访问前驱节点的场景下,双链表比单链表更高效循环链表360°O12完整循环尾部访问实现形式尾节点指向头节点,形成环状结构通过头指针可直接访问尾节点单循环链表和双循环链表循环链表的主要特点是没有明确的终点,这使得从链表中的任意一点出发都能遍历整个链表在单循环链表中,最后一个节点的指针指向头节点;在双循next环链表中,头节点的指针指向尾节点,尾节点的指针指向头节点prev next循环链表特别适合处理周期性数据或需要循环处理的场景例如,操作系统中的进程调度、轮询算法等都可以使用循环链表实现实现循环链表时,需要特别注意循环终止条件,以避免无限循环与普通链表相比,循环链表的操作实现略有不同,尤其是在插入和删除头尾节点时需要特别处理理解这些细微差别对于正确实现循环链表至关重要栈概述后进先出LIFO栈的基本特征,决定了元素的访问顺序基本操作压栈、出栈、获取栈顶元素push poptop应用场景函数调用、表达式求值、语法分析、深度优先搜索等栈是一种非常重要的线性数据结构,它限制了数据的插入和删除只能在一个位置进行,这个位置称为栈顶栈的这种特性使它特别适合处理需要回溯的问题,如递归过程的实现、程序运行时的函数调用链管理等在计算机系统中,栈被广泛用于管理函数调用过程中的局部变量和返回地址编译器使用栈来支持函数调用机制,操作系统用栈来处理中断和异常此外,在算法设计中,许多复杂算法如深度优先搜索、回溯算法等都直接或间接地使用了栈的概念栈的实现通常有两种方式基于数组的顺序栈和基于链表的链式栈两种实现各有优缺点,选择哪种实现取决于具体应用场景和需求栈的顺序存储实现栈的链式存储实现基本结构基本操作实现链式栈使用链表实现,通常选择单链表,并将链表的头部作为栈顶每压栈操作创建新节点,将其指向当前的栈顶节点,然后push next个节点包含数据域和指针域,指针指向下一个节点链式栈不需要预先更新栈顶指针这相当于在单链表的头部插入一个新节点分配空间,可以根据需要动态分配内存出栈操作将栈顶节点保存到临时变量,更新栈顶指针指向下一pop在中,链式栈的节点结构与单链表相同个节点,然后删除原栈顶节点并释放内存这相当于删除单链表的头节C++点template typenameT获取栈顶元素直接返回栈顶节点的数据值struct Node{topT data;判空操作检查栈顶指针是否为empty nullptrNode*next;Nodeconst Td:datad,nextnullptr{}};链式栈的主要优势在于不受固定空间的限制,能够根据需要动态增长,避免了栈溢出的问题此外,链式栈的插入和删除操作都只涉及指针的调整,不需要移动元素,操作简单高效然而,链式栈相比顺序栈需要额外的空间来存储指针,空间利用率较低,且由于节点不连续存储,缓存命中率可能较低栈的应用表达式求值中缀表达式转后缀表达式使用栈处理运算符,按照优先级规则进行转换遇到操作数直接输出•遇到左括号入栈•遇到右括号,弹出栈内运算符直到遇到左括号•遇到运算符,弹出栈内优先级不低于当前运算符的所有运算符,然后将当前运算符入栈•后缀表达式求值算法使用栈存储操作数,从左到右扫描后缀表达式遇到操作数,压入栈中•遇到运算符,弹出所需数量的操作数,进行计算,将结果压回栈中•表达式扫描完成后,栈顶即为最终结果•代码实现要点实现时需要注意以下几点运算符优先级的正确处理•括号匹配的检查•多位数字和浮点数的解析•错误处理和异常情况•表达式求值是栈应用的经典案例,它展示了栈如何有效地处理具有嵌套结构的问题通过两步法(先转换为后缀表达式,再求值)可以简化算法实现,提高代码可读性和可维护性队列概述基本操作先进先出FIFO入队、出队、获取队enqueue dequeue队列的基本特性,决定了元素访问顺序首和队尾元素实现方式4应用场景数组实现循环队列和链表实现链式队列任务调度、消息缓冲、广度优先搜索等队列是一种限制插入在一端(队尾)、删除在另一端(队首)的线性数据结构这种先进先出的特性使队列特别适合处理需要按照到达顺序处理的任务,如操作系统中的进程调度、网络数据包的处理、打印任务的排队等在算法设计中,队列是实现广度优先搜索()的核心数据结构,用于保存待访问的节点此外,在模拟系统中,队列常用于模拟现实世界中的BFS排队现象,如银行服务、交通流量等顺序队列实现空队列状态入队操作出队操作,表示队列为空在初始状新元素加入队尾,指针按顺时针方向移删除队首元素,指针顺时针移动出front==rear rear front态或所有元素出队后出现需要注意,满队动当到达数组末尾时,会绕回到数组队操作的计算公式rearfront=front+1列状态下也可能出现,因此开头,形成环形结构入队操作的计算公式在实际应用中,我们通常不front==rear%maxSize通常需要额外的标志或计数器来区分这两种会真正删除元素,而是移动指针,原rear=rear+1%maxSize front情况数据会在后续入队操作中被覆盖循环队列使用固定大小的数组和两个指针(和)来实现队列通过将数组视为一个环,可以高效地利用空间循环队列解决了front rear普通顺序队列的假溢出问题,即使队列未满但无法添加新元素的情况链式队列实现基本结构链式队列使用链表实现,通常需要两个指针指向队首节点,指向front rear队尾节点每个节点包含数据和指向下一节点的指针这种结构特别适合需要动态调整大小的队列入队操作创建新节点存储元素,将其加入队尾,并更新指针与顺序队列不同,链rear式队列不存在容量限制,可以一直添加新元素入队操作的时间复杂度为O1出队操作删除队首节点,更新指针,并释放内存需要特别处理队列变空的情况,front确保和的一致性出队操作的时间复杂度同样为front rearO1队空判断当为或时,队列为空链式队列不需front nullptrfront==rear==nullptr要像循环队列那样处理队满情况,因为它可以无限扩展优先级队列定义和特点实现方法优先级队列是一种特殊的队列,其中每优先级队列有多种实现方式,最常见的个元素都有一个优先级值,元素的出队是基于堆的实现最大堆用于取出最大顺序由优先级决定,而不是入队顺序优先级元素,最小堆用于取出最小优先优先级队列支持两种主要操作插入元级元素堆实现的优先级队列插入和删素和取出最高优先级的元素除操作的时间复杂度均为Olog n与普通队列的先进先出原则不同,优先其他实现方式包括有序数组、无序数组级队列遵循最高优先级先出的原则,这和平衡二叉搜索树等,各有优缺点选使其在需要按重要性处理任务的场景中择哪种实现取决于具体应用需求和操作非常有用频率应用场景优先级队列在计算机科学中有广泛应用,例如操作系统的进程调度、网络流量控制()、事件驱动模拟系统、贪心算法(如最短路径、最小生成树)等QoS DijkstraPrim在标准库中,是优先级队列的实现,它基于堆结构,提供了高C++std::priority_queue效的优先级排序功能树结构概述层次结构1反映元素之间的一对多关系概念基础2节点、边、根、父子关系、兄弟关系树的类型二叉树、平衡树、树、红黑树等B树是一种非线性数据结构,由节点和连接节点的边组成每个节点可以有零个或多个子节点,但只能有一个父节点(根节点除外)树结构广泛应用于表示具有层次关系的数据,如文件系统、组织架构、文档等XML/HTML树的重要特性包括没有环路(即不存在从任意节点出发可以回到自身的路径);除根节点外,每个节点都有且仅有一个父节点;任意两个节点之间有且仅有一条路径连接这些特性使树成为组织和搜索数据的理想结构在计算机科学中,树被用于开发高效的搜索算法、数据库索引、编译器语法分析等理解树的基本概念和操作是学习更复杂数据结构和算法的基础二叉树概念定义和特点特殊二叉树二叉树的性质二叉树是一种特殊的树形结构,其中每个满二叉树所有内部节点都有两个子节点,二叉树具有许多重要性质,包括节点最多有两个子节点,通常称为左子节所有叶子节点都在同一层一个高度为h在第层上最多有个节点•i2^i-1点和右子节点二叉树是最常用的树形结的满二叉树有个节点2^h-1高度为的二叉树最多有个节构,许多高效的数据处理算法都基于二叉•h2^h-1完全二叉树除了最后一层外,其他层的点树实现节点都是满的,最后一层的节点从左到右对于任何非空二叉树,叶子节点数等•二叉树的每个节点都有一个值和两个指向填充完全二叉树的特点使其非常适合用于度为的节点数加21子树的指针叶子节点是没有子节点的节数组实现具有个节点的完全二叉树高度为•n点,而内部节点是至少有一个子节点的节平衡二叉树任意节点的左右子树高度差₂log n+1点⌊⌋不超过平衡性保证了树的搜索效率不1会退化二叉树的存储结构顺序存储链式存储节点设计考虑二叉树的顺序存储使用一维数组实现,主要链式存储是二叉树最常用的实现方式,每个根据应用需求,节点结构可能需要包含额外适用于完全二叉树节点之间的关系通过索节点包含数据和指向左右子节点的指针信息C++引计算确定实现如下父节点指针便于从子节点向上访问•根节点存储在索引处•0template typenameT平衡因子用于树等平衡二叉树•AVL对于索引为的节点,其左子节点索引为struct TreeNode{•i颜色标记用于红黑树•T data;2i+1计数字段用于统计子树节点数•TreeNode*left;对于索引为的节点,其右子节点索引为•i高度信息记录节点所在的高度•TreeNode*right;2i+2TreeNodeconst Td:datad,合理的节点设计可以简化算法实现,提高操对于索引为的节点,其父节点索引为•ileftnullptr,rightnullptr{}作效率i-1/2⌊⌋};顺序存储的优点是访问效率高,缺点是对于链式存储的优点是灵活性高,能够有效表示非完全二叉树会造成空间浪费任意形状的二叉树;缺点是需要额外的指针空间,且节点分散存储影响访问局部性二叉树的遍历前序遍历后序遍历访问顺序根节点左子树右子树访问顺序左子树右子树根节点→→→→适用于创建树的副本、输出目录结构等适用于计算目录大小、后缀表达式求值、释放节点等中序遍历访问顺序左子树根节点右子树→→适用于二叉搜索树的有序遍历、表达式树求值等二叉树的遍历是指按照某种顺序访问树中的所有节点,每个节点恰好被访问一次三种基本的遍历方式(前序、中序、后序)都是深度优先的遍历策略,它们的区别在于访问根节点的时机不同遍历算法可以用递归或迭代方式实现递归实现简洁直观,但在处理大型树时可能导致栈溢出;迭代实现通常使用显式栈,避免了递归调用的开销,但代码相对复杂通过不同的遍历方式,可以解决不同类型的问题例如,二叉搜索树的中序遍历会产生有序序列;表达式树的后序遍历可以直接求解表达式值;前序遍历常用于序列化树结构二叉树遍历的递归实现前序遍历递归算法中序遍历递归算法前序遍历的递归实现遵循根左右的访问顺序中序遍历的递归实现遵循左根右的访问顺序----void preorderTraversalTreeNode*root{void inorderTraversalTreeNode*root{if root==nullptr return;if root==nullptr return;访问根节点递归遍历左visitroot;//inorderTraversalroot-left;//递归遍历左子树preorderTraversalroot-left;//子树访问根节点visitroot;//递归遍历递归遍历右preorderTraversalroot-right;//inorderTraversalroot-right;//右子树子树}}后序遍历递归算法后序遍历的递归实现遵循左右根的访问顺序--void postorderTraversalTreeNode*root{if root==nullptr return;递归遍历左子树postorderTraversalroot-left;//递归遍历右子树postorderTraversalroot-right;//访问根节点visitroot;//}递归实现的主要优点是代码简洁、易于理解,直接映射了遍历的定义然而,递归实现也有一些局限性,包括函数调用开销和栈空间限制在处理深度较大的树时,可能导致栈溢出二叉树遍历的非递归实现利用栈实现前序遍历利用栈实现中序遍历利用栈实现后序遍历非递归前序遍历的实现思路是使用栈模拟递归过程非递归中序遍历需要跟踪所有左子节点非递归后序遍历较为复杂,需要跟踪已访问的节点void preorderIterativeTreeNode*root{void inorderIterativeTreeNode*root{void postorderIterativeTreeNode*root{if!root return;stackTreeNode*s;if!root return;stackTreeNode*s;TreeNode*curr=root;stackTreeNode*s;s.pushroot;while curr||!s.empty{TreeNode*curr=root;沿左子树方向遍历直到叶子节点while!s.empty{//TreeNode*lastVisited=nullptr;TreeNode*node=s.top;s.pop;while curr{while curr||!s.empty{访问节点visitnode;//s.pushcurr;if curr{注意先右后左入栈,使左子节点先被访问//curr=curr-left;s.pushcurr;if node-right s.pushnode-right;}curr=curr-left;if node-left s.pushnode-left;curr=s.top;s.pop;}else{访问节点}visitcurr;//TreeNode*peek=s.top;转向右子树}curr=curr-right;//if peek-rightpeek-right!=lastVisited{}curr=peek-right;}}else{visitpeek;lastVisited=peek;s.pop;}}}}二叉树的层次遍历算法思路自顶向下,逐层访问所有节点队列辅助实现使用队列存储待访问的节点,确保按层次顺序处理代码演示3标准的广度优先搜索应用BFS层次遍历(也称为广度优先遍历或)是一种自顶向下、逐层访问二叉树节点的方法与前序、中序、后序遍历的深度优先特性不同,层次遍历按照节点在树中的层次从上到下、从左到右的顺序进行访问BFS实现层次遍历通常使用队列作为辅助数据结构基本算法如下void levelOrderTraversalTreeNode*root{if!root return;queueTreeNode*q;q.pushroot;while!q.empty{TreeNode*node=q.front;q.pop;访问当前节点visitnode;//左子节点入队if node-left q.pushnode-left;//右子节点入队if node-right q.pushnode-right;//}}层次遍历在许多应用中非常有用,如逐层打印二叉树、计算二叉树的宽度、查找最近公共祖先、序列化和反序列化二叉树等通过对基本的层次遍历算法进行扩展,还可以实现许多变种算法,如锯齿形之字形遍历、自底向上的层次遍历等二叉搜索树定义和特点查找操作左子树上所有节点的值均小于根节点值,右从根节点开始,小于节点值向左,大于节点子树上所有节点的值均大于根节点值值向右,直到找到或达到叶子节点插入操作时间复杂度先查找,若目标值不存在,则在查找路径的平均情况下,最坏情况下Olog n On最后位置插入新节点二叉搜索树()是一种特殊的二叉树,其结构保证了对数据的高效查找、插入和删除的核心特性是有序性对于树中的任意节点,其左BST BST子树中所有节点的值都小于该节点的值,右子树中所有节点的值都大于该节点的值这种有序性使得二叉搜索树能够支持高效的查找操作在平衡的情况下,查找、插入和删除操作的时间复杂度均为,相比线性查找的Olog n On复杂度有显著提升然而,在最坏情况下(如插入已排序数据时),可能退化为链表,导致操作复杂度为BST On二叉搜索树的删除操作删除叶节点最简单的情况,直接将其父节点对应的指针设为nullptr例如,删除叶子节点将其父节点的右指针设为,然后释放节点1510nullptr删除单子树节点的内存15将要删除节点的子节点直接连接到其父节点上例如,删除只有左子树的节点将其左子节点直接连接到其父节点的左201530删除双子树节点3指针上,然后释放节点的内存20找到该节点的中序后继(右子树中的最小节点)或中序前驱(左子树中的最大节点),用后继前驱的值替换该节点的值,然后删除后继前驱节点//例如,删除有两个子节点的找到其中序后继,将的值替换为,然30353035后删除节点(此时是叶节点或单子树节点,可用前两种情况处理)3535删除是二叉搜索树中最复杂的基本操作,因为需要在删除节点后保持树的二叉搜索特性根据待删除节点子节点的数量,可分为三种情况处理特别是在删除具有两个子节点的情况下,使用中序后继或前驱替换的策略确保了替换后的树仍然满足二叉搜索树的定义平衡二叉树(树)AVL平衡因子平衡因子定义为节点左子树高度减右子树高度的差值在树中,每个节点的平衡因子必须AVL为、或,即左右子树高度差的绝对值不超过-1011平衡因子的计算和维护是树操作的核心,每次插入或删除后都需要更新受影响节点的平衡AVL因子,并检查是否需要进行旋转操作来恢复平衡旋转操作当插入或删除导致不平衡时,树通过旋转操作恢复平衡,主要有四种情况AVL左左情况右旋•LL右右情况左旋•RR左右情况先左旋后右旋•LR右左情况先右旋后左旋•RL旋转操作通过重新排列树的结构来减小高度差,同时保持二叉搜索树的特性插入和删除树的插入和删除操作首先按照普通二叉搜索树的方式执行,然后从插入删除点沿着路径AVL/向上回溯,更新平衡因子并在必要时进行旋转这种自平衡机制确保了树高度始终保持在,从而保证了查找、插入和删除操作的Olog n时间复杂度,避免了普通二叉搜索树可能出现的性能退化Olog n红黑树概述定义和性质插入操作红黑树是一种自平衡的二叉搜索树,每个节点都红黑树的插入类似于普通,但在插入后需要BST有一个额外的颜色属性(红色或黑色),并通过进行颜色调整和可能的旋转以维持红黑性质确保一组性质来保持平衡•每个节点要么是红色,要么是黑色•新插入的节点初始为红色•根节点是黑色•如果父节点为黑色,不需调整•所有叶子节点(NIL节点)是黑色•如果父节点为红色,根据叔叔节点的颜色,执行颜色调整或旋转操作•如果一个节点是红色,则其两个子节点都是黑色旋转包括左旋、右旋或两者组合••从任一节点到其每个叶子的所有路径都包含相同数量的黑色节点删除操作删除更为复杂,需要考虑被删节点颜色和其替代节点的情况如果删除红色节点,直接删除不影响性质•如果删除黑色节点,需执行复杂的平衡恢复操作•处理双黑问题时,考虑兄弟节点及其子节点的颜色•通过颜色调整和旋转,确保所有路径上黑节点数量相等•哈夫曼树带权路径长度构造算法哈夫曼编码哈夫曼树(最优二叉树)是一种特殊的二哈夫曼树的标准构造算法步骤如下哈夫曼编码是哈夫曼树最典型的应用,用叉树,它的构造基于最小化带权路径长度于数据压缩基本原理是•将所有节点(初始时为叶子节点)按()的原则定义为所有叶子WPL WPL权值升序排序频率高的字符用短码表示,频率低的•节点的权值与其路径长度的乘积之和用长码表示•选择权值最小的两个节点,创建一个新的父节点,权值为两子节点权值之从根到叶的路径确定编码,通常左分•哈夫曼树的优势在于能够使带权路径长度和支为,右分支为01最小,这在数据压缩、最优编码等应用中•删除这两个节点,将新父节点加入节生成的编码是前缀码,即没有任何码•非常重要树中出现频率高的元素被分配点集合字是另一个码字的前缀较短的路径,频率低的元素分配较长的路•重复步骤和,直到只剩一个节点,径23哈夫曼编码被广泛应用于文本、图像和视即根节点频压缩,如、等格式的压缩算JPEG MP3法都部分基于哈夫曼编码原理实现时通常使用最小堆(优先级队列)来高效选择最小权值的节点图的基本概念图的定义有向图和无向图图的表示方法图是一种非线性数据结构,由顶点(节无向图中的边没有方向,表示双向连接,常见的图表示方式有邻接矩阵(使用点)集合和连接顶点的边集合组成数如社交网络中的朋友关系有向图中的二维数组存储顶点间连接关系)、邻接学上表示为,其中是顶点边有明确方向,表示单向连接,如网页表(每个顶点维护一个链表,存储与其G=V,E V集合,是边集合图结构用于表示具间的超链接关系有向图的边通常用箭相连的顶点)、邻接集(使用哈希表存E有多对多关系的数据,能够描述现实头表示,指明从一个顶点到另一个顶点储连接关系)、边集数组(直接存储所世界中的许多复杂关系的方向有边)等不同表示方法适合不同的图特性和操作需求图的邻接矩阵表示图的邻接表表示无向图的邻接表有向图的邻接表带权图的邻接表在无向图中,每条边需要在两个顶点的邻接表中各添有向图中,只在边的起始顶点的邻接表中添加终止顶对于带权图,邻接表中不仅需要存储相邻顶点的编号,加一次例如,对于边,需要在顶点的表中添点例如,对于有向边,只在顶点的表中添加还需要存储对应边的权值这通常通过在链表节点中1,211→21加,同时在顶点的表中添加这种冗余确保了从如果需要快速查找指向某顶点的所有顶点,可以额添加一个权值字段实现,或者使用等结构2212pair/tuple任何顶点都能找到与其相连的所有顶点外维护一个逆邻接表同时存储顶点和权值邻接表是一种更节省空间的图表示方法,特别适合稀疏图(边数远小于顶点数的平方)在实现中,通常使用或等结构实现邻接表,每个顶点对应一C++vector vector个链表,存储与其直接相连的所有顶点邻接表的主要优点是空间效率高(空间复杂度为)以及能够快速遍历与特定顶点相连的所有顶点(时间复杂度为度数)然而,判断两个顶点之间是否存在边OV+E O需要度数时间,相比邻接矩阵的效率较低OO1图的深度优先搜索()DFS算法思想深度优先搜索是一种图遍历算法,其基本思想是尽可能深地探索图的分支,直到无法继续前进时回溯DFS的探索过程类似于走迷宫沿着一条路径一直走到尽头,然后回溯到上一个有未探索路径的节点,继续探索递归实现2DFS最自然的实现方式是递归,代码简洁直观void dfsGraphG,int v,vector visited{visited[v]=true;visitv;//处理顶点vfor intw:G.adjacentv{if!visited[w]{dfsG,w,visited;}}}非递归实现使用栈可以实现非递归版本的DFS voiddfsIterativeGraph G,int start{vector visitedG.V,false;stack s;s.pushstart;visited[start]=true;while!s.empty{int v=s.top;s.pop;visitv;for intw:G.adjacentv{if!visited[w]{visited[w]=true;s.pushw;}}}}图的广度优先搜索()BFS算法思想广度优先搜索是一种分层遍历的图搜索算法,它先访问起始顶点的所有邻接点,然后再访问这些邻接点的邻接点,层层推进,直到遍历完整个图适合寻找BFS最短路径和最小生成树等应用的特点是按距离逐渐向外扩展,保证了从起点到任意可达顶点的路径是最短的(以边数计)这一特性使成为解决单源最短路径问题(无权图或等权BFSBFS图)的理想算法队列辅助实现通常使用队列来实现,确保按正确的顺序访问顶点BFSvoid bfsGraphG,int start{vector visitedG.V,false;queue q;visited[start]=true;q.pushstart;while!q.empty{int v=q.front;q.pop;处理顶点visitv;//vfor intw:G.adjacentv{if!visited[w]{visited[w]=true;q.pushw;}}}}应用与扩展的应用包括但不限于BFS寻找无权图中的最短路径•网络爬虫的实现•查找邻近点(如社交网络中的好友推荐)•解决迷宫和拼图等问题•可以扩展为记录层数的分层,或用于寻找最短路径的路径重建,这些变体在实际应用中很有价值BFS BFSBFS最小生成树算法Prim算法思想算法用于查找加权无向图的最小生成树它的核心思想是从一个起始顶点开始,每次选择一条连接Prim树内顶点与树外顶点的最小权值边,将该边加入生成树,直到所有顶点都被包含在生成树中这种贪心选择策略保证了最终构建的生成树权值总和最小算法特别适合处理稠密图,即边数接近Prim顶点数平方的图实现步骤算法的典型实现步骤Prim•选择起始顶点加入树集合T•维护一个键值数组key[],存储每个树外顶点与树集合T的最小边权值•每次选择key值最小的树外顶点加入T•更新剩余树外顶点的key值•重复步骤3和4,直到所有顶点都加入T时间复杂度分析算法的时间复杂度取决于其实现方式Prim使用简单数组实现,适合密集图•OV²使用二叉堆优化,适合稀疏图•OE log V使用斐波那契堆进一步优化•OE+V logV空间复杂度为,用于存储键值、前驱顶点和访问标记OV最小生成树算法Kruskal算法思想并查集优化时间复杂度分析算法同样用于求解加权无向图的最小算法的关键在于判断添加一条边是否算法的主要时间消耗在排序上,总体Kruskal KruskalKruskal生成树,但采用了不同的策略它按照边的会在已选边中形成环这一判断可以高效地复杂度为,其中为边数由于OE logE EE权值从小到大排序所有边,然后依次选择不使用并查集(,)最多为,因此复杂度也可以表示为Disjoint SetUnion DSUOV²OE会形成环的边加入最小生成树,直到添加了数据结构实现logV条边(为顶点数)V-1V并查集维护连通分量信息,支持两种主要操在实际应用中,算法和算法各Kruskal Prim与算法相比,算法更加关注边作有优势Prim Kruskal的选择,而不是顶点的生长它同样基于贪查找元素所在的集合标识符对于稀疏图(),算法通•Findx x•E≈V Kruskal心策略,保证了最终生成树的最小权值和常更高效合并元素和所在的集合算法特别适合处理稀疏图•Unionx,y xyKruskal对于稠密图(),算法配合•E≈V²Prim通过路径压缩和按秩合并等优化,并查集的二叉堆通常表现更好操作可以近似时间复杂度,极大提高O1算法实现更简单,尤其是配合现算法的效率•KruskalKruskal代并查集实现时最短路径算法Dijkstra算法思想贪心策略解决单源非负权图的最短路径问题每次选择当前距离起点最近的未确定顶点时间复杂度松弛操作基本实现,堆优化3不断更新通过当前顶点到达其他顶点的最短距离OV²OE logV算法是解决带权图单源最短路径问题的经典算法,前提是图中不存在负权边算法从起点开始,逐步确定到达其他顶点的最短路径Dijkstra标准实现步骤如下初始化距离数组,起点距离为,其他为无穷大;维护一个未确定顶点集合;每次从未确定集合中选取值最小的顶点,将其标记为已确定;1dist[]023dist u4对的每个邻接顶点进行松弛操作如果,则更新;重复步骤,直到所有顶点都被确定u vdist[u]+weightu,vdist[v]dist[v]53-4算法可以使用优先队列(小顶堆)优化选择最小距离顶点的过程,将时间复杂度从降至,适合处理大规模稀疏图除了计算最短距离外,还可以通过记录前驱Dijkstra OV²OE logV顶点重建最短路径最短路径算法FloydOV³OV²时间复杂度空间复杂度三重循环处理所有顶点对存储距离矩阵和路径矩阵V²结果数量计算图中所有顶点对之间的最短路径算法(算法)是一种解决全源最短路径问题的动态规划算法,可以在一次执行中找出Floyd Floyd-Warshall所有顶点对之间的最短路径与算法不同,算法可以处理包含负权边的图(只要不存在负权Dijkstra Floyd环)算法的核心思想是对于图中的每一对顶点,尝试所有可能的中间顶点,看是否存在一条经过的路径使i,j k k得到的距离变短这可以用一个简洁的状态转移方程表示i jdist[i][j]=mindist[i][j],dist[i][k]+dist[k][j]实现步骤初始化距离矩阵,直接相连的顶点填入边权值,其他填无穷大;对于每个顶点作为1dist[][]2k中间点,更新所有顶点对的最短距离;最终即为从顶点到的最短路径长度通过同时维护一i,j3dist[i][j]i j个路径矩阵,还可以重建最短路径的具体走法拓扑排序有向无环图(DAG)拓扑排序只适用于有向无环图,即不包含环路的有向图常用于表示事件的先后依赖关系,DAG如任务调度、课程安排等场景如果图中存在环路,则意味着存在循环依赖,无法确定一个合理的执行顺序算法实现实现拓扑排序的主要方法有两种基于的方法对图进行深度优先搜索,在回溯时将顶点加入结果序列的前端确保在处理DFS一个顶点之前,先处理其所有后继顶点基于入度的方法(算法)维护每个顶点的入度,首先将入度为的顶点加入队列,然后Kahn0重复以下步骤从队列取出一个顶点,将其添加到结果序列,并减少其所有邻接顶点的入度,将新的入度为的顶点加入队列0应用场景拓扑排序在实际应用中有广泛用途构建系统中的依赖关系解析•任务调度问题中的优先级确定•学校课程的先修关系安排•数据处理流水线的设计•编译系统中的模块编译顺序确定•关键路径1AOE网络网络是一种带权有向无环图,用于表示工程中的活动顶点表示事件(如活动的开始或结束),边表AOE示活动,边上的权值表示活动持续时间关键路径算法用于在网络中找出从源点到汇点的最长路径AOE最早与最晚开始时间对于每个活动,定义最早开始时间指活动最早可能开始的时间,等于活动起点事件的最早发生时间ES最晚开始时间指在不延误整个工程完成时间的前提下,活动最晚必须开始的时间LS计算过程涉及正向遍历(计算每个事件的最早发生时间)和反向遍历(计算每个事件的最晚发生时间)关键活动的确定关键活动是指那些最早开始时间等于最晚开始时间的活动,即活动的时间余量(松弛时间)为这些活0动构成了关键路径,它们的延误将直接导致整个工程的延误查找关键活动的步骤•计算每个顶点的最早发生时间ve[](正向遍历)•计算每个顶点的最晚发生时间vl[](反向遍历)•对每条边i,j,计算松弛时间vl[j]-ve[i]-weighti,j•松弛时间为0的边即为关键活动查找算法概述查找的定义查找效率的衡量查找是在数据集合中找出特定元素评估查找算法主要考虑以下指标的过程它是计算机科学中最基本时间复杂度(平均、最坏和最好情也是最常用的操作之一,几乎所有况)、空间复杂度、适用的数据规的程序都需要实现某种形式的查找模和分布、稳定性(输入数据波动功能查找可以基于元素的值、属对性能的影响)、实现复杂度等性或满足特定条件等各种标准进行不同的应用场景对这些指标的侧重点不同,需要根据具体需求选择合适的算法常见查找算法主要的查找算法包括顺序查找(最简单但效率最低)、二分查找(要求有序数据)、分块查找(顺序和二分的折中)、哈希查找(通过键值直接定位,常数时间复杂度)、树表查找(如二叉搜索树、红黑树等)每种算法都有其适用场景和限制条件顺序查找算法描述时间复杂度分析改进方法顺序查找(线性查找)是最基本的查找算法,顺序查找的时间复杂度分析虽然顺序查找的基本思想简单,但可以通过一它按顺序检查数组中的每个元素,直到找到目些技巧提高其效率最坏情况,目标元素在数组末尾或•On标值或遍历完整个数组虽然效率不高,但它不存在设置哨兵在数组末尾添加目标值作为哨兵,适用于任何数据集合,不要求数据有序或满足可以简化循环中的条件判断,略微提高效率最好情况,目标元素在数组首位特定结构•O1平均情况,假设元素均匀分布,平•On基本实现非常简单有序顺序查找如果数据有序,可以在发现均需要检查个元素n/2当前元素大于目标值时立即停止,避免不必要int sequentialSearchintarr[],int n,空间复杂度为,只需要常数额外空间O1的比较int target{自组织查找根据法则,将频繁查for int i=0;in;i++{80/20找的元素移到数组前端,减少平均查找时间if arr[i]==target{找到目标,返回索引return i;//}跳跃式查找每次跳过固定步长检查元素,}减少比较次数未找到目标return-1;//}二分查找算法描述二分查找是一种高效的查找算法,适用于已排序的数据集它通过不断将搜索区间一分为二,每次比较中间元素与目标值,根据比较结果缩小搜索范围,直到找到目标或确定目标不存在二分查找的关键在于每次比较后都能排除一半的搜索空间,这使得它能够在对数时间内完成查找递归和非递归实现2非递归实现int binarySearchintarr[],int n,int target{int left=0,right=n-1;while left=right{int mid=left+right-left/2;if arr[mid]==target returnmid;if arr[mid]target left=mid+1;else right=mid-1;}return-1;}递归实现int binarySearchRecursiveintarr[],int left,int right,int target{if leftright return-1;int mid=left+right-left/2;if arr[mid]==target returnmid;if arr[mid]targetreturn binarySearchRecursivearr,mid+1,right,target;return binarySearchRecursivearr,left,mid-1,target;}时间复杂度分析二分查找的时间复杂度•最坏情况Olog n,需要缩小搜索范围直到只剩一个元素•最好情况O1,第一次比较就找到目标•平均情况Olog n空间复杂度非递归实现为O1,递归实现为Olog n(递归调用栈)需要注意的边界情况包括处理溢出的中间索引计算、确保输入数组已排序、处理重复元素、处理空数组等分块查找算法思想索引表的设计时间复杂度分析分块查找(也称为索引顺序查找)是顺序索引表是分块查找的核心,它通常包含以假设数据被分为个块,每块包含个m n/m查找和二分查找的一种折中策略它将数下信息元素(为总元素数)n据分成若干块,每块内部可以无序,但块每块的最大值(或最小值),用于确定位块的时间复杂度(如••Olog m与块之间必须有序(即前一块中的最大值定目标值所在的块果使用二分查找在索引表中查找)小于后一块中的最小值)每块的起始位置,用于在确定块后快块内顺序查找的时间复杂度••On/m查找过程分两步首先在索引表中确定目速定位到块的开始标所在的块(可以用二分查找),然后在每块的长度(如果各块长度不同)总体时间复杂度••Olog m+n/m对应块内进行顺序查找这种方法特别适合于频繁变动的大型数据集,因为它只需索引表的设计需要考虑数据分布、查询频当约等于时,时间复杂度最优,为m√n要保持块间有序,而不要求整个数据集完率和存储限制等因素理想情况下,应使这比的顺序查找效率高,但O√n On全有序各块大小相近,且使高频查询的数据集中比的二分查找效率低不过,分Olog n在前几个块中块查找在数据频繁变动的情况下维护成本较低,只需保持块间有序即可哈希表概述哈希函数将任意大小的数据映射为固定大小的值冲突解决处理不同关键字映射到同一位置的策略动态扩容当负载因子过高时重新分配更大的空间哈希表是一种基于哈希函数实现的高效查找数据结构,它提供了近乎恒定时间的插入、查找和删除操作哈希表的核心思想是通过哈希函数将关键字映射到数组索引,从而实现直接访问一个好的哈希函数应该具备以下特性计算简单高效、均匀分布(最小化冲突)、确定性(相同输入产生相同输出)常见的哈希函数包括除法哈希法、乘法哈希法、全域哈希法、密码哈希函数等解决哈希冲突的主要方法有开放寻址法(线性探测、二次探测、双重哈希)和链地址法(拉链法)开放寻址适合数据量可预测且装载因子较低的情况,而链地址法更灵活,适合动态变化的数据集哈希表的性能受装载因子(已用空间与总空间之比)影响,当装载因子过高时,需要重新哈希()到更大的rehash表中排序算法概述冒泡排序算法描述1冒泡排序是一种简单的排序算法,它重复地遍历待排序的序列,比较相邻元素,如果它们的顺序错误就交换它们每一轮遍历都会将当前最大(或最小)的元素冒泡到序列的一端冒泡排序的名称来源于较大的元素会像气泡一样逐渐浮到序列的顶端这是一种稳定的排序算法,因为相等元素的相对顺序不会改变代码实现2冒泡排序的基本实现非常直观void bubbleSortintarr[],int n{for inti=0;in-1;i++{for int j=0;jn-i-1;j++{if arr[j]arr[j+1]{交换和//arr[j]arr[j+1]swaparr[j],arr[j+1];}}}}这可以优化为提前退出的版本如果某一轮没有发生交换,说明序列已经有序,可以提前结束排序时间复杂度分析冒泡排序的时间复杂度分析最坏情况,当数组完全逆序时•On²最好情况,当数组已经有序,使用提前退出优化•On平均情况•On²空间复杂度为,只需要一个临时变量用于交换O1虽然冒泡排序在效率上不如其他高级排序算法,但它实现简单,容易理解,适合小规模数据集或作为教学工具选择排序算法描述选择排序是一种简单直观的排序算法,其基本思想是每次从未排序部分找出最小(或最大)元素,放到已排序部分的末尾这个过程不断重复,直到所有元素都被排序具体步骤首先在未排序序列中找到最小元素,将其与序列第一个元素交换;然后在剩余未排序元素中继续寻找最小元素,与第二个位置交换;以此类推,直到所有元素均排序完毕代码实现2选择排序的实现如下C++void selectionSortintarr[],int n{for inti=0;in-1;i++{找出范围内的最小值//[i,n-1]int minIdx=i;for intj=i+1;jn;j++{if arr[j]arr[minIdx]{minIdx=j;}}将最小值放到已排序序列的末尾//if minIdx!=i{swaparr[i],arr[minIdx];}}}时间复杂度分析选择排序的时间复杂度分析最坏情况•On²最好情况,即使数组已经有序,仍需要轮比较•On²n-1平均情况•On²空间复杂度为,只需要一个临时变量用于交换O1与冒泡排序相比,选择排序的主要优势在于交换次数较少(最多次),这在元素交换成本较高的情况下可能带来性能提升然而,选择排序是不稳n-1定的排序算法,因为交换操作可能改变相等元素的相对顺序插入排序时间复杂度分析代码实现插入排序的时间复杂度取决于输入数据的初始排列状态算法描述插入排序的标准实现如下最坏情况,当数组完全逆序时•On²插入排序是一种简单且高效的排序算法,其工作原理类似于人们void insertionSortintarr[],int n{最好情况,当数组已经有序时整理扑克牌的方式算法将数组分为已排序和未排序两部分,初•Onfor inti=1;in;i++{始时已排序部分只包含第一个元素平均情况•On²当前要插入的元素int key=arr[i];//插入排序的核心思想是不断将未排序部分的第一个元素插入到intj=i-1;//从已排序部分的末尾开始空间复杂度为O1,只需要常数级额外空间已排序部分的适当位置,使已排序部分保持有序这个过程重复将大于的元素向右移动//key插入排序是稳定的排序算法,相等元素的相对顺序保持不变它进行,直到所有元素都被插入到已排序部分while j=0arr[j]key{在处理小规模数据或部分有序数据时表现优异,比同为的On²arr[j+1]=arr[j];冒泡排序和选择排序更高效许多高级排序算法在处理小规模子j--;问题时会切换到插入排序}在正确位置插入//keyarr[j+1]=key;}}希尔排序算法思想增量序列的选择时间复杂度分析希尔排序是插入排序的一种改进版本,也称增量序列的选择对希尔排序的性能有重要影希尔排序的时间复杂度取决于所选择的增量为缩小增量排序它的核心思想是先将整个响常见的增量序列有序列待排序序列分割成若干个子序列分别进行直原始增量,即每最坏情况使用原始增量时为,•Shell N/2,N/4,...,1•Shell ON²接插入排序,待整个序列中的元素基本有序次将增量减半但好的增量序列可以达到甚ON^4/3时,再对全体元素进行一次直接插入排序至•Hibbard增量1,3,7,...,2ᵏ-1,提ON·log²N希尔排序通过设置不同的增量(间隔)来划供了的时间复杂度平均情况与增量序列有关,通常认为是ON^3/2•分子序列,初始增量较大,然后逐渐减小直增量ON·log²N•Sedgewick1,5,19,41,到为每次排序都使序列更加接近有序,最1,是目前已知的最好增量序列之一
109...空间复杂度为,只需要有限的额外空间后一次排序(增量为时)相当于标准插入排O11序,但此时序列已经接近有序,效率大大提理想的增量序列应该是互质的(没有公因高子),这有助于避免某些元素在多次排序中希尔排序是不稳定的排序算法,但相比其他不能被比较到的情况简单排序算法(如冒泡、选择、插入),它对于大规模数据的处理能力显著提高其最大优势在于能够对接近有序的数据进行高效排序快速排序分治思想1快速排序是一种高效的排序算法,基于分治策略其基本思想是选择一个基准元素,将数组分为两部分,使得左部分的所有元素都小于或等于基准,右部分的所有元素都大于或等于基准,然后递归地对两个子数组进行排序快速排序的关键在于分区操作,即如何选择基准并将数组划分为两部分分区操作完成后,基准元素已位于其最终位置,这一特性使得快速排序非常高效划分算法2最常用的划分方法是划分(也称单向划分)Lomutoint partitionintarr[],int low,int high{选择最后一个元素作为基准int pivot=arr[high];//小于基准的元素的最后位置inti=low-1;//for intj=low;jhigh;j++{if arr[j]=pivot{i++;swaparr[i],arr[j];}}swaparr[i+1],arr[high];返回基准的最终位置return i+1;//}时间复杂度分析快速排序的时间复杂度取决于划分的平衡性最好情况,每次划分都很平衡•On log n最坏情况,每次划分都极不平衡(如已排序数组)•On²平均情况•On log n为了避免最坏情况,通常采用随机选择基准或三数取中法快速排序是不稳定的排序算法,但在实际应用中通常是最快的通用排序算法,特别适合大规模数据的排序堆排序堆的定义和性质建堆过程堆是一种特殊的完全二叉树,分为最大堆和最小堆自底向上调整各个子树,确保每个节点满足堆的性质性能分析排序过程时间复杂度稳定在,空间复杂度为3不断将堆顶元素与末尾元素交换,并重新调整堆结构On lognO1堆排序是一种利用堆这种数据结构所设计的排序算法对于最大堆,堆中每个父节点的值都大于或等于其子节点的值;对于最小堆,每个父节点的值都小于或等于其子节点的值堆排序通常使用最大堆来进行升序排序堆排序的基本过程包括两个阶段首先将待排序序列构建成一个堆(通常是最大堆),然后将堆顶元素与堆的最后一个元素交换,缩小堆的范围并重新调整堆这一过程不断重复,直到堆的大小减为1堆排序的主要优点是时间复杂度稳定,无论数据分布如何,都能在时间内完成排序然而,由于堆是一种完全二叉树,其数据访问缺乏局部性,这导致缓存命中率相对较低,On logn在实际应用中性能可能不如快速排序堆排序是不稳定的排序算法,但它可以原地排序,不需要额外的存储空间归并排序分治思想归并排序是一种稳定的排序算法,基于分治策略其基本思想是将待排序序列不断二分为规模更小的子序列,直到子序列只有单个元素(此时认为已有序);然后不断地将有序子序列合并,直到最终合并成一个完整的有序序列归并排序的特点是将排序问题分解为若干个规模较小但类似的子问题,由于子问题相互独立,适合并行计算这种分而治之的方法使得归并排序在大数据量处理时表现出色归并过程归并是整个算法的核心,它将两个已排序的子序列合并成一个更大的有序序列基本步骤是创建一个临时数组,用于存放合并结果
1.使用两个指针分别指向两个子序列的开始
2.比较两个指针指向的元素,将较小的元素放入临时数组,并移动对应指针
3.重复步骤,直到其中一个子序列处理完毕
4.3将剩余子序列中的元素直接复制到临时数组中
5.将临时数组中的所有元素复制回原数组
6.这个过程确保了合并后的序列仍然保持有序,且保留了相等元素的相对顺序(稳定性)时间复杂度分析归并排序的时间复杂度在各种情况下都是On logn分解序列每一层分解需要时间,总共有层,因此为•O1lognOlog n合并子序列每一层合并总共需要时间,总共有层,因此为•On lognOn logn总体时间复杂度•Onlogn空间复杂度为,主要用于存储临时数组这是归并排序的主要缺点,因为它不是原地排序算法,需要额外的存储空间On计数排序算法思想实现步骤计数排序是一种非比较型排序算法,它的核心思想是统计数计数排序的基本实现步骤如下组中每个值出现的次数,然后根据统计结果重建有序序列•找出待排序数组中的最大值和最小值,确定计数数组计数排序适用于已知范围的整数排序,特别是当数据范围不的大小大而元素数量很多时效率极高•创建计数数组,统计每个元素出现的次数与基于比较的排序算法不同,计数排序不通过比较元素来确•修改计数数组,使其存储的是小于等于该索引的元素定它们的相对顺序,而是利用元素值本身作为索引,这使得个数(前缀和)它能够突破比较排序的下界,在特定条件下达到Onlogn•创建输出数组,从后向前扫描原数组,根据计数数组线性时间复杂度将元素放入正确位置•将输出数组复制回原数组第步和第步的处理使得计数排序成为稳定的排序算法,34保证相同元素在排序后相对位置不变适用场景计数排序最适合用在以下情况排序对象是范围较小的整数•数据量较大,但数据范围相对小•需要稳定排序•对时间性能要求高•典型应用场景包括年龄排序、成绩排序、字符频率统计、基数排序的中间步骤等计数排序的时间复杂度为,其中是元素个数,是数据范围空间复杂度为当远大于时,计数排序的效率会On+k n k Ok k n大大降低桶排序时间复杂度分析桶排序的时间复杂度分析最好情况,当数据均匀分布,每个桶中元素数量相近算法思想•On最坏情况,当所有元素都被映射到同一个桶中•On²桶排序是一种分治策略的排序算法,其核心思想是将数据分散到有限数量的桶中,每个桶再单独排序排序过程包括平均情况,其中是桶的数量,通常取,则复杂度为将数据分配到不同的桶中,对每个桶内的数据进行排序,最后将所有桶中的数据按照顺序合并起来•On+n²/k+kkk≈nOn空间复杂度为,需要额外的空间来存储桶和临时数据桶排序假设输入数据均匀分布在某个区间内,通过映射函数将数据映射到有序的桶上,从而达到排序的目的区间划On+k分越细,排序速度越快,但同时空间消耗也越大桶排序是稳定的排序算法(取决于桶内排序使用的算法),特别适合处理元素值分布均匀的大规模数据3实现步骤桶排序的基本实现步骤如下•确定数据的范围,创建相应数量的桶•设计映射函数,将每个元素分配到对应的桶中•对每个桶中的元素进行排序(可以使用任何排序算法)•按照桶的顺序,将各个桶中的元素合并成最终的有序序列桶的数量和映射函数的设计是桶排序效率的关键因素桶数量越多,每个桶内的元素越少,排序速度越快;但同时空间开销也越大基数排序基数排序是一种非比较型的排序算法,它根据元素的位值(个位、十位、百位)进行排序基数排序适用于数字和字符串等可以按位比较的数据...它的核心思想是从最低有效位(或最高有效位)开始,逐位进行排序,利用了数字在不同位上的分布特性基数排序有两种主要实现方式最低位优先和最高位优先从最低位开始排序,逐渐向高位进行;则相反,从最高位开始,LSD MSDLSD MSD递归地对每个子序列排序适合定长数据,实现简单;适合变长数据,但实现较复杂LSD MSD基数排序的时间复杂度为,其中是最大值的位数,是元素个数,是每位可能的取值范围(如十进制数字为)当相对较小且输Odn+k dnk10d入数据较大时,基数排序比基于比较的排序算法(如快速排序、归并排序)更高效空间复杂度为,主要用于存储临时数组On+k外部排序多路归并算法败者树外部排序主要用于处理无法一次性加载到内存败者树()是多路归并中的一种优Loser Tree中的大数据集多路归并是外部排序的核心技化数据结构,用于高效地选择当前所有归并段术,其基本思想是将大文件分割成若干个能中的最小元素在传统的多路归并中,每次需够装入内存的小文件(称为归并段或游程要比较所有路中的首元素来找出最小值,时间),对每个小文件在内存中排序,然后将排复杂度为,其中是归并路数Okk序好的小文件归并,得到最终有序的大文件败者树是一种特殊的二叉树,它通过记录比较多路归并中的多路指的是同时合并多个有序的败者,减少了每次选择最小元素时的比较序列增加归并路数可以减少归并的轮数,从次数使用败者树后,选择最小元素的时间复而减少操作,提高排序效率但路数过多杂度降为,大大提高了多路归并的效I/O Ologk会增加内存开销和比较次数,需要在实际应用率,特别是在归并路数较多的情况下中权衡替换选择排序替换选择是一种生成初始归并段的优化算法,可以产生长度超过内存容量的有序段,从而减少总归并路数传统的方法是填满内存,排序,输出,然后处理下一批数据;而替换选择则采用类似堆排序的方法,动态地处理输入流其工作原理是维护一个最小堆,初始时将内存填满;每次从堆顶取出最小元素并输出,然后从输入流读入新元素;如果新元素大于等于刚输出的元素,则放入堆中;否则开始构建新的归并段这种方法平均产生长度为的归并段(为内存容量),比传统方法的长度更优2M MM课程总结知识体系完整从基础概念到高级应用的系统化学习理论实践结合2算法分析与实现并重C++算法思维培养掌握分析问题和解决问题的方法论恭喜你完成了《数据结构与算法分析》课程的学习!通过本课程,你已经系统地掌握了数据结构与算法的基本概念、实现方法和分析技术从简单的线性结构如数组、链表、栈和队列,到复杂的树结构和图算法,再到各种查找和排序算法,这些都是计算机科学的基石算法设计中的关键技巧包括分治法(将大问题分解为小问题)、动态规划(利用子问题的解构建最优解)、贪心策略(局部最优推导全局最优)、回溯法(有选择地尝试所有可能)等掌握这些技巧将帮助你应对各种算法挑战数据结构与算法是一个不断发展的领域,建议你继续深入学习高级数据结构(如跳表、布隆过滤器)、并行算法、分布式算法等参与算法竞赛或开源项目也是提升实践能力的好方法记住,算法思维的培养是一个持续的过程,需要通过不断实践和思考来强化。
个人认证
优秀文档
获得点赞 0