算法--分治思想的运用

前言:上次算法课主要对分治思想进行了介绍,在这里进行以下总结和几个例子的应用。

一、分治算法

设计过程:(1)分解:将问题分解为子问题,子问题的形式与原问题是一样的,只是规模减小了。

(2)求解:递归地求解出子问题。

(3)合并:将子问题的解组合成原问题的解。

分治算法中最重要的就是递归求解子问题,求解递归式估计出算法的复杂度一般可以有三种方式:

(1)代入法:猜测一个界,然后用数学归纳法证明这个界是正确的。

(2)递归树法:将递归式转为一棵树,其结点表示不同层次的递归调用产生的代价。

(3)主方法:即Master定理,主要求解以下形式的递归式:T(n)=aT(n/b)+f(n);该方法有三种情形,只要记住这三种情形,可以很快速的求解出递归式:

【1】若对某个常数€>0有f(n)=O(nlogba-€)则T(n)=Θ(nlogba);

【2】若f(n)=Θ(nlogba),则T(n)=Θ(nlogbalgn);

【3】若f(n)=Θ(nlogba+€),则T(n)=Θ(f(n));

二、应用例子:

(1)设X[0:n-1]和Y[0:n-1]为两个数组,每个数组中含有n个已排好序的数,设计一个算法复杂度为O(logn)的分治算法,找出X和Y中2n个数中的中位数。(中位数:个数为奇数:中间位置上的数;个数为偶数,中间两个数的平均数)

思路:对于两个已排好序的数组,可以寻找两个数组中的中位数,只需要进行n次的比较,时间复杂度可以为O(n),代码如下

int main(void)
{
  int x[] = { 2, 4, 5, 11, 12, 13, 14 };
  int y[] = { 1, 3, 6, 7, 8, 9, 10 };
  int a1, a2, n1, n2, n;
  n = n1 = n2 = 0;
  while (n < 7)
  {
    a1 = x[n1] <= y[n2] ? x[n1++] : y[n2++];
    n++;
  }
  a2 = x[n1] <= y[n2] ? x[n1++] : y[n2++];
  printf("%lg\n", (double)(a1 + a2) / 2);

  return 0;
}

使用分治法设计算法的思路如下:

1.分别取两个数组的中位数:若n为奇数,则x=X[n/2],y=Y[n/2];若n为偶数,则x,y分别为两个数组中间两个数的平均数。并设两个数组的左右边界分别为:XLeft,XRight;YLeft,YRight.

2.比较两个中位数的大小:

  (1)若x<y:则舍去x之前的数和y之后的数,形成新的子数组:X[n/2:XRight],Y[YLeft:n/2]

  (2)若x>y:则舍去x之后的数和y之前的数,形成新的子数组:X[XLeft:n/2],Y[n/2:YRight]

  (3)若x=y:则该数即是中位数,直接输出即可。

3.若是步骤2中的前两种情形,则进行递归,每次都筛去一半的数,直至两个子数组的长度为1,递归结束,输出数组中两个数的平均数。

复杂度分析:

T(n)=O(1)  n=1;T(n)=T(n/2)+1  n>1;

使用Master定理,f(n)=nlog2=1,所以T(n)=O(logn)。

(2)有一实数序列a1,a2,....an,若i<j且ai>aj,则(ai,aj)形成了一个逆序对,请使用分治算法求整个序列中逆序对个数,并分析算法时间复杂度。

思路:看到这个问题的第一个想法是建立两层循环:

int i,j;

int count=0;

for(i=1;i<=n;i++)

{

  for(j=i+1;j<=n;j++)

  {

    if (ai>aj)

    count++;

  }

}

这种算法的时间复杂度应该为O(n2)

分治算法设计思路:将该实数序列分解为两个等大的子序列,则逆序对存在于左序列、右序列及左右序列之间,递归求解左序列、右序列中的逆序对,而左右序列之间的逆序对需要合并,若直接进行合并,则遍历需要的时间复杂度为(n/2)*(n/2),复杂度并未得到改善,所以需要对合并进行优化,若对两个排好序的左右序列进行合并,则不影响逆序对的结果,并且只需要遍历n次,即合并的时间复杂度为O(n).

