分治算法几个经典例子
‘壹’ 程序员都应该精通的六种算法,你会了吗
对于一名优秀的程序员来说,面对一个项目的需求的时候,一定会在脑海里浮现出最适合解决这个问题的方法是什么,选对了算法,就会起到事半功倍的效果,反之,则可能会使程序运行效率低下,还容易出bug。因此,熟悉掌握常用的算法,是对于一个优秀程序员最基本的要求。
那么,常用的算法都有哪些呢?一般来讲,在我们日常工作中涉及到的算法,通常分为以下几个类型:分治、贪心、迭代、枚举、回溯、动态规划。下面我们来一一介绍这几种算法。
一、分治算法
分治算法,顾名思义,是将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治算法一般分为三个部分:分解问题、解决问题、合并解。
分治算法适用于那些问题的规模缩小到一定程度就可以解决、并且各子问题之间相互独立,求出来的解可以合并为该问题的解的情况。
典型例子比如求解一个无序数组中的最大值,即可以采用分治算法,示例如下:
def pidAndConquer(arr,leftIndex,rightIndex):
if(rightIndex==leftIndex+1 || rightIndex==leftIndex){
return Math.max(arr[leftIndex],arr[rightIndex]);
}
int mid=(leftIndex+rightIndex)/2;
int leftMax=pidAndConquer(arr,leftIndex,mid);
int rightMax=pidAndConquer(arr,mid,rightIndex);
return Math.max(leftMax,rightMax);
二、贪心算法
贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。
贪心算法的基本思路是把问题分成若干个子问题,然后对每个子问题求解,得到子问题的局部最优解,最后再把子问题的最优解合并成原问题的一个解。这里要注意一点就是贪心算法得到的不一定是全局最优解。这一缺陷导致了贪心算法的适用范围较少,更大的用途在于平衡算法效率和最终结果应用,类似于:反正就走这么多步,肯定给你一个值,至于是不是最优的,那我就管不了了。就好像去菜市场买几样菜,可以经过反复比价之后再买,或者是看到有卖的不管三七二十一先买了,总之最终结果是菜能买回来,但搞不好多花了几块钱。
典型例子比如部分背包问题:有n个物体,第i个物体的重量为Wi,价值为Vi,在总重量不超过C的情况下让总价值尽量高。每一个物体可以只取走一部分,价值和重量按比例计算。
贪心策略就是,每次都先拿性价比高的,判断不超过C。
三、迭代算法
迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程。迭代算法是用计算机解决问题的一种基本方法,它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。最终得到问题的结果。
迭代算法适用于那些每步输入参数变量一定,前值可以作为下一步输入参数的问题。
典型例子比如说,用迭代算法计算斐波那契数列。
四、枚举算法
枚举算法是我们在日常中使用到的最多的一个算法,它的核心思想就是:枚举所有的可能。枚举法的本质就是从所有候选答案中去搜索正确地解。
枚举算法适用于候选答案数量一定的情况。
典型例子包括鸡钱问题,有公鸡5,母鸡3,三小鸡1,求m钱n鸡的所有可能解。可以采用一个三重循环将所有情况枚举出来。代码如下:
五、回溯算法
回溯算法是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
典型例子是8皇后算法。在8 8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问一共有多少种摆法。
回溯法是求解皇后问题最经典的方法。算法的思想在于如果一个皇后选定了位置,那么下一个皇后的位置便被限制住了,下一个皇后需要一直找直到找到安全位置,如果没有找到,那么便要回溯到上一个皇后,那么上一个皇后的位置就要改变,这样一直递归直到所有的情况都被举出。
六、动态规划算法
动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。
动态规划算法适用于当某阶段状态给定以后,在这阶段以后的过程的发展不受这段以前各段状态的影响,即无后效性的问题。
典型例子比如说背包问题,给定背包容量及物品重量和价值,要求背包装的物品价值最大。
‘贰’ 阒愯堪涓涓鐢熸椿涓鎭ㄦ墍浜呜В镄勭敤鍒嗘不娉曡В鍐抽梾棰樼殑妗堜緥銆
鐢熸椿涓鐢ㄥ垎娌绘硶瑙e喅闂棰樼殑妗堜緥濡备笅锛
镓惧嚭浼甯
缁欎綘涓涓瑁呮湁16涓纭甯佺殑琚嫔瓙銆16涓纭甯佷腑链変竴涓鏄浼阃犵殑锛屽苟涓旈偅涓浼阃犵殑纭甯佹瘆鐪熺殑纭甯佽佽交涓浜涖备綘镄勪换锷℃槸镓惧嚭杩欎釜浼阃犵殑纭甯併备负浜嗗府锷╀綘瀹屾垚杩欎竴浠诲姟锛屽皢鎻愪緵涓鍙板彲鐢ㄦ潵姣旇缉涓ょ粍纭甯侀吨閲忕殑浠鍣锛屽埄鐢ㄨ繖鍙颁华鍣锛屽彲浠ョ煡阆扑袱缁勭‖甯佺殑閲嶉噺鏄钖︾浉钖屻
姣旇缉纭甯1涓庣‖甯2镄勯吨閲忋傚亣濡傜‖甯1姣旂‖甯2杞伙纴鍒欑‖甯1鏄浼阃犵殑锛涘亣濡傜‖甯2姣旂‖甯1杞伙纴鍒欑‖甯2鏄浼阃犵殑銆傝繖镙峰氨瀹屾垚浜嗕换锷°傚亣濡备袱纭甯侀吨閲忕浉绛夛纴鍒欐瘆杈幂‖甯3鍜岀‖甯4銆傚悓镙凤纴锅囧傛湁涓涓纭甯佽交涓浜涳纴鍒椤绘垒浼甯佺殑浠诲姟瀹屾垚銆
锅囧备袱纭甯侀吨閲忕浉绛夛纴鍒欑户缁姣旇缉纭甯5鍜岀‖甯6銆傛寜镦ц繖绉嶆柟寮忥纴鍙浠ユ渶澶氶氲繃8娆℃瘆杈冩潵鍒ゆ柇浼甯佺殑瀛桦湪骞舵垒鍑鸿繖涓浼甯併
鍙﹀栦竴绉嶆柟娉灏辨槸鍒╃敤鍒呜屾不涔嬫柟娉曘傚亣濡傛妸16涓纭甯佺殑渚嫔瓙鐪嬫垚涓涓澶х殑闂棰樸
绗涓姝ワ纴鎶婅繖涓闂棰桦垎鎴愪袱涓灏忛梾棰樸傞殢链洪夋嫨8涓纭甯佷綔涓虹涓缁勭О涓篈缁勶纴鍓╀笅镄8涓纭甯佷綔涓虹浜岀粍绉颁负B缁勚傝繖镙凤纴灏辨妸16涓纭甯佺殑闂棰桦垎鎴愪袱涓8纭甯佺殑闂棰樻潵瑙e喅銆
绗浜屾ワ纴鍒ゆ柇A鍜孊缁勪腑鏄钖︽湁浼甯併傚彲浠ュ埄鐢ㄤ华鍣ㄦ潵姣旇缉A缁勭‖甯佸拰B缁勭‖甯佺殑閲嶉噺銆傚亣濡备袱缁勭‖甯侀吨閲忕浉绛夛纴鍒椤彲浠ュ垽鏂浼甯佷笉瀛桦湪銆傚亣濡备袱缁勭‖甯侀吨閲忎笉鐩哥瓑锛屽垯瀛桦湪浼甯侊纴骞朵笖鍙浠ュ垽鏂瀹冧綅浜庤缉杞荤殑闾d竴缁勭‖甯佷腑銆
链钖庯纴鍦ㄧ涓夋ヤ腑锛岀敤绗浜屾ョ殑缁撴灉寰楀嚭铡熷厛1 6涓纭甯侀梾棰樼殑绛旀堛傝嫢浠呬粎鍒ゆ柇纭甯佹槸钖﹀瓨鍦锛屽垯绗涓夋ラ潪甯哥亩鍗曘傛棤璁篈缁勮缮鏄叠缁勪腑链変吉甯侊纴閮藉彲浠ユ帹鏂杩1 6涓纭甯佷腑瀛桦湪浼甯併傚洜姝わ纴浠呬粎阃氲繃涓娆¢吨閲忕殑姣旇缉锛屽氨鍙浠ュ垽鏂浼甯佹槸钖﹀瓨鍦ㄣ
锅囱鹃渶瑕佽瘑鍒鍑鸿繖涓浼甯併傛妸涓や釜鎴栦笁涓纭甯佺殑𨱍呭喌浣滀负涓嶅彲鍐嶅垎镄勫皬闂棰樸傛敞镒忓傛灉鍙链変竴涓纭甯侊纴闾d箞涓嶈兘鍒ゆ柇鍑哄畠鏄钖﹀氨鏄浼甯併傚湪涓涓灏忛梾棰树腑锛岄氲繃灏嗕竴涓纭甯佸垎鍒涓庡叾浠栦袱涓纭甯佹瘆杈冿纴链澶氭瘆杈冧袱娆″氨鍙浠ユ垒鍒颁吉甯併
杩欐牱16纭甯佺殑闂棰桦氨琚鍒嗕负涓や釜8纭甯侊纸A缁勫拰B缁勶级镄勯梾棰樸傞氲繃姣旇缉杩欎袱缁勭‖甯佺殑閲嶉噺锛屽彲浠ュ垽鏂浼甯佹槸钖﹀瓨鍦ㄣ傚傛灉娌℃湁浼甯侊纴鍒欑畻娉旷粓姝銆傚惁鍒欑户缁鍒掑垎杩欎袱缁勭‖甯佹潵瀵绘垒浼甯併傚亣璁綛鏄杞荤殑闾d竴缁勶纴锲犳ゅ啀鎶婂畠鍒嗘垚涓ょ粍锛屾疮缁勬湁4涓纭甯併
绉板叾涓涓缁勪负B1锛屽彟涓缁勪负B2銆傛瘆杈冭繖涓ょ粍锛岃偗瀹氭湁涓缁勮交涓浜涖傚傛灉B1杞伙纴鍒欎吉甯佸湪B1涓锛屽啀灏咮1鍙埚垎鎴愪袱缁勶纴姣忕粍链変袱涓纭甯侊纴绉板叾涓涓缁勪负B1a锛屽彟涓缁勪负B1b銆傛瘆杈冭繖涓ょ粍锛屽彲浠ュ缑鍒颁竴涓杈冭交镄勭粍銆
鐢变簬杩欎釜缁勫彧链変袱涓纭甯侊纴锲犳や笉蹇呭啀缁嗗垎銆傛瘆杈幂粍涓涓や釜纭甯佺殑閲嶉噺锛屽彲浠ョ珛鍗崇煡阆揿摢涓涓纭甯佽交涓浜涖傝缉杞荤殑纭甯佸氨鏄镓瑕佹垒镄勪吉甯併
镓╁𪾢璧勬枡锛
瑙i樻ラ
鍒嗘不娉曡В棰樼殑涓鑸姝ラわ细
锛1锛夊垎瑙o纴灏呜佽В鍐崇殑闂棰桦垝鍒嗘垚鑻ュ共瑙勬ā杈冨皬镄勫悓绫婚梾棰桡绂
锛2锛夋眰瑙o纴褰揿瓙闂棰桦垝鍒嗗缑瓒冲熷皬镞讹纴鐢ㄨ缉绠鍗旷殑鏂规硶瑙e喅锛
锛3锛夊悎骞讹纴鎸夊师闂棰樼殑瑕佹眰锛屽皢瀛愰梾棰樼殑瑙i愬眰钖埚苟鏋勬垚铡熼梾棰樼殑瑙c
鍙傝冭祫鏂欐潵婧愶细锏惧害锏剧-鍒嗘不绠楁硶
‘叁’ 几种经典算法回顾
今天无意中从箱子里发现了大学时学算法的教材《算法设计与分析》,虽然工作这么几年没在什么地方用过算法,但算法的思想还是影响深刻的,可以在系统设计时提供一些思路。大致翻了翻,重温了一下几种几种经典的算法,做一下小结。分治法动态规划贪心算法回溯法分支限界法分治法1)基本思想将一个问题分解为多个规模较小的子问题,这些子问题互相独立并与原问题解决方法相同。递归解这些子问题,然后将这各子问题的解合并得到原问题的解。2)适用问题的特征该问题的规模缩小到一定的程度就可以容易地解决该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题3)关键如何将问题分解为规模较小并且解决方法相同的问题分解的粒度4)步骤分解->递归求解->合并 divide-and-conquer(P) { if ( | P | <= n0) adhoc(P); //解决小规模的问题 divide P into smaller subinstances P1,P2,...,Pk;//分解问题 for (i=1,i<=k,i++) yi=divide-and-conquer(Pi); //递归的解各子问题 return merge(y1,...,yk); //将各子问题的解合并为原问题的解 }google的核心算法MapRece其实就是分治法的衍生5)分治法例子:合并排序规约过程:动态规划1)基本思想将待求解问题分解成若干个子问题,但是经分解得到的子问题往往不是互相独立的,如果能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算2)适用问题的特征最优子结构在递归计算中,许多子问题被重复计算多次3)步骤找出最优解的性质,并刻划其结构特征。递归地定义最优值。以自底向上的方式计算出最优值。根据计算最优值时得到的信息,构造最优解。贪心算法1)基本思想贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择2)适用问题的特征贪心选择性质,即所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。最优子结构性质3)步骤:不断寻找局部最优解4)例子:找硬币,哈夫曼编码,单源最短路径,最小生成树(Prim和Kruskal) 最小生成树图示:回溯法1)基本思想在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索2)适用问题的特征:容易构建所解问题的解空间3)步骤定义问题的解空间 确定易于搜索的解空间结构以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索 4)回溯法例子:N皇后问题分支限界法1)基本思想分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。 在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。2)分支限界法例子:单源最短路径问题问题描述:在下图所给的有向图G中,每一边都有一个非负边权。
‘肆’ 分治算法几个经典例子
分治法,字面意思是“分而治之”,就是把一个复杂的1问题分成两个或多个相同或相似的子问题,再把子问题分成更小的子问题直到最后子问题可以简单地直接求解,原问题的解即子问题的解的合并,这个思想是很多高效算法的基础。
图二
大整数乘法
Strassen矩阵乘法
棋盘覆盖
合并排序
快速排序
线性时间选择
最接近点对问题
循环赛日程表
汉诺塔
‘伍’ 什么叫算法算法有哪几种表示方法
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。计算机科学家往往将“算法”一词的含义限定为此类“符号算法”。“算法”概念的初步定义:一个算法是解决一个问题的进程。而并不需要每次都发明一个解决方案。
已知的算法有很多,例如“分治法”、“枚举测试法”、“贪心算法”、“随机算法”等。
(5)分治算法几个经典例子扩展阅读
算法中的“分治法”
“分治法”是把一个复杂的问题拆分成两个较为简单的子问题,进而两个子问题又可以分别拆分成另外两个更简单的子问题,以此类推。问题不断被层层拆解。然后,子问题的解被逐层整合,构成了原问题的解。
高德纳曾用过一个邮局分发信件的例子对“分治法”进行了解释:信件根据不同城市区域被分进不同的袋子里;每个邮递员负责投递一个区域的信件,对应每栋楼,将自己负责的信件分装进更小的袋子;每个大楼管理员再将小袋子里的信件分发给对应的公寓。