
算法导论相关
文章平均质量分 50
wdq347
喜欢数学,热衷于算法研究,钟爱开源软件
展开
-
单连通图(算法导论22.3-12)
注意此处的单连通(singleconnected)指任意两个结点u,v,从u到v至多只有一条简单路径。这个问题作为上星题有难度,对于一次DFS来讲,如果存在forward edge则表明必定不是单连通图,但还要考虑cross edge,如果在一个DFS树来讲只要存在cross edge,就表明不是单连通,关键是要处理不同DFS树间的cross edge还有back edge。 最简单的实现原创 2013-09-04 21:17:21 · 14638 阅读 · 2 评论 -
判断一个图是否为二分图且输出结点二分的两个部分(算法导论22.2-6)
这题实际上是判断图是否为二分图,可以假设此图为连通图(非连通图时对其各个连通分支作相同操作即可)。二分图的充要条件是“所有环路的长度均为偶数”,刚开始我也从这方面着手,努力证明如果此图有环路,则环路长度不可能为奇数,继而发现此路不通,而且如果为二分图还要输出结点的两个部分,这种方法比较困难,使用以下方法证明。对任意一个结点使用BFS,BFS树的根结点设置为红色,与根结点距离为奇数的点设置为原创 2013-07-19 18:09:49 · 2603 阅读 · 0 评论 -
动态规划之整齐打印
考虑在一个打印机上整齐地打印一段文章的问题。输入的正文是n个长度分别为L1、L2、……、Ln(以字符个数度量)的单词构成的序列。我们希望将这个段落在一些行上整齐地打印出来,每行至多M个字符。“整齐度”的标准如下:如果某一行包含从i到j的单词(i 乍一看以为是空格个数和,如此可以使用“贪心算法”求解,即从第一行开始尽可能多的放置单词,可以证明这个“贪心解”也是最优的,假设有一个最优解从某行开原创 2013-07-03 16:51:43 · 4507 阅读 · 2 评论 -
单调队列之广告印刷问题
问题:有n幢建筑,其高度分别为H1,...,Hn,其宽度为1,且这些建筑紧靠在一起,当前需要在这些建筑上刷一个非常大的矩形广告,求矩形广告的最大值。 先翻译成数学题,给定n个正数的序列,定义区间值A(i,j)= Min(Hi,Hi+1,..,Hj) * (j-i+1),求所有区间值的最大值。这个问题网上都有答案,无奈答案一律都相当简单,个人实在无法理解,故成此文!(1) 最简单原创 2013-06-29 17:04:24 · 2009 阅读 · 0 评论 -
单调队列之烽火传递问题
烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情。在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价。为了使情报准确的传递,在m个烽火台中至少要有一个发出信号。现输入n,m和每个烽火台发出的信号的代价,请计算总共最少需要话费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递。 例如原创 2013-07-01 10:07:39 · 2511 阅读 · 2 评论 -
背包问题练习之货币系统
【问题描述】母牛们不但创建了他们自己的政府而且选择了建立了自己的货币系统。[In their own rebellious way],他们对货币的数值感到好奇。传统地,一个货币系统是由1,5,10,20或25,50和100的单位面值组成的。母牛想知道有多少种不同的方法来用货币系统中的货币来构造一个确定的数值。举例来说,使用一个货币系统{1,2,5,10,…}产生18单位面值的一些可能的方法原创 2013-06-29 16:24:21 · 2316 阅读 · 0 评论 -
背包问题练习之邮票
【问题描述】已知一个N枚邮票的面值集合(如,{1分,3分})和一个上限K——表示信封上能够贴K张邮票。计算从1到M的最大连续可贴出的邮资。例如,假设有1分和3分的邮票;你最多可以贴5张邮票。很容易贴出1到5分的邮资(用1分邮票贴就行了),接下来的邮资也不难:6 = 3 + 37 = 3 + 3 + 18 = 3 + 3 + 1 + 19 = 3 + 3 + 310 =原创 2013-06-29 14:47:16 · 1739 阅读 · 0 评论 -
二叉树遍历之morris traversal
刚开始接触到这个是因为算法导论习题10.4-5,其中有一句话说不能改变二叉树结构,即使临时改变也不行,个人就感觉改变二叉树结构也可以进行遍历。搜索的过程中发现了morris 遍历,刚开始根本不相信如此短的代码可以使用无栈、O(1) 空间进行二叉树遍历,但深入之后才发现这个想法是如此的巧妙,感谢J Morris,不知这个Morris 和KMP中的Morris 是否是同一个人?原创 2013-04-26 11:40:32 · 8942 阅读 · 4 评论 -
最长公共子序列(nlogn)
最长公共子序列(LCS)最常见的算法是时间复杂度为O(n^2)的动态规划(DP)算法,但在James W. Hunt和Thomas G. Szymansky 的论文"A Fast Algorithm for Computing Longest Common Subsequence"中,给出了O(nlogn)下限的一种算法。 定理:设序列A长度为n,{A(i)},序列B长度为m,{B(i)}原创 2013-05-31 16:03:56 · 16341 阅读 · 2 评论 -
两次BFS求树的直径(算法导论22.2-7)
以任意点w开始,先做一次BFS,找到最远的点v,然后再以此点v进行一次BFS,找到最远的点为u,u到v就是树的直径。 此问题的关键不是在编程,而是要证明,网上也找了很多资料,没有看到证明,以下是个人的证明方法。 首先要知道树是没有环路的连通图,任意两点都有一条通路,而且也只有一条通路。同时假设树的一条直径为u到v的路径,记为d(u,v)。分情况讨论:(1) 假设起始点w正好原创 2013-07-15 09:08:32 · 5988 阅读 · 0 评论 -
有向无环图两点之间的路径数目(算法导论22.4-2)
有向无环图G=(V,E),求其点s和e之间的路径数目。此题首先要以点s开始作DFS,得到从s为起点的所有可达点的一顶DFS树,但这个路径数目需要详细参详。定义P(x),表示点s到点x的路径数目,P(s)=1,即s到自身有一条路径,其余的所有路径数目都初始化为0。路径从s到达点x,则必须到达x的上一层结点,假设以x为终点的上一层结点有n个,即a1,a2,...,an,由加法定律可知P(x)=原创 2013-08-01 09:47:39 · 11984 阅读 · 0 评论 -
可到达性(算法导论22-4)
有向图G,对每个点赋值一个正整数[1 - |V|],对于V的每个点u,定义R(u)为u点可到达的点集的正整数集合,min(u)为R(u)的最小值,给出O(V+E)的算法,得到所有点的min值首先考虑一个强连通分支内各个点有相同的min值,故可以仅考虑图G的分支图,即有向无环图。然后考虑有向无环图的叶子结点,其min值即为自身值,而其父结点的min值为其所有子结点的min值和自身min值原创 2013-09-07 12:59:41 · 3119 阅读 · 0 评论 -
强连通分支之Tarjan 算法
比Kosaraju算法更神奇,只需要一次DFS就可以获得强连通分支。其原理是每一个强连通分支,均包含在DFS树的某个子树中,只要找到这个子树的树根,再逐一将该连通分支所有结点取出即可。数据结构:1) 栈,DFS访问结点时入栈,某个强连通分支访问结束后其所有结点出栈,以下需要证明当刚好访问结束某个强连通分支时,其所有结点均在栈顶;2) 数组dfn,DFS过程中原创 2013-08-30 18:06:23 · 1661 阅读 · 0 评论 -
算法导论22.5-7 给出一个算法确定一个有向图是否为半连通
半连通的定义,有向图G(V,E),任意两个不同的点u、v,u有一条路径可达v或者v有一条路径可达u,从定义中可以看出,强连通图一定是半连通的。引理:有向无环图G(V,E),G是半连通的当且仅当有一条路径,这条路径上有图G中所有点。证明:充分性很显然,如果有这样一条路径,则任意两个点之间都有一条路径。必要性,有向无环图,可以对其进行拓扑排序得到一个拓扑序列,拓扑序列中任意两个相邻原创 2013-08-16 08:15:30 · 5959 阅读 · 0 评论 -
强连通分支及kosaraju算法
图论中最重要的结构,很多图论问题都可以转化为强连通分支来降低处理复杂度。一个强连通分支中所有的点都是互相连通的,可以将其收缩为单个点,以此来简化图的处理。强连通分支中的点集合是一个最大集合,即再加入任何一个其他点都会导致不连通。 引理1:图G的两个强连通分支C、C’,如果存在点u属于C,u’属于C’,使得(u,u’)为G的一条边,则一定不存在另一条边(v’,v),使得v’属于C’,v属于C原创 2013-08-28 08:24:55 · 2732 阅读 · 0 评论 -
拓扑排序
拓扑排序只针对于有向无环图,它是所有结点的一个线性序列,确保如果有边(u,v),则结点u一定在v前面。引理:有向图有环的充要条件是DFS存在back edge。证明:充分性很好证明,当DFS存在back edge时,比如(v,u),又由于可以沿DFS找到u到v的路径,则找到一个环u=>...=>v=>u;必要性证明,考虑有向图中的某个环u=>...=>v=>u,不失一般性可以假设DFS原创 2013-08-15 12:53:03 · 1193 阅读 · 0 评论 -
欧拉回路问题(算法导论22.2-8 和22-3)
22.3 欧拉回路的算法来自1873年的Hierholzer,前提是假设图G存在欧拉回路,即有向图任意点的出度和入度相同。从任意一个起始点v开始遍历,直到再次到达点v,即寻找一个环,这会保证一定可以到达点v,因为遍历到任意一个点u,由于其出度和入度相同,故u一定存在一条出边,所以一定可以到达v。将此环定义为C,如果环C中存在某个点x,其有出边不在环中,则继续以此点x开始遍历寻找环C’,将环C、C’原创 2013-08-24 14:19:44 · 9737 阅读 · 0 评论 -
DFS 及其相关证明
DFS 对个人来讲真是个奇怪的遍历算法,但最后再仔细看来却是相当重要的算法,难怪在CLRS的chapter notes中讲到由Hopcroft和Tarjan首先意识到DFS的重要性,记住Tarjan这个人,他在图论上有着相当大的贡献,而且首次引入了amortized analysis(分摊分析),这两位同时获得了图灵奖。DFS如此重要,以下几乎全翻译自CLRS,包括部分习题的解答。 顾名思原创 2013-08-12 08:24:51 · 2824 阅读 · 0 评论 -
BFS 最短路径证明及实现
BFS最短路径感觉是显而易见的,但证明却颇费工夫,以下证明大部分摘自CLRS,使用倒序形式进行证明比较好理解。首先需要证明一条引理,即BFS中所有点的d值按照入队列成升序排列,即d(s) 1. BFS得到的是一条路径,即从起始点s到任意一点v的路径d(v),因此它必定大于等于最短路径δ(s,v),即有d(v) >=δ(s,v) 2. 以下只需要证明d(v)>δ(s,v)情况不存在原创 2013-07-27 10:08:47 · 7357 阅读 · 0 评论 -
红黑树扩展
红黑树可以用来解决更多的问题,不仅提供search功能,还可以顺序数等功能。扩展数据结构有以下步骤:(1) 确定用的基础结构(2) 确定增加的域(3) 证明新增域的维护可以在原有基础结构上完成(4) 给出新的操作 算法导论14.1-8 圆内有n条弦,且没有两条弦共享端点,使用O(nlgn)的算法找出在圆内相交弦的对数(感谢网友们的智慧)此题的关键是使用极坐标来表示圆原创 2013-05-14 09:01:56 · 1545 阅读 · 0 评论 -
AVL树实现
AVL 树,相当于在二叉搜索树(BST)中再增加一个平衡因子的变量,用来存储当前结点左右子树高度的差值struct avl_node { struct avl_node *parent; struct avl_node *left; struct avl_node *right; int key; /* the same as bst_node on原创 2013-05-13 11:37:27 · 854 阅读 · 0 评论 -
算法导论15.4-6 最长递增子序列(nlogn)
设序列X(n),最长递增子序列长度为m,考虑长度为i的递增子序列,这种序列有多个,最小的末尾元素记为L(i),可以得到 L(1) 从左到右扫描序列X(n),L(1) 初始为x(1),再引入一个当前最大长度K,初始为1,K表示目前扫描过的序列包含的最长递增子序列的长度,此时L(1)...L(K)有意义。读入一个数据x,如果L(i)是大于x中最小的,则x也是一个递增子序列的末尾元素,按照L(i)的原创 2013-05-27 09:30:07 · 7613 阅读 · 0 评论 -
完全二叉树和堆排序
堆排序算法复杂度虽然也是O(nlgn),但其常数因子较大,而且在实际测试中发现和快速排序、二路归并排序相比速度会慢不少,但堆有其独特的优势:(1) 堆排序仅需要O(1)的空间,这是其他O(nlgn) 排序算法所没有的特点;(2) 堆结构可以以O(1) 的代价取出最大值(最小值),这在优先级队列中得到广泛应用,这些应用不需要完全排序,只需要“快速取出最值”,堆结构在插入、删除、修改时的复杂度原创 2013-04-09 09:48:23 · 1961 阅读 · 0 评论 -
算法导论6.4-4 所有元素均不相同时,最好情况下,堆排序复杂度为Ω(nlgn)
即证明堆排序复杂度不会小于nlgn!考虑堆排序的过程,弹出当前最大元素后,将最长路径序列往上拉一层,再将末尾元素放置到最长路径序列的合适位置。可以看到,由于元素互不相同,除max-heapify的叶子结点外,其余元素均一层一层向根结点靠拢。假设非叶子结点与根结点距离为h,则当此元素弹出时(已经被排好序),恰好被移动h次,序号为i的非叶子结点,与根结点距离为[lgi](以下论原创 2013-04-19 10:46:46 · 1411 阅读 · 0 评论 -
算法导论6.3-3 证明完全二叉树高度为h的结点个数上限「n/2^(h+1)」
这个问题可以使用以下几种方法进行证明: (1) 使用结点高度的定义假设某结点序号为i,则其最长路径为i, i*2, i*2^2,....,i*2^h,而且I*2^(h+1) > n,于是有不等式 i*2^h 而且这个证明方式不仅得到了结点个数的最大值,而且可以精确计算出某高度的结点个数,比如叶子结点个数为[n/2](上取整函数)(2) 使用数学归纳法需要关注原创 2013-04-19 10:40:06 · 6758 阅读 · 2 评论 -
算法导论12.2-8 从任意结点使用后继函数k次的时间复杂度为O(k+h)
从任意结点使用后继函数k次,假设二叉搜索树的高度为h,则时间复杂度为O(k+h)分析从表面上来看,回溯父结点的复杂度为O(h),如果多次回溯,则总体复杂度可能远超O(k+h)再想使用数学归纳法,假设起始结点和回溯结点分别位于某个结点的左右子树,然后发现无法使用替换法证明。最后从12.2-7 中得到一丝信息,即只要证明后继函数k次后,访问结点的总次数为O(k+h)即可原创 2013-04-18 16:39:48 · 3116 阅读 · 1 评论 -
算法导论6.3-3 证明完全二叉树高度为h的结点,其结点个数最多为[n/2^(h+1)](上取整函数)
这个问题可以使用以下几种方法进行证明:(1)使用结点高度的定义假设某结点序号为i,则其最长路径为i, i*2, i*2^2,....,i*2^h,而且I*2^(h+1) > n,于是有不等式 i*2^h而且这个证明方式不仅得到了结点个数的最大值,而且可以精确计算出某高度的结点个数,比如叶子结点个数为[n/2](上取整函数)(2)使用数原创 2023-06-16 11:02:58 · 459 阅读 · 0 评论 -
算法导论12.2-8 从任意结点使用后继函数k次的时间复杂度为O(k+h)
从任意结点使用后继函数k次,假设二叉搜索树的高度为h,则时间复杂度为O(k+h)分析从表面上来看,回溯父结点的复杂度为O(h),如果多次回溯,则总体复杂度可能远超O(k+h)再想使用数学归纳法,假设起始结点和回溯结点分别位于某个结点的左右子树,然后发现无法使用替换法证明。最后从12.2-7 中得到一丝信息,即只要证明后继函数k次后,访问结点的总次数为O(k+h)即可原创 2023-06-16 11:05:29 · 249 阅读 · 0 评论 -
二叉树遍历之递归、父结点、栈
二叉树结构struct bst_node { struct bst_node *parent; struct bst_node *left; struct bst_node *right; int key;};1. 递归这个代码最直接,不需要父结点,不多解释void bst_inorder(struct bst_node *node)原创 2013-04-26 10:30:28 · 1133 阅读 · 0 评论 -
算法导论12.3-4 修改二叉树删除函数,确保结点被引用时删除不会出问题
分析:原有的二叉搜索树删除流程分成两种情况:(1) 当删除结点的度数小于2,即删除结点最多只有一个孩子,直接删除此结点,然后将其父结点指向删除结点的孩子(2) 当删除结点的度数等于2,找到删除结点的后继结点,删除此后继结点,然后将更新删除结点的key和数据为后续结点的相应值,这种删除方法会导致某个key对应的结点指针发生变化,如果结点指针被别的结构引用了,就会出问题原创 2013-04-18 14:03:18 · 1619 阅读 · 0 评论 -
算法导论 14-2 Josephus 排列
n个人排成环,并从1到n编号,给定正整数m,从第一个人开始报数,每次报到m的那个人出列,求人员出列的排列,称为(n,m)-Josephus 排列 a) 假定m的常数,找出O(n)的算法完成(n,m)-Josephus排列最直观的方法,让这n个人组成环形队列,遍历m个结点才能出一个人,(n,m)-Josephus排列需要m*n次遍历,由于m为常数,故此算法为O(n) b) 假定m原创 2013-05-17 09:44:11 · 1117 阅读 · 0 评论 -
Catalan数及二叉树计数
算法导论12-4 N个结点的二叉树,能构成多少种形状不同的二叉树? 这个问题的答案就是著名的Catalan数,以下为详细证明过程:此题还可以通过组合数学的方法解决,在Knuth的著作TAOCP中有非常经典的证明:原题如此描述, n个数在入栈序列一定的情况下,其合法的出栈序列有多少种? 将入栈事件用1表示,出栈事件用0表示,则n个数的出入栈为n个1和原创 2013-05-21 20:02:24 · 4139 阅读 · 0 评论 -
红黑树的删除
删除比插入复杂不少,但深入分析,发现它的关键形状和插入极其类似,记住这个形状!case1,即sibling 结点为红色,不为黑色,这时需要对其parents结点作一次左旋操作,则此时sibling 结点必为黑色;以下的case2 case3 case4中sibling 结点均为黑色case2,sibling 结点的两个子结点均为黑色,此时置sibling 结点为红色,然后再以par原创 2013-05-13 16:25:59 · 680 阅读 · 0 评论 -
treap(树堆)
第一眼看上去是一个非常怪异复杂的结构,实现上感觉也相当困难,但仔细想来却着实简单,它就是一个“二叉搜索树”加“小顶堆”,插入、删除时使用旋转将结点放到合适的位置。 实现void treap_insert(struct treap_node *node, struct treap_root *root){ struct bst_node *p = &node->bnod原创 2013-05-13 14:43:21 · 748 阅读 · 0 评论 -
AVL 树高度和结点数的关系
AVL 树是 平衡树的鼻祖,递归定义,某棵树的左、右子树的高度差不大于1利用线性代数的知识进行分析,请参考"线性代数求解递推形式数列的通项公式"原创 2013-05-13 10:48:07 · 7155 阅读 · 0 评论 -
红黑树插入的栈实现
可以不使用parent 指针,直接使用栈实现,方法是在插入时记下插入结点的所有祖先结点,然后回溯时再使用,以下代码使用数组简单实现栈结构。/* ================================================================== *//* no parent rbtree *//* ============================原创 2013-05-13 15:44:04 · 619 阅读 · 0 评论 -
动态规划之最长公共子序列
15.4-2Q: 不使用数组b完成LCSA: 直接通过数组c和序列即可。从数组c的最右下角开始,输出的字符按照从右到左的顺序填充公共子序列,即最先输出最后一个字符。步骤如下:1. 如果当前两个字符不相等则转步骤2,否则输出该字符并且当前位置往左上方前进一位;2. 查看当前位置的数组c,如果c[i-1][j]>=c[i][j-1]则往上方前进一位,否则往左方前进一位;原创 2012-09-13 18:33:21 · 717 阅读 · 0 评论 -
红黑树及其插入
相当复杂的一个结构,想不通算法大家们是如何找到这种结构的,而且它的添加、删除性能居然比AVL树还要好,真是奇怪啊!红黑树的意图是尽量保证树的平衡性,对于n结点的红黑树,其高度最多为2log(n+1) 红黑树在操作时,最需要关注的是以下两条性质:(1) 红色结点不允许连续,插入时需要关注(2) 从某个结点出发到叶结点,每条路径的blackhight 均相同,删除时需要关注原创 2013-05-13 15:41:31 · 675 阅读 · 0 评论 -
算法导论13.2-5 二叉树T1右旋转换成T2,右旋次数为O(n^2)
此问题个人思考良久,一朝得解,全身放松啊! 证明过程相当简单,计算右旋最多执行的次数,如果其总次数为O(n^2),则命题一样得证。 如果y属于x的右子树,则右旋之后,y仍然属于x的右子树 考虑右旋操作的两个结点,y和y的左子结点x,右旋之后,y成为x的右子结点,以后无论再如何右旋,x都是y的祖先结点,所以x和y无法再在一起参与右旋原创 2013-05-09 19:10:27 · 2066 阅读 · 2 评论 -
二叉排序树的旋转
这是所有树最基础的操作,分为左旋转和右旋转,下图直接取自算法导论 可以看到右旋操作时,y的右子树一直保持不变,左旋操作时,x的左子树一起保持不变 13.2-2证明n 个结点的二叉树,恰好有n-1 个旋转n个结点的二叉树,正好有n-1条边,每一次旋转都对应一条边,所以命题得证 13.2-3左旋操作对于 αβγ深度的影响左旋后,α深度加1,β深度不变,γ深度减1原创 2013-05-09 19:09:18 · 2119 阅读 · 0 评论