过程:

[1].分解:将原序列分为两个等大的实数序列,即左序列,右序列

[2].求解:递归求解左序列、右序列中的逆序对

[3].合并:在合并的过程中排序并计算逆序对的个数。比如两个排好序的数组{1,3,4,6}和{2,7,8,9}:1<2,左序列中往后移;3>2,则说明左序列中后面的数都大于2,所以有3个逆序对(3,2)(4,2)(6,2),右序列中指针向后移;3<7,左序列中往后移,4<7,往后移,6<7,往后移,至此已经没有逆序对了。

仔细想这个过程和归并排序非常像,只是在合并时计算逆序对的个数。

时间复杂度分析:

T(n)=2T(n/2)+O(1)+O(n);依次为求解子问题的代价,分解时的代价,及合并时的代价。利用Master定理:a=2,b=2,nlog2=n,f(n)=n,所以T(n)=O(nlogn)

(3)求解天际线问题

给定n座建筑物B[1,2,...,n],每个建筑物B[i]表示为一个矩形,用三元组B[i]=(ai,bi,hi)表示,其中ai表示建筑左下顶点,bi表示建筑的右下顶点,hi表示建筑的高,请设计一个O(nlogn)的算法求出这n座建筑物的天际轮廓。例如,左下图所示中8座建筑的表示分别为(1,5,11),(2,7,6),(3,9,13),(12,16,7),(14,25,3),(19,22,18),(23,29,13)和(24,28,4),其中天际轮廓如右下图所示可用9个高度的变化(1,11),(3,13),(9,0),(12,7),(16,3),(19,18),(22,3),(23,13)和(29,0)表示。另举一个例子,假定只有一个建筑物(1,5,11),其天际轮廓输出为2个高度的变化(1,11),(5,0)。

思路:若只有一个建筑,那么输出的点一定是建筑的左上和右下顶点,根据这个思路,使用分治法解决。

算法描述:

  1. 取mid=n/2(n为建筑的总数目)为分界线,将原有所有的建筑划分为左建筑集合(0,mid)右建筑集合(mid+1,n-1),将原问题一分为二进行递归
  2. 当建筑数量为1时:返回两个点:p1=左上顶点;p2=右下顶点
  3. 合并:

(1)定义三个变量hl,hr,h,全部初始化为0,分别记录左集合、右集合及总体高度的变化值;

(2)遍历左建筑点L和右建筑点R:选取横坐标较小的点,若该点在左集合中,则hl为该点的纵坐标,比较hl与hr,选取二者的较大值替换该点的纵坐标,再与h进行比较,若纵坐标与h相等,则舍弃该点;若纵坐标与h不等,则保留该点,且当纵坐标大于h时,将h替换为纵坐标的值,h始终为高度的较大值。

(3)当左集合或右集合遍历完毕时,另一方剩余的点直接取到。

可以用一个例子模拟该过程进栈出栈的过程:

建筑群集合为(1,5,11)(2,7,6):

  1. 经过分解,取到每个建筑的左上顶点和右下顶点:(1,11)(5,0);(2,6)(7,0);
  2. 进行合并:

hl=0,hr=0,h=0;

①left[0].x=1<right[0].x=2;(1,11)进栈。此时hl=11,hr=0,h=max(hl,hr)=11;保留该点;左集合中右移left[1]。

②left[1].x=5>right[0].x=2;(2,6)暂时进栈;此时hr=6,hl=11,h=11;则将right[1].y=max(hl,hr)=11;此时right[1].y=h:舍弃该点,(2,11)出栈;右集合右移,right[1]

③left[1].x=5<right[1].x=7;(5,0)暂时进栈;此时hr=0,hl=11,h=11;则left[1].y=max(hl,hr)=11;此时left[1].y=h:舍弃该点,(5,11)出栈。左集合右移

④左集合遍历完毕,直接将右集合中剩余的点入栈

