算法系列之常用算法之一----分治算法

一、基本概念

在计算机科学中,分治法是一种很重要的算法。分治算法,字面上的解释是“分而治之”,分治算法主要是三点:

1.将一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题----“分”

2.将最后子问题可以简单的直接求解----“治”

3.将所有子问题的解合并起来就是原问题打得解----“合”

这三点是分治算法的主要特点,只要是符合这三个特点的问题都可以使用分治算法进行解决(注意用词,是”用”,至于好不好就是另外一回事了)

二、分治法的特征

分治法所能解决的问题一般具有以下几个特征:

1) 该问题的规模缩小到一定的程度就可以容易地解决

2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。

3) 利用该问题分解出的子问题的解可以合并为该问题的解;

4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;

第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、

第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。

第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好

三、为什么用分治法?怎么正确使用分治法?

为什么用分治算法?我们使用一种算法的原因大部分情况下都是为了”快“,只有在少数情况下,在程序已经足够”快“的前提下,我们才会牺牲一部分的”快“,去保全一些开发因素(比如,程序的可维护性等等),那么分治算法为什么快?我们在用这个算法之前必需理解清楚这个问题。

分治算法的思想就是将一个问题规模比较大的问题划分为几个相同逻辑性质(或者直接理解为类似)的问题规模变小的子问题。我们可以从这里入手。

举个超级简单的例子:

假如有一个存在n个元素的int型数组,我们需要求该数组的和。

可能有些人想不想就是一个分治算法,将这个问题分为两个子问题,然后每个子问题再分为两个子问题,当子问题的规模为只有两个数时进行相加。。。

然而,这种办法是使用了分治算法,可是效率比直接遍历一遍相加得到的效率还要低的多.

为什么?因为分治算法本身不适合这种单次遍历就可以搞定的简单问题。你们在阅读一遍分治算法的思想:分治算法的思想就是将一个问题规模比较大的问题划分为几个相同逻辑性质的问题规模变小的子问题,那么这个定义存在一个隐含的前提,当问题规模比较大时,该问题解决起来要成倍的困难!

我们可以举这样一个简单的例子:
我们对一个存在n个元素的数组,使用简单排序进行排序时:

当n=1时,无需比较

当n=2时,我们需要1次比较

当n=3时,我们需要3次比较

当n=4时,我们需要6次比较

当n的数值比较大时,我们需要比较的次数越来越多将会是一个巨大的数字。

而对于前面的求和的例子:
当n=1时,无需相加

当n=2时,我们需要1次相加

当n=3时,我们需要2次相加

当n=4时,我们需要3次相加

仔细观察这组数据,是否发现了什么?

对于求和的例子来说,该问题的计算量与问题规模成正比,在相同的条件下,我们根本无须使用分治算法,因为即使这个问题规模变大,他的解决问题的难易程度没有丝毫改变,它所付出的,只不过是增大了问题规模后所必须付出的计算量,概括起来就是线性增长的问题规模导致了线性增长的计算量。

而对于排序的例子,当问题规模变大时,计算量的增大是成幂次型增长的,概括起来就是线性增长的问题规模导致了幂次型计算量的增长。使得问题规模大的问题解决起来更加困难。

综合起来概括,在问题规模与计算量成正比的算法中,分治算法不是最好的解法,并且有可能是效率极其底下的算法。如果存在某个问题,线性增长的问题规模可能带动计算量的非线性增长,并且符合分治算法的三个特征,那么分治算法是一个很不错的选择。

四、实例解析

1.二分查找

实例分析:也许有人看到这里认为二分查找算法与我前面的推论是矛盾的,但其实并不矛盾。不矛盾的原因在于二分查找算法是存在前提条件的。二分查找的数组必须是有序状态的,二分查找是根据它的规则特性人们找到的一种取巧的方法。对于一个无序的查找方法,我的结论依然有效。

代码来一波:

2.棋盘覆盖

问题描述: 一个2^k×2^k 个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

实例分析:每次都对分割后的四个小方块进行判断,判断特殊方格是否在里面。这里的判断的方法是每次先记录下整个大方块的左上角(top left coner)方格的行列坐标,然后再与特殊方格坐标进行比较,就可以知道特殊方格是否在该块中。如果特殊方块在里面,这直接递归下去求即可,如果不在,这根据分割的四个方块的不同位置,把右下角、左下角、右上角或者左上角的方格标记为特殊方块,然后继续递归。在递归函数里,还要有一个变量s来记录边的方格数,每次对方块进行划分时,边的方格数都会减半,这个变量是为了方便判断特殊方格的位置。其次还要有一个变nCount来记录L型骨牌的数量。

代码秀:

运行结果:

3.归并排序/合并排序

实例分析:咳咳,大名鼎鼎的合并排序不用我多说吧。。。缺点是需要多占一些内存充当缓冲区。。

上代码:

4.快速排序

运行结果:有了合并排序怎么可以没有快速排序咧

代码:

时间: 2024-08-05 19:32:40

算法系列之常用算法之一----分治算法的相关文章

五种常用的算法设计技巧之二:分治算法

