算法导论-复习笔记

合集下载

《算法导论》读书笔记

《算法导论》读书笔记

哨兵(sentinel) 哨兵是一个哑对象,其作用是简化边界条件的处理。 类似于以前学习时讲到的头节点。加入哨兵将原来的双向链表转变成一个有哨兵的双向循环两表。L.nil代表哨兵。一图胜千言,如下:
如果有很多个很短的链表,慎用哨兵,因为哨兵所占用的额外存储空间会造成严重的存储浪费。哨兵并没有优化太多的渐进时间界,只是可 以使代码更紧凑简洁。 指针和对象的实现 对象的多数组表示: next和prev中的数字指的是对应数字的下标,有趣!
关于几个时间复杂度
通常情况下,当数据量足够大时,一般满足 θ(1)>θ(N)>θ(NlogN)>θ(N^2) (>代表优于)
1.算 法 基 础
1.1 插 入 排 序
时间复杂度:O(N^2) 思想:每次从右至左跟已排序数列进行对比,放入合适位置。两次遍历,一次相当于摸牌,另一次相当于具体的查找算法。
1.2 分 治 法
解决冲突的方法
链接法(chaining)
关于链接法采用双链的一些解释: 简单讲,删除就是从x对应的链表里删除x,双链表的删除加入哨兵只需两行,但是单链表的话只能指定x.next=null,但是在这之前需 要先将x.prev.next指向x.next,由于是单链,所以没有prev,只能一直next找下去,相比双链多了查找的时间耗费。
将问题分解为几个规模较小但类似于原问题的子问题,递归的求解这些子问题,然后再合并这些子问题的解来建立原问题的解。 (分解-解决-合并) 归并排序 时间复杂度O(NlogN)(O还是θ——theta,都差不多)
归并排序算法如下:
函数的增长
O渐进上界Ω渐进下界,o和ω 具体看数学定义最清楚。
分治策略
1. 维护堆的性质 通俗的讲,就是保持数组的最大堆性质。思路比较简单,比较parent节点和孩子节点,找到最大值,如果最大值是某个孩子节点,交 换,递归运行。对于树高为h的节点来说,该程序时间复杂度为O(h)

算法导论笔记

算法导论笔记

第一章算法在计算中的应用第九章中位数和顺序统计学9.1-11.将数组中的元素分组,每组两个元素,然后比较每组中的两个元素得到最小值,重新得到包含原来一半元素的数组,继续重复上述过程,那么最后一个元素必然为最小值。

如图所示,数组为{2, 1, 4, 3, 5}2.上述过程形成的是一个二叉树,其中叶子节点都为数组元素,非叶子节点刚好4个,这是二叉树的性质。

3.然后我们来找第二小元素,第二小元素必然跟着1,首先赋值为5,然后再赋值为3,然后赋值为2,即为所求。

【运行结果】:1.我们先将N个数配对,每两个数一对,每对之间进行互相比较得出小值。

2.对得出的N/2个元素重复第一步,直至得出最小值。

到这儿我们得出了最小值,实际上比较的次数为n-1次。

不难发现上面的过程实际上可以演示为一个二叉树。

例如在5,8,11,18四个数找出最小值,二叉树演示如下(每个节点相当于一次比较):观察二叉树,可以得出这样一个结论,所有与最小值比较过的数中的最小值纪即为第二小的的值。

二叉树的高度为lgn,因此与最小值比较的数的数目为lgn,在lgn个数中找出最小值要进行lgn-1次比较。

9.1-29.3节的方法可以在最坏情况下的线性复杂度的算法来求顺序统计量.其核心思想在于获得一个更好的中枢值来更好地划分数组.然而题中给了我们一个"黑盒子"来以线性复杂度获取一个真正好的中枢值,那么再好不过了。

在同时找到最大值和最小值时,每个元素都参与了与最大值和最小值的比较,比较了两次。

将数组成对处理。

两两互相比较,将大的与最大值比较,小的与最小值比较。

每两个元素需要的比较次数是,两两比较一次,大者与最大值比较一次,小者与最小值比较一次,共三次。

9.2-1长度为0的数组,RANDOMIZED-SELECT直接返回,当然不会递归调用。

9.2-29.2-3 写出RANDOMIZED-SELECT的一个迭代版本【算法思想】:递归:在函数中调用自身的程序过程。

算法导论小笔记

算法导论小笔记

算法导论小笔记算法导论(CLRS)笔记Note on CLRS(Outline & Draft, 2011)Jian Xiao1第2章Getting started1. Merge sort相关的扩展问题1)链表实现V.S.数组实现。

Divide阶段,如何快速找到链表的中点?另外,如何减少Divide/Combine的时间(相比数组实现)?2)In-place merge的方法。

3)归并树:把Merge sort的中间过程都记录下来,所形成的树。

(其实就是一棵线段树,每个节点存放该节点的区间内有序化后的数)4)逆序数的计算。

2. 两数和问题。

数的集合S,一个数x,判断S中是否存在两个数,二者的和为x。

这个问题扩展以后是一个subset problem,为NPC问题。

第3章Growth of Functions1. 全部5个渐进符号的确切含义第4章Recurrences1. Substitution方法,Recursion tree方法计算复杂度2. Master Theorem及其不能被三种case覆盖的其他情况Master Theorem的证明3. Chip testing problem4. Monge arrays,凸四边形不等式,最优二叉查找树DP算法的优化第5章Probabilistic Analysis and Randomized Algorithms1. 用Biased-Random产生Uniform-Random分布。

2. In-place、O(n)的uniform random permutationIn-place、O(n)的各个position等概的排列1iamxiaojian@/doc/d1*******.html,3. Coupon collector’s problem4. On-line hiring problem第6章Heapsort1. 两种建堆的方法:逐个插入的O(nlogn),以及自底向上调整的O(n)算法2. Heap sort可能存在效率的地方:每次删除堆顶,都是把某树叶放置于堆顶,然后调整;但是,树叶一般比较大,放于堆顶后,几乎总是要进行多次的比较才能恢复堆的结构。

算法导论读书笔记

算法导论读书笔记

算法导论读书笔记【篇一:《算法概论》读书笔记及读后感】《算法概论》读书笔记12计转1 12130907 李酉辰第0章本章较为简短,没有深入系统地涉及某些内容。

主要以fibonacci 数列的例子,让我体会了递归和递推思想的差别。

针对fibonacci数列例子直接递归解法中涉及的重复计算,优化出递推方式,展示了思考问题中自顶向下与自底向上的不同思考角度可能产生较大的算法效率差别,同时隐约体现记忆化搜索的思想。

另外本章较为详细介绍了大o复杂度度量标准。

第1章本章以rsa算法为例,细致深入讨论了rsa算法涉及的相关数论知识,诸如取模运算、模下的四则运算与逆元概念、取模幂运算、素性检测。

在素性检测部分有经典的欧几里德算法、扩展欧几里德算法,同时引入随机化算法概念,以极高的概率保证素性检测有效性。

通过本章的学习,我对过去不曾深入考虑或者说真正考虑的基础性运算有了更深的理解。

之前对乘除运算复杂度总是在以单元操作的概念下以o(1)带过,以后会更加细致地考虑乘除等基本运算的复杂度。

另外,本章以rsa为案例,系统地展示了针对某一问题,如何从基础性知识入手,一步一步学习案例所需基础知识,并将其整合从而解决案例。

素性检测与素因子分解,两个看似相去不远的问题,其复杂性天差地别的现实,从一般角度让人们想到的是类似问题的解决难度可能差别很大仅此而已,而rsa算法展示了如何深入的多想一步,利用这种情况设计出优雅的解决方案。

这思想很值得我借鉴与利用。

第2章本章介绍分治算法思想,提及分治,相信每一个学习算法的人都不会陌生,经典的《算法导论》中就已合并排序为例在开篇不久就引入分治概念。

本书介绍分治的角度与众不同,不似《导论》中总是介绍比较显而易见的可以分治的案例。

本书列举了矩阵相乘、快速傅立叶变换等数学领域分治的应用案例,在这些案例之中,分治的应用很多情况下隐藏的较为深,并非显而易见,加大了分析难度。

但是更能让我感受到分治应用之广泛,可能在学习本章之前,许多类型的题目我不会想到去向分治的角度思考,因为不易看出,但是本章给我的备忘录上加了一条:永远不要忽视分治,针对陌生题目,不要轻易就否决掉往分治角度思考的路线。

算法导论(第三版)-复习-第六部分图论22-26[转]

算法导论(第三版)-复习-第六部分图论22-26[转]

算法导论(第三版)-复习-第六部分图论22-26[转]22习题22.1-5 有向图G(V, E)的平⽅图。

链表表⽰时,对每结点u的Adj[u]中所有v加⼊队列,后边出队边将Adj[v]加⼊Adj[u]中。

矩阵表⽰时,若w[i, j]、w[j, k]同时为1则将w[i, k]置1.习题22.1-6 O(V)的时间寻找通⽤汇点。

汇点的特征是邻接矩阵的第j列除[j, j]外所有元素为1. 可将每⾏调整[j ,j]后作为⼀个整数,所有整数与运算,为1的位是汇点。

习题22.1-7 有向⽆环图的关联矩阵B,BB’每个元素C[i, j]=∑B[i, k]*B’[k, j]=∑B[i, k]*B[j, k],即同时进i, j两结点与同时出i, j的结点总数-⼀进⼀出i, j两结点的结点总数。

习题22.2-7 类似BFS,d mod2为0则标为B(娃娃脸),d mod2为1则标为H(⾼跟鞋)。

但若有边连接相同类的结点,则⽆法划分。

wrestler(G){for each u in G{(u,v)=Adj[u];if(v.mark==u.mark){throw error;}if(v.d==NIL) {v.d=u.d+1; v.mark=v.d mod 2;}}}习题22.2-8 任意点之间的最短路径。

重复的Dijktra算法或Floyd-Warshall算法习题22.2-9 ⽆向图扩展为有向图。