(4)POJ3714:在与联邦的战斗接连失败后,帝国撤退到最后的据点。帝国依靠其强大的防御系统,击退了六次联合进攻。在经历了几个不眠之夜后,阿瑟将军注意到防御系统的唯一弱点是它的能源供应。该系统由N个核电站负责,并拆除其中任何一个将使该系统失效。这位将军很快就开始了对这些电台的突袭,这些特工被带进了要塞。不幸的是,由于帝国空军的袭击,他们未能到达预期的位置。作为一名经验丰富的将军,亚瑟很快意识到他需要重新安排这个计划。他现在想知道的第一件事是,哪个代理是离任何发电站最近的。你能不能帮助将军计算一个代理和一个站之间的最小距离?

【思路】:此题是对最近点对问题的一个应用,最近点对问题中最后的解可能出现在三种情形中:左问题集合;右问题集合;一个点在左集合,一个点在右集合中。而POJ3714的这道题目相当于对最后的解进行了限制:必须是两个集合中的点,所以我们只需在求最近点对的问题上加上一个限制:判断最后求得的两个点是否处于两个集合中,判断时可使用“标志位”。具体的算法可以使用两种方法:

【1】只进行了x轴预排序

f(Q,i,j)//Q已经按x轴进行了排序

{

  if(j-i<=c)

  {

    直接计算距离,比较得到最小距离;

    return;

  }

  di=f(Q,i,(i+j)/2-1);

  dj=f(Q,(i+j)/2,j);

  d=min{di,dj};

  A:临界区,存入中位线范围d的所有点;

  对A中的所有点按y轴排序;//该过程每次调用都对y进行排序,时间复杂度较大

  计算A中点间的距离;

}

【2】空间换时间,对集合中的点进行X,Y预排序

算法描述如下:

(1)输入点集合sa[2n]:为station[n]集合中的点加上标志flag=ture,为agent[n]中的点加上标志flag=false。

(2)预排序:对sa集合中的点按X轴从小到大排序;按y轴从小到大排序并记录下标值。(通过预排序用空间换时间,降低算法的时间复杂度)

(3)递归调用求解最近点对的方法

  1. 集合中只有一个点,不进行计算
  2. 集合中有两个点,判断两个点是否属于同一集合:是--计算距离;否--不进行计算。
  3. 集合中多于两个点,按x轴排序计算中位数然后将原集合分解为两个规模基本一致的子集合,递归调用。

对于临界区中的点,因为已经进行了y轴预排序,所以直接进行扫描,对每一个点计算按y轴排序后面的6个点(鸽巢原理)与它的距离,并与两个子集合中的最小距离进行比较。

原文地址:https://www.cnblogs.com/wangjm63/p/9748749.html

时间: 2024-11-09 03:48:10

算法--分治思想的运用的相关文章

经典算法宝典——分治思想(四)(1)

分治法(Divide and Conquer)的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的几个相似问题,以便各个击破,分而治之. 说明: 分治策略的应用很广,具体表现形式各异,比如:折半查找.合并排序.快速排序.二叉树遍历(先遍历左子树再遍历右子树).二叉排序树的查找等算法. 一.分治算法框架 1.算法设计思想 分治法求解问题的过程是,将整个问题分解成若干个小问题后分而治之.如果分解得到的子问题相对来说还太大,则可反复使用分治策略将这些子问题分成更小的同类型子问题,直至产生出方

排序算法大集锦_二路归并排序_2&3(分治思想)

第一段代码和合并排序差不多,用它来和第二段代码--二路归并排序作对比. 这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好,所以这些博客就算是对自己的总结吧. #include <stdio.h> #include <limits.h> #include <malloc.h> void merge(int *m, int x, int y, int z) { int b1,b2,i,j,k; b1=y

五大算法基本思想—分治,动态规划,贪心,回溯,分支界限