一,介绍 分治算法主要包含两个步骤:分.治.分,就是递归地将原问题分解成小问题:治则是:在解决了各个小问题之后(各个击破之后)合并小问题的解,从而得到整个问题的解 二,分治递归表达式 分治算法一般都可以写出一个递归表达式:比如经典的归并排序的递归表达式:T(N)=2T(N/2)+O(N) T(N)代表整个原问题,采用了分治解决方案后,它可以表示成: ①分解成了两个规模只有原来一半(N/2)的子问题:T(N/2) ②当解决完这两个子问题T(N/2)之后,再合并这两个子问题需要的代价是 O(N) 递

算法系列笔记8(有关图的算法二—最短路径问题)

图的最短路径问题主要分为两类,单源最短路径问题和全对最短路径问题.单源最短路径问题指给点单个源点,求其到所有其它顶点之间的最短距离.而全对最短路径问题指所有顶点之间的最短路劲问题.此外对于单对最短路径问题,从渐进意义上来看,目前还没有比最好的单元算法更快的算法来解决这一问题. 一:单源最短路径问题 单源最短路劲问题根据其权重分为四类,当图G=(V,E)为无权图,直接使用广度优先遍历(这里不做介绍):当权值为非负值,则使用Dijkstra算法:存在负权值及负权环,可以使用Bellman-Ford算

算法系列笔记10(有关图的算法三—最大流与二分图)

本次主要记录流网络以及最大流的简单概念(以后可能会将最大流的实现算法补充),重点讲解用匈牙利算法来求二分图的最大匹配. 1:流网络 流网络是G(V, E)是一个有限的有向图,它的每条边(u, v)∈E都有一个非负值实数的容量c(u, v)≥0.如果(u, v)不属于E,我们假设c(u, v) = 0.我们区别两个顶点: 一个源点s和一个汇点t..并假定每个顶点均处于从源点到汇点的某条路径上. 形式化的定义:一道网络流是一个对于所有结点u和v都有以下特性的实数函数::满足下面两条性质: 容量限制:

[算法系列之二十七]Kruskal最小生成树算法

简介 求最小生成树一共有两种算法,一个是就是本文所说的Kruskal算法,另一个就是Prime算法.在详细讲解Kruskal最小生成树算法之前,让我们先回顾一下什么是最小生成树. 我们有一个带权值的图,我们要求找到一个所有生成树中具有最小权值的生成树.如下图所示,T是图G的生成树.但不是具有最小权值的生成树. 我们可以把他们想象成一组岛屿和连接它们的可能的桥梁.当然修桥是非常昂贵和费时的,所以我们必须要知道建设什么样的桥梁去连接各个岛.不过有一个重要的问题,建设这样一组连接所有岛屿的桥梁的最低价

算法系列笔记6(有关图的算法一—搜索,拓扑排序和强连通分支)

简单概念:对于图G(V,E),通常有两种存储的数据结构,一种是邻接矩阵,此时所需要的存储空间为O(V^2):第二种是邻接表,所需要的存储空间为O(V+E).邻接表表示法存在很强的适应性,但是也有潜在的不足,当要快速的确定图中边(u,v)是否存在,只能在顶点u的邻接表中搜索v,没有更快的方法,此时就可以使用邻接矩阵,但要以占用更多的存储空间作为代价:此外当图不是加权的,采用邻接矩阵存储还有一个优势:在存储邻接矩阵的每个元素时,可以只用一个二进位,而不必用一个字的空间. 图的搜索算法 搜索一个图示有

数据结构与算法系列研究七——图、prim算法、dijkstra算法

图.prim算法.dijkstra算法 1. 图的定义 图(Graph)可以简单表示为G=<V, E>,其中V称为顶点(vertex)集合,E称为边(edge)集合.图论中的图(graph)表示的是顶点之间的邻接关系. (1) 无向图(undirect graph)      E中的每条边不带方向,称为无向图.(2) 有向图(direct graph)      E中的每条边具有方向,称为有向图.(3) 混合图       E中的一些边不带方向, 另一些边带有方向.(4) 图的阶      指

分治算法(一)

当我们求解某些问题时,由于这些问题要处理的数据相当多,或求解过程相当复杂,使得直接求解法在时间上相当长,或者根本无法直接求出.对于这类问题,我们往往先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法.如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止.这就是分治策略的基本思想. 1.引例: 如果给你一个装有16枚硬币的袋子,其中有一枚是伪造的,并且那枚伪造硬币的重量和真硬币的重量不同.你能不能用最少

【万字博文】分析与设计:插入排序和分治算法、递归和迭代的探讨

插入排序及其解决思路 算法的作用自然不用多说,无论是在校学生,还是已经工作多年,只要想在计算机这条道路走得更远,算法都是必不可少的. 就像编程语言中的"Hello World!"程序一般,学习算法一开始学的便是排序算法.排序问题在日常生活中也是很常见的,说得专业点: 输入是:n个数的一个序列<a1,a2,...,an?1,an> 输出是:这n个数的一个全新的序列<a,1,a,2,...,a,n?1,a,n>,其特征是a,1≤a,2≤...≤a,n?1≤a,n 举

程序员常用的10个算法

1.二分查找算法(非递归)  此篇写的是非递归算法,递归的在之前的查找算法中写过了. 1.1 算法的适用条件 二分查找只适用于从有序的数列中进行查找(比如数字和字母等),将数列排序后在查找. 1.2算法的效率: 时间复杂度为O(log2 n) 实例:使用二分查找的非递归形式对数组{1 3 8 10  11 67 100}进行查找 public class BinarySearchNoRecur { public static void main(String[] args) { //测试 int