问题变成要遍历所有边⼀次。

访问结点u时,将u的⼦结点v的其他边都可视为⼦集v,问题等价于u到v,访问v的集合,v到u。

u标为visiting⼊列,然后访问v,v标为visiting⼊列,然后访问v的后继结点,访问过的边标为visited,返回到visiting的点时,如果该点所有连接的边都标为visited只剩⼀条返回上级的边,则返回上级结点并将点标为visited,v出列,访问u的其他⼦结点,最终u出列。

全部结点出列后达到遍历所有边⼀次。

《算法导论》读书笔记 附录A习题解答

《算法导论》读书笔记 附录A习题解答

A.1-1求的简化公式。

利用等差级数求和公式和级数线性性质:
A.1-2利用调和级数性质证明。

利用调和级数性质:
A.1-3对,证明。

对无穷递减几何级数式两边求导,再乘以:
对该式再进行同上操作得到:
A.1-4 求。

A.1-5 求的值。

当时求得
当时:
计算得到:
A.1-6 利用求和公式的线性特征证明。

令,则下式显然成立:
再把函数代换回即可。

A.1-7 求的值。

A.1-8 求的值。

A.2-1 证明有常量上界。

A.2-2 求和的渐近上界。

故渐近上界是
A.2-3 通过分割求和证明第个调和数是。

故取得下界
A.2-4 通过积分求的近似值。

A.2-5 题略。

为了保证被积函数在积分域上都连续。

思考题
A-1 求和的界
求下列和式的渐近确界。

假设,都是常量。

a)
,得到确界为
b)
根据此式得到上界:
故得到下界:
故据此得到确界
c)
故得到上界:
故得到下界:
因此得到确界。

算法导论复习资料

算法导论复习资料

算法导论复习资料一、选择题:第一章的概念、术语。

二、考点分析:1、复杂度的渐进表示,复杂度分析。

2、正确性证明。

考点:1)正确性分析(冒泡,归并,选择);2)复杂度分析(渐进表示O,Q,©,替换法证明,先猜想,然后给出递归方程)。

循环不变性的三个性质:1)初始化:它在循环的第一轮迭代开始之前,应该是正确的;2)保持:如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确;3)当循环结束时,不变式给了我们一个有用的性质,它有助于表明算法是正确的。

插入排序算法:【INSERTION-SORT(A)1 for j ←2 to length[A]2 do key ←A[j]3 ▹Insert A[j] into the sorted sequence A[1,j - 1].4 i ←j - 15 while i > 0 and A[i] > key6 do A[i + 1] ←A[i]7 i ←i - 18 A[i + 1] ←key插入排序的正确性证明:课本11页。

》归并排序算法:课本17页及19页。

归并排序的正确性分析:课本20页。

3、分治法(基本步骤,复杂度分析)。

——许多问题都可以递归求解考点:快速排序,归并排序,渐进排序,例如:12球里面有一个坏球,怎样用最少的次数找出来。

(解:共有24种状态,至少称重3次可以找出不同的球)不是考点:线性时间选择,最接近点对,斯特拉算法求解。

解:基本步骤:一、分解:将原问题分解成一系列的子问题;二、解决:递归地解各子问题。

若子问题足够小,则直接求解;三、合并:将子问题的结果合并成原问题的解。

复杂度分析:分分治算法中的递归式是基于基本模式中的三个步骤的,T(n)为一个规模为n的运行时间,得到递归式、T(n)=Q(1) n<=cT(n)=aT(n/b)+D(n)+C(n) n>c附加习题:请给出一个运行时间为Q(nlgn)的算法,使之能在给定的一个由n个整数构成的集合S 和另一个整数x时,判断出S中是否存在有两个其和等于x的元素。

算法导论读书笔记

算法导论读书笔记

算法导论读书笔记第1章:算法在计算中的作用1.算法即是一系列的计算步骤,用来将一个有效的输入转换成一个有效的输出。

2.计算机的有限的资源必须被有效的利用,算法就是来解决这些问题的方法。

第2章:算法入门1、循环不变式的三个性质:(循环不变式通常用来证明递归的正确性)(1)初始化:它在循环的第一轮迭代开始之前,应该是正确的。

(2)保持:如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。

(3)终止:当循环结束时,不变式给了我们一个有用的性质,它有助于表明算法是正确的。

2、分治策略:将原问题划分成n个规模较小而结构与原问题相似的子问题;递归地解决这些小问题,然后再合并其结果,就得到原问题的解。

3、分治模式在每一-层递归上都有三个步骤:(1)分解(Divde):将原问题分解成-系列子问题;(2)解决(Conquer):递归地解答各子问题。

若子问题足够小,则直接求解;(3)合并(Combine):将子问题的结果合并成原问题的解。

第3章:函数的增长1.对几个记号的大意:。

(非渐近紧确上界)≈<;0(渐近上界)≈≤;日(渐近紧界)≈=;2(渐近下界)≈≥;w(非渐近紧确下界)≈>;这里的<,s,=,2,>指的是规模上的比较,即o(n))的规模比g(n)小。