一.算法理解 算法是什么,即是按照一定的步骤,一步步去解决某个问题,解决问题的方法步骤就称为算法,例如数学中我们学过的做一个运算,解一个方程,等等,都需要有一个清晰的思路,一步步地去完成.可以说算法就在身边. 算法和计算机有什么关系,计算机它是机器,没有人类的大脑可以思考,但是它怎么完成我们交给他的人物的呢,就是通过算法(当然是人为预先设计好的),计算机解决任何问题都要依赖于算法,没有算法也就没有计算机. 为了计算机能更好更有效率的运行,算法就必须足够好,既要正确易理解,又要可靠效率.下面来研究

分治思想在Trimino拼图中的Java实现

近期学习<算法设计与分析基础 第二版>,学习到了分治法,被课后习题吸引了,即Trimino拼图问题.想了好久,都没有想到如何去分而治之.然后就是Google到了相关的PPT.一看就明白了.自己就用代码实现了下.理解思想后,代码实现挺容易的. 这个谜题实际上可以做成一个小益智游戏. 分治思想的关键就在于构造相同的子问题.这是这个思想核心看似简单,实则需要对问题的洞察力.看到的PPT核心图如下: 算法的源代码如下: /**  * @author shuaiguangying  * 用分治法解决棋盘

五大算法—分治算法

分治算法 一.基本概念 在计算机科学中,分治法是一种很重要的算法.字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并.这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)…… 任何一个可以用计算机求解的问题所需的计算时间都与其规模有关.问题的规模越小,越容易直接求解,解题所需的计算时间也越少.例如,对于n个元素的排序问题,当n=1时

一次性弄懂到底什么叫做分治思想(含有大量经典例题,附带详细解析)

期末了,通过写博客的方式复习一下算法,把自己知道的全部写出来 分治:分而治之,把一个复杂的问题分解成很多规模较小的子问题,然后解决这些子问题,把解决的子问题合并起来,大问题就解决了 但是我们应该在什么时候用分治呢?这个问题也困扰了我很久,做题的时候就不知道用什么算法 能用分治法的基本特征: 1.问题缩小到一定规模容易解决 2.分解成的子问题是相同种类的子问题,即该问题具有最优子结构性质 3.分解而成的小问题在解决之后要可以合并 4.子问题是相互独立的,即子问题之间没有公共的子问题 第一条大多数问

(学习5 分治思想)最短对的问题

最短对问题: 在一个拥有n个点的空间中,求其中欧几里得距离最小的点对. 解析 原理理解的参考视频:https://www.bilibili.com/video/BV1Y7411w71e?from=search&seid=14190590970009131408 1:点的数量较少的情况可以直接用蛮力算法计算出,比如 n=1或者2: 2:点的数量较多的情况,就需要利用分治的思想来实现,首先将每个点按照X轴升序排序,将点集合Q一分为二,称为Q1,Q2.然后分别求出Q1.Q2中最短点对d1与d2:但是我

动态规划_基础_任意子区间序列求和问题_滑动窗口解法_多种思路_分治思想演变

题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入描述 第一行是一个正整数 N ( 1 ≤ N ≤ 200000 ) ,表示了序列的长度. 接下来的 N 行包含 N 个绝对值不大于 10000 的整数 A [ i ] ,描述了这段序列. 输出描述 仅包括 1 个整数,为最大的子段和是多少,子段的最小长度为 1 . 样例输入 72-43-12-43 样例输出 4 Hint Origin: SidneyEdit by stdKonjac in 2020 解题思路: 关于求子区间求

各种与视频编解码以及视频图像处理的应用相关的新技术,新方法,各种软件开发相关的算法,思想。

1. 各种视频压缩标准(MPEG2, MPEG4, H261/2/3/4,X264, T264以及H264(AVC)和HEVC(H265)等的优化,改进,创新. 2. 各种不同平台的(CPU, GPU, DSP, ARM等等)开发,移植优化等, 涉及到的语言包括C, C++, X86汇编,TI DSP汇编,ADI DSP汇编, ARM汇编(armv4/v5/v6/v7 XSCALE WMMX cortex A8等),MMX, SSE, SSE2/3等, 以及目前利用OpenCL来调用GPU实现并