o(g())={f(n):对任意正常数C,存在常数n.>0,使对所有的n≥n,有0≤f(n)<cg(n)}O(g(n))={f(n):存在正常数c和n。

,使对所有n≥n。

,有0S(n)≤cg(n)}0(g(n)={f(n):存在正常数Cc.c和n。

,使对所有的n≥n,有0≤c,g(n)Sf(n)≤cg(n)}Q(g(n))={f(n):存在正常数c和n。

,使对所有n≥n,有0≤cg(n)Sf(n)}w(g())={f(n):对任意正常数c,存在常数n,>0.使对所有的n,有0≤cg(n)<f(n)}第4章:递归式1.递归式是一组等式或不等式,它所描述的函数是用在更小的输入下该函数的值来定义的。

(完整版)算法导论复习要点

(完整版)算法导论复习要点

一、基础知识部分1. 算法的复杂性有时间复杂性和空间复杂性之分。

2. 算法是由若干条指令组成的有穷序列,且要满足输入、输出、确定性和有限性四条性质。

3. 快速排序算法是基于分治策略的一种排序算法。

4. 矩阵连乘问题的算法可由动态规划设计实现。

5、算法是指解决问题的一种方法或一个过程。

6、从分治法的一般设计模式可以看出,用它设计出的程序一般是递归算法。

7、问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

8、贪心选择性质是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。

9. 贪心算法的基本要素是贪心选择质和最优子结构性质。

10. 动态规划算法的两个基本要素是最优子结构性质和重叠子问题性质。

11、合并排序算法是利用(A )实现的算法A、分治策略B、动态规划法C、贪心法D、回溯法12、下列不是动态规划算法基本步骤的是( A )。

A、找出最优解的性质B、构造最优解C、算出最优解D、定义最优解13.下列算法中通常以自底向上的方式求解最优解的是( B )。

A、备忘录法B动态规划法C、贪心法D回溯法14. 备忘录方法是那种算法的变形。

(B )A、分治法B动态规划法C、贪心法D回溯法15.最长公共子序列算法利用的算法是( B )。

A、分支界限法B动态规划法C贪心法D回溯法16.贪心算法与动态规划算法的主要区别是( B )。

A、最优子结构B、贪心选择性质C、构造最优解D、定义最优解17. (D )是贪心算法与动态规划算法的共同点。

A、重叠子问题B、构造最优解C、贪心选择性质D最优子结构性质18. 矩阵连乘问题的算法可由(B )设计实现。

A、分支界限算法 B 、动态规划算法C、贪心算法D、回溯算法19、使用分治法求解不需要满足的条件是( A )。

A 子问题必须是一样的B 子问题不能够重复C 子问题的解可以合并D 原问题和子问题使用相同的方法解20.下列是动态规划算法基本要素的是( D )。

算法导论读书笔记

算法导论读书笔记

算法导论读书笔记算法导论读书笔记篇1《算法导论》是计算机科学中一门重要的课程,它是一本经典的算法教材,被广泛使用于各个领域。

这本书的作者是美国计算机科学家ChristopherD.H.Anderson,他是一位著名的计算机科学家和数学家,曾在斯坦福大学和卡内基梅隆大学任教。

这本书主要介绍了各种基本算法,包括排序、搜索、图论、动态规划、贪心算法、分治算法等。

它通过示例代码和问题解决的方式,向读者展示了如何使用这些算法来解决实际问题。

这本书的特点是简洁明了、易于理解、逻辑清晰、重点突出。

作者在书中使用了通俗易懂的语言和简单的例子,使得读者可以轻松地理解各种算法的原理和应用。

同时,作者还对各种算法进行了深入的分析和比较,使得读者可以更好地理解它们的优缺点和应用场景。

在阅读这本书的过程中,我深刻地感受到了算法的重要性和应用价值。

算法是一种解决问题的工具,它可以帮助我们快速地解决复杂的问题,提高工作效率。

同时,算法也是一种思维方式和解决问题的手段,它可以帮助我们更好地理解问题和现象,提高我们的逻辑思维能力和解决问题的能力。

在阅读这本书的过程中,我遇到了一些困难和挑战。

首先,书中的算法种类繁多,有些算法比较抽象,需要深入思考才能理解它们的原理和应用。

其次,书中的代码示例比较简单,需要自己动手实现才能更好地理解算法的原理和应用。

总的来说,《算法导论》是一本非常优秀的教材,它可以帮助我们更好地理解算法的原理和应用,提高我们的逻辑思维能力和解决问题的能力。

在未来的学习和工作中,我将继续深入学习和研究算法,不断提高自己的专业水平和实践能力。

算法导论读书笔记篇2《算法导论》是计算机科学中广泛使用的一种经典算法教材。

本书的目的是为学生提供一种系统而全面的算法学习体验,旨在帮助学生理解算法的基本原理,掌握常见算法的实现和应用,提高编程能力和解决问题的能力。

本书共有11个章节,涵盖了各种常见算法的介绍和实现,如排序、搜索、图论、动态规划、贪心算法等。

计算机算法导论部分知识点

计算机算法导论部分知识点

算法的概念:算法是解决问题的一种方法或者过程算法是若干指令的有穷序列包含以下5条性质:有0个或者多个输入至少一个输出有限性确定性可行性算法复杂性=算法所需的计算机资源算法复杂性是算法效率的度量算法的时间复杂性和算法的空间复杂性渐进上界O 渐进下届渐进下界o非近下界w渐进近界分治法的设计思想:讲一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破之,分而治之分治法所能解决的问题一般具以下几个特征1:将问题的规模缩小到一定程度就可以容易地解决2:该问题具有最优子结构性质3:利用该问题分解出的子问题的解可以合并为该问题的解4:子问题之间不包含公共的子问题分治法的步骤:1:分解:将原问题分解为若干规模较小,相互独立,与原问题形式相同的子问题2:解决:若子问题规模较小而容易被解决则直接解,否则递归的求解各个子问题3:合并:将各个子问题合并为原问题的解二分搜索技术:O(logn)快速排序:O(NLOGN)合并排序:O(LOGN^2)动态规划算法的基本要素:1:最优子结构性质2:重叠子问题性质动态规划的基本步骤:1:找出最优解性质,并刻画其结构特征2:递归地定义最优值3:以自底向上的方式计算最优值4:根据计算最优值时得到的信息,构造最优解动态规划算法的基本思想:将待解决的问题分解成若干子问题,先求解子问题,然后从这些子问题的解得到原问题的解二分搜索算法是利用(动态规划算法)实现的算法贪心算法的基本思想:1:最优子结构性质2:贪心选择性质贪心选择算法的步骤:1:从问题的某个初始解出发2:采用循环结构,当可以向求解目标前进一步时,根据局部最优策略,得到一个部分解,缩小问题的范围或规模3:将所有部分解综合起来,得到问题的最终解贪心算法和动态规划算法的差别:相同点:具有最优子结构性质不同点:贪心算法不能解决0-1背包问题回溯法的基本思想:1:针对所给问题,定义该问题的解空间2:确定易于搜索的解空间结构3:以深度优先方式搜索解空间结构,并在搜索过程中用剪枝函数避免无效搜索回溯法的搜索效率:1:产生x【k】的时间2:满足显约数的x【k】值的个数3:计算约束函数constaint的时间】4:计算上界函数bound的时间5:满足约束函数和上界函数的所有x【k】的个数最优子结构性质:某问题的最优解包含着其子问题的最优解。

《算法导论》读书笔记之第10章 基本数据结构之二叉树

《算法导论》读书笔记之第10章 基本数据结构之二叉树

《算法导论》读书笔记之第10章基本数据结构之二叉树摘要书中第10章10.4小节介绍了有根树,简单介绍了二叉树和分支数目无限制的有根树的存储结构,而没有关于二叉树的遍历过程。

为此对二叉树做个简单的总结,介绍一下二叉树基本概念、性质、二叉树的存储结构和遍历过程,主要包括先根遍历、中根遍历、后根遍历和层次遍历。

1、二叉树的定义二叉树(Binary Tree)是一种特殊的树型结构,每个节点至多有两棵子树,且二叉树的子树有左右之分,次序不能颠倒。

由定义可知,二叉树中不存在度(结点拥有的子树数目)大于2的节点。

二叉树形状如下下图所示:2、二叉树的性质(1)在二叉树中的第i层上至多有2^(i-1)个结点(i&gt;=1)。

备注:^表示此方(2)深度为k的二叉树至多有2^k-1个节点(k&gt;=1)。

(3)对任何一棵二叉树T,如果其终端结点数目为n0,度为2的节点数目为n2,则n0=n2+1。

满二叉树:深度为k且具有2^k-1个结点的二叉树。

即满二叉树中的每一层上的结点数都是最大的结点数。

完全二叉树:深度为k具有n个结点的二叉树,当且仅当每一个结点与深度为k的满二叉树中的编号从1至n的结点一一对应。

可以得到一般结论:满二叉树和完全二叉树是两种特殊形态的二叉树,满二叉树肯定是完全二叉树,但完全二叉树不不一定是满二叉树。

举例如下图是所示:(4)具有n个节点的完全二叉树的深度为log2n + 1。

3、二叉树的存储结构可以采用顺序存储数组和链式存储二叉链表两种方法来存储二叉树。

经常使用的二叉链表方法,因为其非常灵活,方便二叉树的操作。

二叉树的二叉链表存储结构如下所示:1 typedef struct binary_tree_node2 {3 int elem;4 struct binary_tree_node *left;5 struct binary_tree_node *right;6 }binary_tree_node,*binary_tree;举例说明二叉链表存储过程,如下图所示:从图中可以看出:在还有n个结点的二叉链表中有n+1个空链域。

完整word版,《算法导论》复习大纲DOC

完整word版,《算法导论》复习大纲DOC

《算法设计与分析》复习提纲2014.7.51 引言(ch1)1.什么是算法及其特征算法(Algorithm)是通过一个有限的指令序列集合对特定问题进行求解的一种计算执行描述。

算法特征:(1)输入:一个算法具有零个或多个取自指定集合的输入值;(2)输出:对每一次输入,算法具有一个或多个与输入值相联系的输出值;(3)确定性:算法的每一个指令步骤都是明确的;(4)有限性:对每一次输入,算法都必须在有限步骤(即有限时间)内结束;(5)正确性:对每一次输入,算法应产生出正确的输出值;(6)通用性:算法的执行过程可用于所有同类求解问题,而不仅适用于特殊输入。

2.问题实例和问题规模问题实例是指需要计算同一个结果的问题的所有输入。

问题规模是指输入实例的大小,而输入实例是指问题的具体计算例子2 算法初步(ch2)1.插入排序算法1)算法步骤:从左到右扫描数据A,扫描到一个元素,将A[j]与其左边的元素从右到左依次比较,若比之小,则将其之前元素后移,插入A【j】,直至A【j】比他前面的元素大,扫描A中的下一个元素2)伪代码:InsertSort(A){for j=2 to A.length //第一层循环{Key=A[j]i=j-1While i>0 and a[i]>key //第二层循环{A[i+1]=A[i]}i=i-1A[i+1]=key}}2.算法复杂性及其度量(1)时间复杂性和空间复杂性;(2)最坏、最好和平均情形复杂性;顺序情况下B(n)=O(n)、倒序情况下W(n)=O(n2)、A(n)=O(n2)<W(n)空间复杂性:需要常数个额外的临时空间存储临时数据2.插入排序的最坏、最好和平均时间最坏O(n2)、最好O(n)和平均时间O(n2),空间复杂度是O(1),稳定排序3.归并排序算法及其时间复杂性-时间Θ(n log n))1)算法步骤分解:分解待排序的n个元素的序列为各具n/2个元素的两个子序列解决:适用归并排序递归的排序2个子序列合并:从左到有遍历2个子序列,比较最前面的元素,将较小的元素移出子序列合并到上级序列的末尾,循环进行上2步,直接所有元素都被合并到上级序列,公进行r-p+1次;2)伪代码:MERGE-SORT(A,p,r){if p<rq=向下取整(p+r)/2MERGE-SORT(A,p,q);MERGE-SORT(A,q+1,r)MERGE(A,p,q,r)}MERGE(A,p,q,r){N1=q-p+1N2=r-q将A拆成长度分别为N1、n2的2个子数组L,RL,R的末尾元素的后一个元素取值无穷大,作为哨兵;i=1,j=1for k=p to rif L[i]<=R[j]A[k]=L[i]i=i+1elseA[k]=R[j]j=j+1}3函数增长率(ch3)1.渐近记号O、Ω、θ的定义及其使用1)O渐进上界:0<=f(n)<=C(g(n))当n->∞, f(n)的阶小与g(n)的阶2)Ω渐进下界:0<=C(g(n)) <=f(n)当n->∞, f(n)的阶大与g(n)的阶3)Θ渐紧界:0<=C1(g(n)) <=f(n) <=C2(g(n))当n->∞, f(n)的阶与g(n)的阶相等2.标准复杂性函数及其大小关系(1)多项式时间阶的大小O(1) < O(log n) < O(n) < O(n*log n) < O(n²) < O(n3)(2)指数时间阶的大小O(2n) <O(n!) < O(n n)3.和式界的证明方法1)数学归纳法猜测解->证明2)对象限界最大最小项限界;几何级数限界;3)和式分解简单的一分为二;更复杂的划分;积分近似;4)Knuth求和:使用数学归纳法;使用摄动法;使用递归;使用积分;使用二重求和;使用有限演算;使用母函数。

《算法导论》学习总结——快速排序

《算法导论》学习总结——快速排序

《算法导论》学习总结——快速排序曾经在程序员杂志上看到快速排序的作者,Hoare,曾经的图灵奖获得者啊,牛光闪闪的。

不过当时,对快速排序什么的,印象不算深刻,毕竟没好好学。

记得当时杂志上说到的是,快速排序,应该是目前最快的内部排序算法(虽然独立到语言上,C++的sort会比调用快速排序快)。

现在就进入快速排序的美好复习吧。

与归并排序类似,快排也用分治模式。

主要是三个步骤:1)分解:将数组A[p....r]划分为2个子数组A[p....q-1]和A[q+1....r],使前一个每个元素都小于A[q],后一个数组,每个元素都大于A[q](q在划分过程中计算)2)解决:递归调用快速排序,对2个子数组进行排序3)合并:因为2个子数组是就地排序,所以合并不用操作,数组已排序看到这个合并,就想到啊,和归并比,一个从小到大,一个从大到小,差距就是这么大,快排么得合并开销,一下就省了很多啊,说明,方向很重要啊,如同那句,同样一个B,S与N 的差别,大家都懂的。

快速排序的实现代码如下:1//===============================================================2// Name : Qsort.cpp3// Author : xia4// Copyright : NUAA5// Description : 快速排序的实现6//===============================================================7#include <iostream>8#include <vector>9#include <fstream>10#include <algorithm>11#include <ctime>1213using namespace std;14const int MAX = 1000 ;1516void WriteToFile(vector<int> v)17{//将v写入文件,纯看排序结果是否正确,也可以写个test()18int i;19 ofstream result("Qsort.txt");20if (result.fail())21 {22 cout << " open data error " << endl;23 exit(EXIT_FAILURE);24 }25for (i=0 ; i<v.size() ; i++)26 {27 result << v[i] << " " ;28 }29 result.close();30}31int Partion(vector<int> &A,int p ,int r)32{//数组划分33int x=A[r];//x都感觉没用34int i=p-1;35for (int j=p ; j<r ;j++)36 {37if ( A[j] <= x )38 {39 i++;40 swap(A[i],A[j]);41 }42 }43 swap(A[i+1],A[r]);44return i+1;45}46void Qsort(vector<int> &A, int p ,int r)47{//递归快排48if (p < r)49 {50int q = Partion(A,p,r);51 Qsort(A,p,q-1);52 Qsort(A,q+1,r);53 }54}55int main(int argc, char **argv)56{57 vector<int> v;58int i;59for (i=0 ; i< MAX ;i++)60 v.push_back(i);61 random_shuffle(v.begin(),v.end());//打乱6263 Qsort(v,0,v.size()-1);64 WriteToFile(v);6566return 0;67}说到代码,很惭愧的,/chinazhangjie/archive/2010/12/09/1901491.html张杰同学的c++模板类实现,(这里也得感谢Tanky Woo,看了他的总结,得以看很多的链接),果然我写的只是C的扩充啊,像用vector只是防止溢出,也就是数组来使用,一些操作也只是作为一个扩充。

MIT CLRS NOTES 2 mit 算法导论课堂笔记2

MIT CLRS NOTES 2 mit 算法导论课堂笔记2

Lecture 7 Symbol-table problemTable T holding n records.record is a hunk of data broken into what we called fields. dynamic set OperationsInsert(T,x)Delete(T,x)Search(T,k)Idea: Sup. the set of keys is K ≤{}1,...,1,0-m and keys are distinct. Set up array T[0,...,m-1] or “direct-access ” tablex if x T[k]=nil otherwisex can be a pointer or just record itselfP223 figure 11.1 ops take )1(O timepractical application can be bit vector to sort keys, just like counting sort(count once or not)Problem: usu. the range of keys is large264 nearly = 16*1018 too huge“dynamic set ”Solution: Use a hash function h to map keys into {0,1,…,m-1}P255 figure 11.2We have Universe U of keys, a much smaller set we care about is K. what the hash function is going to do is map keys into a table T[0..m-1]When a record to be inserted maps to an already occupied slot a collision occurs. Perfert hashing with no collision will be talked next lecture. Resolving collision by chaining:Records in same slot are linked into a list.Ex. P225 figure 11.3Analysis of chaining(only chaining not hashing)Worst case: into the same slot.Ave. case: Assumption of simple uniform hashingEach key in K is equally likely to be hashed to any slot inT indep. of where other keys are hashed.n key, m slotsload factor α=n/m=ave. # keys per slotExp. Search cost=)+Θ 1 represents hash & access slot ; αsearch1(αlist. = )1(Θif α=)1(O, i.e. if n=)O(mChoosing a hash functionShould distribute keys uniformly into slotsRegualriy in the key distribution should not affect this uniformityDivision method integer not floating divisionH(k)=k mod mDon’t want m to have a small divisor dWorse: m=2r -does not depend on all bits of k101110011000 r=6Pick m to be prime ????? not too close to power of 2 or 10Multiplication method faster than divisionFaster because multiplicaion software implementation of our computer is the inner loop of division impl.m=2r , computer has w-bit wordsh(k)=(A*k mod 2w)rsh(w-r)A is odd integer 2w-1 <A<2w A’s first(not sigh) and last bit is on; rsh is bit wise right shift opDon’t pick A too close to 2wP232 ExModular wheel ????Another method to collision resolution isOpen addressingno storage outside of hash tableprobe systematically until an empty slot is foundh: U univers of keys {0,1,.., m-1} prob #-> {0,1,.., m-1} slot probe sequence for a given key should be a permutationtable may fill up( chaining may cause long list); del. is diffcultOpen address is used where with no deletions, Although deletion is possible in this method. eg: compilerEx. InsertK=496Search- same probe seq.-successful –find record-unsuccessful –find nilProbing strategiesLinear probing : look seq. through table“primary clustering ” –long runs of filled slotsDouble hashing h(k,i)=(h 1(k)+ih 2(k)) mod m (excellent)usu. pick m=2r and h 2 (k) odd Analysis Uniform hash – asumme ea. key equally likely to have any one of the m! perms. as its prob seq. indep. of other keys Therom E[# probes] ≤α-11 if 1<α (i.e., n<m) P241 formal oneProof.1 probe always necessary with prob n/m collision -> 2nd probe nec.2 prob. n-1/m-1 collision -> 3nd probe nec. Note:i m i n -- <mn =α for i=1,2,…,n-1 E[# probes]=1+m n (1+11--m n (1+22--m n (…(....1(1αα++≤....12αα++≤α-≤11 When this is constant timeLecture 8 Weakness of hashingFor any choice of hash function, ∃ bad set of keys that all hash to same slot. Idea: Choose a hash function at random, independently from keys. Universal hashing Def. Let U be a universe of keys, and let be H a finite collection of hash function mapping U to {0,1, ... , m-1} H is universal if for x, y ∈U, where x ≠y, m Hy h x h H h ==∈)}()(:{the chance of collision betw. x and y is 1/m if h is chosen randomly from H{hTheorem Choose h randomly from H(universe), and sup we wish to hash n keys into m slots in table T. Then, for a given key x,E[#Proof. Let C x be random variable denote total # collisions of keys in T with x, and letH1 if h(x)=h(y)c xy =0 otherwiseNote: E[c xy ]=1/m (because H is universal) and C x =∑-∈}{x T y xy c therefore E[C x ]=E[∑-∈}{x T y xy c ]=(n-1)/m <αConstructing a universal hash function Let m be prime. Decompose key k ∈U into r+1 “digits ”k=<k 0 , k 1 , ... , k r > where m k i <≤0the following random a will determine which hash function to use. Pick a=< a 0 , a 1 , ... , a r >, each a i randomly from{0,1, ... ,m-1} Define h a (k)=(∑=ri i i k a 0) mod m (Dot product modulo m)How big is H={h a }? m r+1Theorem H is universalLet x=< x 0 , x 1 , ... , x r >y=< y 0 , y 1 , ... , y r > be distinct keysThey differ in at least one digit, wlog(without loss of generality) pos. 0. For how many h a ∈H do x and y collide?Must have h a (x)= h a (y)->∑∑==≡r i i i r i i i y a x a 00(mod m) {≡means congruent}->∑=≡-r i i i i y x a 00)( (mod m)->∑=≡-+-ri i i i y x a y x a 10000)()((mod m)->∑=--≡-ri i i i y x a y x a 1000)()((mod m)Number theory factLet m be prime, for any z ∈m Z (integers mod m)$(such that) z 0≠, ∃ unique z -1 ∈ m Z $ z ⨯z -1≡1(mod m) Ex. m=7 z1 2 3 4 5 6 z -11 4 523 6Since x 0≠y 0, ∃( x 0-y 0)-1->10010))()((-=---≡∑y x y x a a r i i i i (mod m)Thus, for any choices of a 1, a 2, ... a r , exactly one of m choices for a 0 cause x and y to collide.# h a ’s choices that cause x, y collide = m (choices for a 1)⨯m(choices fora 2) ...m(choices for a r )⨯1(choices for a 0, the above one )= m r =m HPerfect hashingGiven n keys, construct a static hash table of size m=O(n) $ search tales )1(Θtime in the worst case.Idea: 2 level scheme with universal hashing in both levelsNo collisions at level 2!Level-2 analysisTheorem Hash n keys into m=n 2 slots using random h in universal H E[#collisions]<1/2 verse of birthday paradoxProof Prob 2 given keys collide under h is 1/m=1/n 2⎪⎪⎭⎫ ⎝⎛2n pairs of keys -> E[#collisions]= 212n n ⨯⎪⎪⎭⎫ ⎝⎛<1/2 Corollary Pr{no collisions}≥1/2Pf. Markov ’s inequality for r.v. X ≥0Pr{X ≥t}≤tX E ][ Pr{≥1 collision}≤ 1][#collisions E <1/2Analysis of StorageFor level 1, choose m=n, and let n i be r.v. for # keys that hash to slot i in T. Use n i2 slots for each level 2 table S i.E[total storage]=E[∑-=Θ1 02) (niin]=)(nΘLecture 9Binary-search-tree SortT←Φfor i←1 to ndo Insert(T,A[i])Perform inorder tree walk of TEx A=[3 1 8 2 6 7 5]Tree-walk time=)(nΘHow long to build BST? n2 worst-case Observation: BST sort≡quicksort-Same comparisons, different orderEx A=[3 1 8 2 6 7 5]comparison is the same, but have different comparison order. Node depthDepth=# comparisons during insert Ave. node depth=(∑nodesnode s comparison )/(#)/n=)lg (1n n O n(QS analysis)=O(lgn)Ave. BST height=O(lgn) but this argument doesn ’t show it. Ex:Quicksort BSTAve. node depth nn n n n 2lg ⨯+≤=O(nlgn)/n=O(lgn)to choose2nn ⨯large (not dominate the n n lg ) will make the BST height increase: the thresh h will be nlgnHeight of randomly built BST1. Prove Jensen ’s inequality, which says f(E[X])≤ E[f(X)] for any convex function f and r.v. X2. Analyze “exponential height ” of randomly built BST on n nodes: Y n =nX 2 where X n is r.v. for height.3. Prove that][2n X E ≤]2[n X E =][n Y E =][3n O therefore ][lg ][n O X E n =Def. A function f: R->R is convex if 0,≥∀βα $ 1=+βα, we have)(y x f βα+≤)()(y f x f βα+R y x ∈∀,fConvexity lemmaLet f be convex and let {1α,2α, ... ,n α} be nonnegtive constants $1=∑xα. Then, for any set {1x ,2x , ... ,n x }, we have)()(11∑∑==≤nk k k n k k k x f x f ααhere {1α,2α, ... ,n α}will be probability for us to use Proof: Induction on n.For n=1, 11=α then )()(1111x f x f αα≤ trivial )1)1(()(111∑∑-==--+=n k k nkn n n nk k k x x f x f ααααα ≤)1()1()(11∑-=--+n k k nkn n n x f x f αααα convexity ≤)(1)1()(11∑-=--+n k k nkn n n x f x f αααα I.H.=)(1∑=nk k k x f αJensen ’s inequalityLet f be convex, and x be a r.v. Then f(E[X])≤ E[f(X)]. Proof. f(E[X])=f(∑+∞-∞==k k X k }Pr{)≤∑+∞-∞==k k X k f }Pr{)(=E[f(X)]Analysis of BST heightX n =r.v. denoting height of BST( all perms equally likely)Y n =n X 2Root has rank k X n =1+max{X k-1,X n-k } Y n =2 max{Y k-1,Y n-k } Def. ⎪⎩⎪⎨⎧=01nkZrandom v. indicator , like quick sort Pr{nk Z =1}=E[nk Z ]=1/n Y n =∑=nk nk Z 1k -n 1-k ))Y ,Y max(2(E[Y n ] =E[∑=nk nk Z 1k -n 1-k ))Y ,Y max(2(]=]))Y ,Y max(2([1k -n 1-k ∑=nk nk Z E=]))Y ,Y max(2[(][1k -n 1-k ∑=nk nk E Z E independence≤]Y Y [21k -n 1-k ∑=+nk E n=∑-=1i ]Y [4n i E n Solve Use subst. to show E[Y n ]≤Cn 3 for const C large enough for initial condition.E[Y n ] ≤ ∑-=1034n k Ck n {substitution}if root has rank kotherwise≤⎰n dx x n C 034≤)4(44n n C =Cn 3]2[2][n n X X E E ≤Jensen=E[Y n ]≤ Cn 3therefore E[X n ]≤3lgn+O(1)Why Y n is used instead of X n ? max {a, b}≤a+bwhen b a - gets big, a+b approaches max {a, b} very slowly max{a 2, b 2}≤a 2+b 2when b a - gets big, a 2+b 2 approaches max {a 2, b 2} very quickly, really tight bound.sum of exponential like a 2+b 2is not easy to deal with, but product of exponential is easy, that is where Jensen comes in using convexity of exponentals.Lecture 10Balance-search treesBinary search tree data structure with guaranteed O(lg n) height supporting dynamic set operationsEx: A VL trees, B-treses/ 2-3 trees, red-black treesRed-black treeseach node stores one-bit color fieldthese five loop invariance will maintain the balance1.every node is either red or black2.root is black3.leaves (nil’s) are black4.if a node is red, then its parent is black = no red adjacent wedon’t want too many red, you can have lots of black nodes, black will be good.5. for any node xall simple paths from x to a descendent leaf have same # of black nodes. we do not count red nodes because there are not too many red nodes , so this will do us well.black-height(x)=# black nodes on such a path excluding x (but including nil’s)Ex:black-height (leaves) = 0 black-height(node 8)=1black-height(node 10)=1 black-height(node 7)=2Theorem Red-black tree with n keys has height h 2 lg (n+1) Proof: Book uses induction ->read thoroughlyIntuition: Merge every red nodes into its parents (which is black) Produces a tree in which# children of each node is 0, 2, 3, or 4all leaves have the same depththe height of this tree is h '=2≥4/22/h h ≥' F1bec≤1/2 the nodes on a root-to-leaf path are red# leaves ≤h '4 F2 # leaves ≥h '2F3h=4h ’=2the # of leaves in these two trees is the same , we never merge leaves.# leaves=n+1 F4)1lg(+≤'n h F3+F4 h n ≥+)1lg(2 F1Corollary: queries ’s Search, Min, Max, Successor, and Predecessor all run O(lg n) time.Updates: Insert & Delete cause several changes on tree BST Tree-Insert, Tree-Delete assign colorsrestructure tree by changing pointersrecolor nodesRotations P278 figure 13.2right rotations and left rotations will preserve BST property( preserve in-order traversal order, left is smaller and right is bigger) and takes Θ(1) this will maintain the BST tree and just change a few pointersInsertion Idea:insert x into tree and color red(will preserve the black height)only trouble: x’s parent is redmove violation up in the tree by recolorring until violation can be fixed by O(1) rotations (2) and recolorring.Insert(15)if we only change the color of node 8 and 11 into black, black heightbalance of node 18 will be broken.rotationsCASE 3node 10 and node 18 are all red , after the rotation black height will not be the sameRB-Insert(T, x)Tree-Insert(x)Color[x] ←REDwhile x≠root[T] and color[p[x]]=RED do if p[x]=left[p[p[x]]]then y←right[p[p[x]]] // auntif color[y]=rthenelse if x=right[p[x]]else symmetric with “left” and “right” reversed color[root[T]] ←BLACKcase 1 2 3 P285Extra space n bitsRunning time: O(lg n) worst caseO(1) rotations (at most 2)RB-Delete(x) has same running time。

【算法导论】学习笔记——第14章数据结构的扩张

【算法导论】学习笔记——第14章数据结构的扩张

【算法导论】学习笔记——第14章数据结构的扩张这⼀章节特别有意思。

习题也⽐较多,但是很容易掌握。

主要描述的就是在已知数据结构的基础上,通过增加或修改部分基础操作。

来构造更加有效的新数据结构。

14.1 动态数据统计本节主要介绍如何修改红⿊树,使得可以在O(lgn)时间内确定顺序统计量,如何在O(lgn)时间内确定⼀个元素的秩,即它在集合线性序中的位置。

顺序统计树(order-static tree)T只是简单地在每个结点上存储附加信息的⼀棵红⿊树,附加信息是当前⼦树的size⼤⼩。

源代码如下:1 #include <iostream>2 #include <cstdio>3 #include <cstring>4 #include <cstdlib>5 #include <algorithm>6using namespace std;78#define LOCAL_DEBUG9#define RED true10#define BLACK false1112 typedef struct Node_t {13bool color;14int key, size;15 Node_t *p, *left, *right;16 Node_t() {}17 Node_t(int kkey) {18 key = kkey;19 }20 } Node_t;2122 typedef struct Tree_t {23 Node_t *NIL;24 Node_t *root;25 Tree_t() {26 NIL = new Node_t();27 NIL->key = 0;28 NIL->size = 0;29 NIL->color = BLACK;30 NIL->p = NIL->left = NIL->right = NIL;31 root = NIL;32 }33 } Tree_t;3435 Tree_t *t;3637void Inorder_RBTree_Walk(Tree_t *t, Node_t *x) {38if (x != t->NIL) {39 Inorder_RBTree_Walk(t, x->left);40 printf("key: %d, size: %d\n", x->key, x->size);41 Inorder_RBTree_Walk(t, x->right);42 }43 }4445void Preorder_RBTree_Walk(Tree_t *t, Node_t *x) {46if (x != t->NIL) {47 printf("key: %d, size: %d\n", x->key, x->size);48 Preorder_RBTree_Walk(t, x->left);49 Preorder_RBTree_Walk(t, x->right);50 }51 }5253void Postorder_RBTree_Walk(Tree_t *t, Node_t *x) {54if (x != t->NIL) {55 Postorder_RBTree_Walk(t, x->left);56 Postorder_RBTree_Walk(t, x->right);57 printf("key: %d, size: %d\n", x->key, x->size);58 }59 }6061void Inorder_RBTree_Walk_WithColor(Tree_t *t, Node_t *x) {62if (x != t->NIL) {63 Inorder_RBTree_Walk_WithColor(t, x->left);64 printf("key: %d, size: %d, color: %s\n", x->key, x->size, x->color?"Red":"Black");65 Inorder_RBTree_Walk_WithColor(t, x->right);66 }67 }6871 printf("key: %d, size: %d, color: %s\n", x->key, x->size, x->color?"Red":"Black");72 Preorder_RBTree_Walk_WithColor(t, x->left);73 Preorder_RBTree_Walk_WithColor(t, x->right);74 }75 }7677void Postorder_RBTree_Walk_WithColor(Tree_t *t, Node_t *x) {78if (x != t->NIL) {79 Postorder_RBTree_Walk_WithColor(t, x->left);80 Postorder_RBTree_Walk_WithColor(t, x->right);81 printf("key: %d, size: %d, color: %s\n", x->key, x->size, x->color?"Red":"Black");82 }83 }8485 Node_t *RBTree_Minimum(Tree_t *t, Node_t *x) {86while (x->left != t->NIL)87 x = x->left;88return x;89 }9091 Node_t *RBTree_Maximum(Tree_t *t, Node_t *x) {92while (x->right != t->NIL)93 x = x->right;94return x;95 }9697 Node_t *RBTree_Search(Tree_t *t, int key) {98 Node_t *x = t->root;99while (x!=t->NIL && x->key!=key) {100if (key < x->key) {101 x = x->left;102 } else {103 x = x->right;104 }105 }106return x;107 }108109void Left_Rotate(Tree_t *t, Node_t *x) {110 Node_t *y = x->right;111 x->right = y->left;112if (y->left != t->NIL)113 y->left->p = x;114 y->p = x->p;115if (x->p == t->NIL) {116// x is root117 t->root = y;118 } else if (x == x->p->left) {119 x->p->left = y;120 } else {121 x->p->right = y;122 }123 y->left = x;124 x->p = y;125 y->size = x->size;126 x->size = x->left->size + x->right->size + 1;127 }128129void Right_Rotate(Tree_t *t, Node_t *y) {130 Node_t *x = y->left;131 y->left = x->right;132if (x->right != t->NIL)133 x->right->p = y;134 x->p = y->p;135if (y->p == t->NIL) {136// y is root137 t->root = x;138 } else if (y == y->p->left) {139 y->p->left = x;140 } else {141 y->p->right = x;142 }143 x->right = y;144 y->p = x;145 x->size = y->size;146 y->size = y->left->size + y->right->size + 1;147 }148149void RBTree_Transplant(Tree_t *t, Node_t *u, Node_t *v) {150if (u->p == t->NIL) {151 t->root = v;152 } else if (u == u->p->left) {153 u->p->left = v;155 u->p->right = v;156 }157 v->p = u->p;158 }159160void RBTree_Insert_Fixup(Tree_t *t, Node_t *z) { 161 Node_t *y;162163while (z->p->color == RED) {164// means z->p->p->color == BLACK165if (z->p == z->p->p->left) {166 y = z->p->p->right;167if (y->color == RED) {168 z->p->color = BLACK;169 y->color = BLACK;170 z->p->p->color = RED;171 z = z->p->p;172 } else {173// y->color is BLACK174if (z == z->p->right) {175 z = z->p;176 Left_Rotate(t, z);177 }178 z->p->color = BLACK; // break later 179 z->p->p->color = RED;180 Right_Rotate(t, z->p->p);181 }182 } else {183 y = z->p->p->left;184if (y->color == RED) {185 z->p->color = BLACK;186 y->color = BLACK;187 z->p->p->color = RED;188 z = z->p->p;189 } else {190// y->color is BLACK191if (z == z->p->left) {192 z = z->p;193 Right_Rotate(t, z);194 }195 z->p->color = BLACK; // break later 196 z->p->p->color = RED;197 Left_Rotate(t, z->p->p);198 }199 }200 }201 t->root->color = BLACK;202 }203204void RBTree_Insert(Tree_t *t, Node_t *z) {205 Node_t *y = t->NIL;206 Node_t *x = t->root;207while (x != t->NIL) {208 y = x;209 ++x->size;210if (z->key < x->key) {211 x = x->left;212 } else {213 x = x->right;214 }215 }216 z->p = y;217if (y == t->NIL) {218// tree is empty219 t->root = z;220 } else if (z->key < y->key) {221 y->left = z;222 } else {223 y->right = z;224 }225 z->left = z->right = t->NIL;226 z->color = RED;227 z->size = 1;228 RBTree_Insert_Fixup(t, z);229 }230231void RBTree_Delete_Fixup(Tree_t *t, Node_t *x) { 232 Node_t *w;233234while (x!=t->root && x->color==BLACK) {235if (x == x->p->left) {236 w = x->p->right;237if (w->color == RED) {239 x->p->color = RED;240 Left_Rotate(t, x->p);241 w = x->p->right;242 }243// means w->color == BLACK244if (w->left->color==BLACK && w->right->color==BLACK) {245 w->color = RED;246 x = x->p; // fetch the black in w & x and pass it to x->p247 } else {248if (w->right->color == BLACK) {249// means w->left->color == RED250 w->left->color = BLACK;251 w->color = RED;252 Right_Rotate(t, w);253 w = x->p->right;254 }255// means w->right->color == RED && w->left->color == uncertain 256 w->color = x->p->color;257 x->p->color = BLACK;258 w->right->color = BLACK;259 Left_Rotate(t, x->p);260 x = t->root;261 }262 } else {263 w = x->p->left;264if (w->color == RED) {265 w->color = BLACK;266 x->p->color = RED;267 Right_Rotate(t, x->p);268 w = x->p->left;269 }270// means w->color == BLACK271if (w->left->color==BLACK && w->right->color==BLACK) {272 w->color = RED;273 x = x->p; // fetch the black in w & x and pass it to x->p274 } else {275if (w->left->color == BLACK) {276// means x->right->color == RED277 w->right->color = BLACK;278 w->color = RED;279 Left_Rotate(t, w);280 w = x->p->left;281 }282// means w->left->color == RED && w->right->color = ANY283 w->color = x->p->color;284 x->p->color = BLACK;285 w->left->color = BLACK;286 Right_Rotate(t, x->p);287 x = t->root;288 }289 }290 }291 x->color = BLACK;292 }293294void RBTree_Delete(Tree_t *t, Node_t *z) {295 Node_t *x, *y = z;296 Node_t *q; // q used to back trace for maintening the [size] of Node_t 297bool y_original_color = y->color;298299if (z->left == t->NIL) {300 x = z->right;301 q = y->p;302 RBTree_Transplant(t, y, x);303 } else if (z->right == t->NIL) {304 x = z->left;305 RBTree_Transplant(t, y, x);306 q = y->p;307 } else {308 y = RBTree_Minimum(t, z->right);309 y_original_color = y->color;310 x = y->right;311if (y->p == z) {312 x->p = y;313 q = y;314 } else {315 RBTree_Transplant(t, y, x);316 q = y->p;317 y->right = z->right;318 y->right->p = y;319 }320 RBTree_Transplant(t, z, y);321 y->left = z->left;323 y->color = z->color;324 y->size = z->size;325 }326327// maintence the [size] of Node_t328while (q != t->NIL) {329 --q->size;330 printf("key=%d,size=%d, --\n", q->key, q->size);331 q = q->p;332 }333334if (y_original_color == BLACK)335 RBTree_Delete_Fixup(t, x); // use x replace y336 }337338int check_BHeight(Tree_t *t, Node_t *x, bool &f) {339if (x == t->NIL)340return1;341int lBH = check_BHeight(t, x->left, f);342int rBH = check_BHeight(t, x->right, f);343344if (f == false)345return0;346347if (lBH != rBH)348 f = false;349if (x->color == BLACK)350return lBH+1;351return lBH;352 }353354bool check_RNode(Tree_t *t, Node_t *x) {355if (x == t->NIL)356return true;357if (x->color==RED && (x->left->color!=BLACK || x->right->color!=BLACK)) {358return false;359 }360return check_RNode(t, x->left) && check_RNode(t, x->right);361 }362363bool check_Size(Tree_t *t, Node_t *x) {364if (x == t->NIL) {365return x->size == 0;366 }367368if (x->left->size+x->right->size+1 != x->size) {369 printf("check_size: key=%d, size=%d, wrong\n", x->key, x->size);370 printf("x->left: key=%d, size=%d\n", x->left->key, x->left->size);371 printf("x->right: key=%d, size=%d\n", x->right->key, x->right->size);372return false;373 }374return check_Size(t, x->left) && check_Size(t, x->right) && x->left->size+x->right->size+1==x->size; 375 }376377bool check_RBTree(Tree_t *t) {378bool ret = true;379380if (t->NIL->color != BLACK)381return false;382if (t->root == t->NIL)383return ret;384if (t->root->color != BLACK)385return false;386387 ret = check_RNode(t, t->root);388if (ret == false) {389 puts("not fit B<-R->B");390return false;391 }392393 ret = check_Size(t, t->root);394if (ret == false) {395 puts("size not fit");396return false;397 }398399 check_BHeight(t, t->root, ret);400if (ret == false)401 puts("BHeight not fit");402403return ret;404 }405407int r = x->left->size + 1;408if (r == i)409return x;410else if (r > i)411return OS_Select(t, x->left, i);412else413return OS_Select(t, x->right, i-r);414 }415416int OS_Rank(Tree_t *t, Node_t *x) {417int r = x->left->size + 1;418 Node_t *y = x;419while (y != t->root) {420if (y == y->p->right)421 r += y->p->left->size + 1;422 y = y->p;423 }424return r;425 }426427void init() {428 t = new Tree_t();429int a[] = {26, 17, 41, 14, 21, 30, 47, 10, 16, 19, 21, 28, 38, 7, 12, 14, 20, 35, 39, 3}; 430int n = sizeof(a) / sizeof(int);431 Node_t *p;432433 printf("n = %d\n", n);434for (int i=0; i<n; ++i) {435 p = new Node_t(a[i]);436 RBTree_Insert(t, p);437 }438439 Inorder_RBTree_Walk_WithColor(t, t->root);440if (check_RBTree(t))441 puts("Right");442else443 puts("Wrong");444 printf("\n");445 }446447void test_delete() {448 Tree_t *t = new Tree_t();449int a[] = {26, 17, 41, 14, 21, 30, 47, 10, 16, 19, 21, 28, 38, 7, 12, 14, 20, 35, 39, 3}; 450int n = sizeof(a) / sizeof(int);451bool visit[30];452int i, j, k;453 Node_t *p;454455 printf("n = %d\n", n);456for (i=0; i<n; ++i) {457 p = new Node_t(a[i]);458 RBTree_Insert(t, p);459 }460461 memset(visit, false, sizeof(visit));462for (i=0; i<n; ++i) {463while (1) {464 j = rand()%n;465if (visit[j] == false)466break;467 }468 visit[j] = true;469 p = RBTree_Search(t, a[j]);470 printf("delete %d\n", a[j]);471 RBTree_Delete(t, p);472 Inorder_RBTree_Walk_WithColor(t, t->root);473if (check_RBTree(t))474 puts("Right");475else476 puts("Wrong");477 RBTree_Insert(t, p);478if (check_RBTree(t))479 puts("Right");480else481 puts("Wrong");482 printf("\n\n\n");483 }484 }485486int main() {487488 #ifdef LOCAL_DEBUG489 freopen("data.in", "r", stdin);491#endif492493//init();494 test_delete();495496return0;497 }View Code14.1-514.1-6注意在新的秩的定义下, x.size = x.left.size + 1. 因此,相关函数修改为1void RBTree_Insert(Tree_t *t, Node_t *z) {2 ......3while (x != t->NIL) {4 y = x;5if (z->key < x->key) {6 ++x->size;7 x = x->left;8 } else {9 x = x->right;10 }11 }12 ......13 }1415void Left_Rotate(Tree_t *t, Node_t *x) {16 ......17 y->size += x->left->size;18 }1920void Right_Rotate(Tree_t *t, Node_t *y) {21 ......22 y->size -= x->left->size;23 }2425void RBTree_Delete(Tree_t *t, Node_t *z) {26 ......27while (q != t->NIL) {28if (q == q->p->left)29 --q->size;30 q = q->p;31 }32 }14.1-714.2 如何扩张数据结构扩张⼀种数据结构⼀般分为4个步骤:1. 选择⼀种基础数据结构;2. 确定基础数据结构中要维护的附加信息;3. 检验基础数据结构上的基本修改操作能否维护附加信息;4. 设计⼀些新操作。

《算法导论》摘记经典算法题集锦

《算法导论》摘记经典算法题集锦

《算法导论》摘记经典算法题集锦分治策略Diogenes教授有n个被认为是完全相同的VLSI芯⽚,原则上它们是可以互相测试的。

教授的测试装置⼀次可测⼆⽚,当该装置中放有两⽚芯⽚时,每⼀⽚就对另⼀⽚作测试并报告其好坏。

⼀个好的芯⽚总是能够报告另⼀⽚的好坏,但⼀个坏的芯⽚的结果是不可靠的。

这样,每次测试的四种可能结果如下: A芯⽚报告 B芯⽚报告 结论 B是好的 A是好的 都是好的,或都是坏的 B是好的 A是坏的 ⾄少⼀⽚是坏的 B是坏的 A是好的 ⾄少⼀⽚是坏的 B是坏的 A是坏的 ⾄少⼀⽚是坏的Q:1.证明如果超过n/2个芯⽚是坏的,使⽤任何基于这种逐对检测操作的策略,教授都不能确定哪些芯⽚是好的。

2.如果超过n/2个芯⽚是好的,如何O(n)找出所有好的芯⽚?解:对于问题2,只需找出1个好的芯⽚,就能确定所有的芯⽚的好坏。

粗暴的⽅法是拿⼀个芯⽚与其他所有芯⽚配对,因为好的⽐坏的多,就可以根据多的确定该芯⽚的好坏,直到找到⼀个好芯⽚为⽌。

复杂度是O(n2)的。

同时也说明了Q1(否则⽆法确定好坏) O(n)怎么做?递归缩⼩问题规模。

假设有偶数个芯⽚。

两两配对,有a对检测结果为都是好的,b对检测结果为⼀好⼀坏,c对检测结果为都是坏的。

取a对中每对的任意⼀个芯⽚,组成a个芯⽚的⼦问题。

易证明该⼦问题依然满⾜好的芯⽚数量多于坏的芯⽚。

(因为b+c对中坏的 >= 好的)如果有奇数个芯⽚。

两两配对,有a对检测结果为都是好的,b对检测结果为⼀好⼀坏,c对检测结果为都是坏的。

还有⼀个零头。

1.如果零头是坏的,则a对中好的芯⽚ > 坏的芯⽚,⽤a对去检测零头,发现有⼀半以上的检测结果是坏的,反⾔之⼩于⼀半的检测结果是好的。

2.如果零头是好的,则a对中好的芯⽚ >= 坏的芯⽚,⽤a对去检测零头,发现⼤于等于⼀半的检测结果是好的。

根据结果判断零头是好是坏。

如果是1,把零头弃掉;如果是2,把零头加进来,这样该⼦问题依然满⾜好的芯⽚数量多于坏的芯⽚。

算法导论复习笔记

算法导论复习笔记

《算法导论》复习笔记Chapter 22 基本图算法22、1-1 有向图邻接链表,计算节点出度与入度的时间复杂度?O(V+E)开一个degree[]数组,大小为结点个数,复杂度O(V);遍历邻接链表,经过边uv时,计算出度degree[u]+=1,计算入度degree[v]+=1,复杂度O(E) 22、1-4 将一个多图变成等价无向图,用邻接链表表示,时间复杂度O(V+E)多图就是允许重复边与自循环边的图。

开一个bool数组mark[],大小为节点个数,初始化为false。

复杂度O(V)。

对每个顶点u的邻接链表,遍历,令v为u的边所指向的顶点;如果mark[v]=false,将uv加入新图,并将mark[v]设置为true;否则就跳过。

复杂度O(E)再次遍历u的连边,将mark[v]初始化整体复杂度O(V+E)伪代码:SOLVE(G,G’)1 for each vetex u∈G2 for each v∈ G、Adj[u]3 if mark[v]==false4 mark[v]==true5 Addedge(G’,u,v)6 for each v∈G、Adj[u]7 mark[v]=false22、1-6 图G的邻接矩阵表示,给出一个O(V)的算法来判断有向图G中就是否存在一个通用汇点。

通用汇点指的就是入度|V|-1,但出度为0。

等价问题:给定有向图G的V×V邻接矩阵G,在O(V)时间内判断就是否存在一个数k,使得对所有的i有A[i][k]=1,对所有的j有A[k][j]=0,(i≠k,j≠k)令i与j初值为1,若G[i][j]=0,说明i到j无边,j不可能就是通用汇点,令j=j+1;若G[i][j]=1,说明i到j有边,i不可能就是通用汇点,令i=i+1,循环直到i>|V|或者j>|V|;若i>|V|,则不存在通用汇点,若j>|V|,则检查顶点i就是否满足要求。

算法导论-复习笔记

算法导论-复习笔记

《算法导论》复习笔记Chapter 22 基本图算法22.1—1 有向图邻接链表,计算节点出度和入度的时间复杂度?O(V+E)开一个degree[]数组,大小为结点个数,复杂度O(V);遍历邻接链表,经过边uv时,计算出度degree[u]+=1,计算入度degree[v]+=1,复杂度O(E)22。

1—4 将一个多图变成等价无向图,用邻接链表表示,时间复杂度O(V+E)多图是允许重复边和自循环边的图。

开一个bool数组mark[],大小为节点个数,初始化为false.复杂度O(V)。

对每个顶点u的邻接链表,遍历,令v为u的边所指向的顶点;如果mark[v]=false,将uv加入新图,并将mark[v]设置为true;否则就跳过。

复杂度O(E)再次遍历u的连边,将mark[v]初始化整体复杂度O(V+E)伪代码:SOLVE(G,G’)1 for each vetex u∈G2 for each v∈ G。

Adj[u]3 if mark[v]==false4 mark[v]==true5 Addedge(G’,u,v)6 for each v∈G。

Adj[u]7 mark[v]=false22.1—6 图G的邻接矩阵表示,给出一个O(V)的算法来判断有向图G中是否存在一个通用汇点。

通用汇点指的是入度|V|-1,但出度为0。

等价问题:给定有向图G的V×V邻接矩阵G,在O(V)时间内判断是否存在一个数k,使得对所有的i有A[i][k]=1,对所有的j有A[k][j]=0,(i≠k,j≠k)令i和j初值为1,若G[i][j]=0,说明i到j无边,j不可能是通用汇点,令j=j+1;若G[i][j]=1,说明i到j有边,i不可能是通用汇点,令i=i+1,循环直到i〉|V|或者j>|V|;若i>|V|,则不存在通用汇点,若j>|V|,则检查顶点i是否满足要求。

伪代码:判断是否存在通用汇点 O(V)HAS_UNIVERSL_SINK(G)1 i=j=12 while i≤V and j≤V3 if G[i][j]==14 i=i+15 else j=j+16 if i>V7 return false8 else return CHECK(G,i)CHECK(G,u)1 for each vertex v∈G.V2 if G[u][v]=13 return false4 for each vertex v ∈ G.V5 if G[v][u]==0& u!=v6 return false7 return true检查点u是否是通用汇点【宽度优先搜索】22。

MIT-算法导论-第二章-读书笔记

MIT-算法导论-第二章-读书笔记

MIT-算法导论读书笔记-第二章给出的代码目前也仅仅为解决问题,没有做优化,请见谅,等有时间了我再好好修改。

插入排序算法伪代码INSERTION-SORT(A)1for j←2 to length[A]2do key←A[j]3//Insert A[j] into the sorted sequence A[1..j-1]4i←j-15while i>0 and A[i]>key6do A[i+1] ←A[i]7i←i-18A[i+1] ←keyC#对插入排序算法的实现:public static void InsertionSort<T>(T[] Input) where T:IComparable<T>{T key;int i;for (int j = 1; j < Input.Length; j++){key = Input[j];i = j - 1;for (; i >= 0 && Input[i].CompareTo(key)>0;i-- )Input[i + 1] = Input[i];Input[i+1]=key;}}Insertion-sort的C语言实现:/* insertion-sort(A) 插入排序算法 */#include "stdio.h"void main(){int A[10],i,j,key;printf("pls input 10 numbers!\n>>");for(i=0;i<=9;++i)scanf("%d",&A[i]);for(j=1;j<=9;++j){key=A[j];i=j-1;while(i>0 && A[i]>key){A[i+1]=A[i];i=i-1;}A[i+1]=key;}printf("the sort is:\n");for(i=0;i<=9;++i)printf("%d\n",A[i]);}插入算法的设计使用的是增量(incremental)方法:在排好子数组A[1..j-1]后,将元素A[j]插入,形成排好序的子数组A[1..j]这里需要注意的是由于大部分编程语言的数组都是从0开始算起,这个与伪代码认为的数组的数是第1个有所不同,一般要注意有几个关键值要比伪代码的小1.如果按照大部分计算机编程语言的思路,修改为:INSERTION-SORT(A)1for j←1 to length[A]2do key←A[j]3i←j-14while i>=0 and A[i]>key5do A[i+1] ←A[i]6i←i-17A[i+1] ←key循环不变式(Loop Invariant)是证明算法正确性的一个重要工具。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

《算法导论》复习笔记Chapter 22 基本图算法有向图邻接链表,计算节点出度和入度的时间复杂度O(V+E)开一个degree[]数组,大小为结点个数,复杂度O(V);遍历邻接链表,经过边uv时,计算出度degree[u]+=1,计算入度degree[v]+=1,复杂度O(E)将一个多图变成等价无向图,用邻接链表表示,时间复杂度O(V+E)多图是允许重复边和自循环边的图。

开一个bool数组mark[],大小为节点个数,初始化为false。

复杂度O(V)。

对每个顶点u的邻接链表,遍历,令v为u的边所指向的顶点;如果mark[v]=false,将uv加入新图,并将mark[v]设置为true;否则就跳过。

复杂度O(E)再次遍历u的连边,将mark[v]初始化整体复杂度O(V+E)伪代码:SOLVE(G,G’)1 for each vetex u∈G2 for each v∈ [u]3 if mark[v]==false4 mark[v]==true5 Addedge(G’,u,v)6 for each v∈[u]7 mark[v]=false图G的邻接矩阵表示,给出一个O(V)的算法来判断有向图G中是否存在一个通用汇点。

通用汇点指的是入度|V|-1,但出度为0。

等价问题:给定有向图G的V×V邻接矩阵G,在O(V)时间内判断是否存在一个数k,使得对所有的i有A[i][k]=1,对所有的j有A[k][j]=0,(i≠k,j≠k)令i和j初值为1,若G[i][j]=0,说明i到j无边,j不可能是通用汇点,令j=j+1;若G[i][j]=1,说明i 到j有边,i不可能是通用汇点,令i=i+1,循环直到i>|V|或者j>|V|;若i>|V|,则不存在通用汇点,若j>|V|,则检查顶点i是否满足要求。

伪代码:判断是否存在通用汇点 O(V)HAS_UNIVERSL_SINK(G)1 i=j=12 while i≤V and j≤V3 if G[i][j]==14 i=i+15 else j=j+16 if i>V7 return false8 else return CHECK(G,i)CHECK(G,u)1 for each vertex v∈2 if G[u][v]=13 return false4 for each vertex v ∈5 if G[v][u]==0& u!=v6 return false7 return true检查点u是否是通用汇点【宽度优先搜索】计算无向图BFS后的d值和π值简单,注意初始节点u的π值写NIL或者写-1输入如果是邻接矩阵表示的,BFS的运行时间O(V^2)对于队列中的每一个节点,都要遍历所有的节点来判断是否有边。

举例说明一个有向图G中可能存在这样一个边集Eπ:s到v的唯一简单路径也是一条最短路径,但是无论如何该边集Eπ都不能通过在图G上运行BFS获得。

V={1,2,3,4,5}, E={(1,2),(2,3),(1,4),(4,5),(2,5),(3,4)}, Eπ={(1,2),(2,3),(1,4),(4,5)}, s=1求一棵树T=(V,E)的直径,并分析算法的运行时间。

直径指的是树中所有最短路径的最大值。

两遍BFS就能解决.设v任意一点,BFS(v),令u=v能到达的最远点。

再BFS(u),取w为u能达到的最远点,则u 和w之间的最短路径就是直径。

时间复杂度是O(V+E)。

注意本题的证明。

反证法,设t1到t2是直径,u是v能达到的最远点,但是u不是t1或者t2中的一个,产生矛盾的结论。

【深度优先搜索】给出DFS每个结点的发现时间和完成时间,并给出每条边的分类用栈实现DFS,写出伪代码DFS-VISIT(G,u)1 (u)2 while(!3 u=4 if ==GRAY5 ==BLACK6 time=time+17 =time89 continue10 if ==WHITE11 =GRAY12 time=time+113 =time14 for each v∈G:Adj[u]15 if ==WHITE16 v.π=u17 (v)举出一个反例反驳:有向图G包含u到v的路径,并且DFS时<,则v是u在DFS森林中的一个后代。

V={w,u,v}E={(w,u),(u,w),(w,v)}有一条从u到v的路径,u->w->v,且d[u]<d[v]举出一个反例反驳:有向图G包含u到v的路径,则任意DFS都将导致≤。

例子同上为什么节点u同时有入边和出边,u还是深度优先树中的唯一节点V={w,u,v}E={(u,w),(v,u)}证明:在无向图上使用深度优先搜索来获取图G的连通分量,并且深度优先搜索包含的树的棵数与G的连通分量相同。

也就是说,修改深度优先搜索让每个结点赋予一个介于1和k之间的整数值,k是G的连通分量数。

相同连通分量中的点有相同的。

将DFS_VISIT(G,u)改成DFS_VISIT(G,u,++k),然后在该方法开头添加一句=k。

给出一个算法判断一个有向图是单连通图单连通:图G至多包含一条从u到v的简单路径。

判断是否出现了前向边或者横向边即可。

即分别对每个顶点进行DFS,记录过程中是否访问到黑色的节点。

时间复杂度(V*(V+E))伪代码:SOLVE(G)1 for each vertex u∈2 for each vertex v∈3 =WHITE4 v.π=NIL5 time=06 if(DFS(G,u))7 return false8 return trueDFS(G,u)1 time=time+12 =time3 =GRAY4 for each v∈G:Adj[u]5 if ==WHITE6 v.π=u7 if (DFS(G,v))8 return true9 if ==BLACK10 return true=BLACK12time=time+1=time14return false【拓扑排序】Compute the number of distinct paths from s to t in a directed acyclic graph(要求线性时间复杂度)为每个顶点声明数组dp[ ],dp[v]为s到v的路径数,初始化为0,dp[s]置为1。

进行拓扑排序,在拓扑排序的过程中,每到达一个节点u,其每个相连的节点v都将dp[v]加上dp[u]。

最后dp[t]就是s到t的路径数。

复杂度:O(V+E)给出一个算法来判断给定无向图G=(V,E)是否包含一个环路,复杂度O(V)DFS,访问当前节点的邻接表时,如果存在某个节点已经被标记为访问状态,而且该节点不是当前节点的父亲,则终止DFS,存在环路。

拓扑排序的另一种做法:重复寻找入度为0的结点,输出该结点,将该结点及其发出的边从图中删除。

请解释如何在O(V+E)的时间内实现这种思想。

如果图G包含环路,将会发生什么情况利用队列Queue。

邻接链表存储这个图G。

开一个大小为|V|的degree数组用来存储入度,遍历邻接链表,将各个点的入度存入degree数组中(复杂度O(E))。

从degree中取出入度为0的结点存入队列Q中,通过遍历数组实现(O(V))。

删掉入度为0的点,删除的过程中将该点引出的边也删掉,顺便检测有没有其他点因此变成了入度为0,将这些点加入队列中。

因此到最后所有的点都进过一次队列,复杂度O(V),每条边也都被处理了一遍,复杂度O(E)。

所以O(V+E)。

环路的入度不会为0,边不会被删掉,点不会加入拓扑序中。

给出一个算法判断图G是否是半连通的。

证明算法的正确性并分析运行时间。

对于有向图,任意节点对,存在u到v的路径或者v到u的路径。

这个题和拓扑排序的另一种做法有关。

对半连通图进行拓扑排序过程中,入度为0的点不能同时有2个或者以上。

否则,这两个入度为0的点之间就没有路径了。

因此就用中的算法,要求保持队列中最多只有1个点,如果多于1个就不是半连通的了。

思考题22-3 欧拉回路强连通有向图G=(V,E)中的一个欧拉回路是指一条遍历图G中每条边恰好一次的环路。

这条环路可以多次访问同一个结点。

a.证明:图G有一条欧拉回路当且仅当对于图中每个节点v,有in-degree(v)=out-degree(v)。

b.给出一个复杂度为O(E)的算法来找出图G的一条欧拉回路。

a.证明:=>若强连通有向图G有欧拉回路,则可知对于出发点s,假设有x次从s出,则最后回到s必须恰好有x次,因此对于s,出度和入度必然相等。

假设对于某个非出发点v,出度与入度不相等;假设出度y大于入度x,则第x次从v离开后再也不能回到v,剩余·的y-x条边不能被访问到;假设出度y小于入度x,则第y+1次进入v 后无法出去。

由此可知,对于非出发点v,入度与出度同样相等。

因此G有Euler回路则入度等于出度成立。

<=假设强连通图G的每个结点出度等于入度,则从出发点开始遍历,最终必然会回到出发点s。

因为如果最终没有回到出发点,会有一条s->v1->v2->…->vi的路径,其中vi不等于s,则遍历过程中进入vi的次数比从vi走出的次数多一次,这样就肯定有一条从vi出去的边没有被访问到。

所以不成立。

这样遍历一次后会形成一个子回路,再在这个子回路上某个不同于s点的s1点继续遍历,会形成一个以s1为起始点(也是终止点)的子回路,这两个回路没有公共边,而这两个子回路明显可以合并为一个回路,该回路为s->…->e->s1->f->…->s1->…->s, 这样不断扩展就必然形成一个欧拉回路。

b.从任意点开始DFS并在DFS过程中保存回路上的边。

DFS的复杂度是O(E)的。

设e为连通图G的某条环路上权重最大的边,证明:图G’=(V,E-{e})中存在一棵最小生成树,它也同时是G的最小生成树。

也就是说,G中存在一棵不包含边e的最小生成树。

证明:反证。

假设G中所有最小生成树都包含e。

任取一个这样的最小生成树T,在T上去掉e,将T分为两棵子树T1和T2,T1上顶点集合为V1,T2上顶点集合为V2,则(V1,V2)是一个割。

e所在的圈至少穿越割(V1,V2)两次,C至少有2条边在(V1,V2)中,其中一条边是e。

令e’为除了e之外的另外一条边,则w(e’)≤w(e)。

将e’并到T1和T2上,将T1和T2连接成一棵新的生成树T‘。

由于T’是在T上去掉e、加入e’后形成的,因此w(T’)≤w(T)。

因此,T’也是G的一棵最小生成树,且T‘中不包含e,与假设矛盾。

相关文档
最新文档