程序员的算法课(13)-分治法
一、什么是分治
【百度百科】分治法((Divide and Conquer))可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化。
分治法的精髓:
- 分--将问题分解为规模更小的子问题;
- 治--将这些规模更小的子问题逐个击破;
- 合--将已解决的子问题合并,最终得出“母”问题的解;
在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)……
大白话:大事化小,小事化了。
二、分治法能解决的问题特征
- 该问题的规模缩小到一定的程度就可以容易地解决
- 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
- 利用该问题分解出的子问题的解可以合并为该问题的解;
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
上述的第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
大白话:简单问题分治法,复杂问题贪心法或动态规划,是否简单看上面的四个特征↑。
PS:大数据中运用分治法非常多,通常会把大问题分解成相同的小问题,后面我们再慢慢聊。
三、分治法解法
由分治法产生的子问题往往是原问题的较小模式,这就可以使用递归技术来解决。分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
这样,我们就把分治法化为了怎么进行递归。
1.求解递归式的三种方法
这三种方法分别是:代入法,递归树法,和主方法。代入法是一种十分强大的方法,它几乎可以求解所有的递归式。然而,对于一些“小鸡”来说,不需要这么强大的“牛刀”。对于一些特定类型的递归式(具体类型在主方法的小节中会介绍),用主方法可以用更少的时间得到一个更确切的界。
2.分治法在每一层递归上都有三个步骤:
- 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
- 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
- 合并:将各个子问题的解合并为原问题的解。
3.它的一般的算法设计模式如下:
- 1. if |P|≤n0
- 2. then return(ADHOC(P))
- 3. 将P分解为较小的子问题 P1 ,P2 ,...,Pk
- 4. for i←1 to k
- 5. do yi ← Divide-and-Conquer(Pi) △ 递归解决Pi
- 6. T ← MERGE(y1,y2,...,yk) △ 合并子问题
- 7. return(T)
四、分治法实现最近对问题(JAVA)
题目:假设所有点都在集合S中。
- 用S中个点坐标的中位数作为分割点,则会得到一个平衡的分割点m,使得子集S1,S2中有个数大致相同的点。
- 选取垂直线x=c(中位线)来作为分割线。
- 递归地求出S1和S2中的最近对,假设D1、D2是最近距离。
- 距离最近的点,可能在线的俩边,所以,我们需要在以x=c为对称的、宽度为2D的(D为D1、D2中的最小值)的垂直带中。
- 在该范围中递归得出最近距离。
import java.util.*;
public class S2_4 {public static void main(String[] args) {Scanner s=new Scanner(System.in);int x=0,x1=0,x2=0,x3=0,x4=0;int y=0,y1=0,y2=0;double dis1=0,dis2=0;System.out.print("输入需要生成多少个随机点N:");int n=s.nextInt();int A[][]=new int[n][2];int B[][]=new int[n][2];int C[][]=new int[n][2];int D[][]=new int[n][2];for(int i=0;i<n;i++) {A[i][0]=(int)(Math.random()*100)+1;//生成100以内的随机数,放入横坐标}for(int i=0;i<n;i++) {A[i][1]=(int)(Math.random()*100)+1;//生成100以内的随机数,放入纵坐标}for(int i=0;i<n;i++) {System.out.println("("+A[i][0]+","+A[i][1]+")");}int minX = (int) Double.POSITIVE_INFINITY; //保证假设的初始最小值足够大 int maxX = (int) Double.NEGATIVE_INFINITY; //保证假设的初始最大值足够小 for(int i = 0; i < A.length; i++){ if(A[i][0] < minX) minX = A[i][0]; if(A[i][0] > maxX) maxX = A[i][0]; } int mid = (minX + maxX)/2;int p=0,t=0;for(int i=0;i<n;i++) { //将x坐标分成俩个部分if(A[i][0]<=mid) { B[p][0]=A[i][0];B[p][1]=A[i][1];p++;}else {C[t][0]=A[i][0];C[t][1]=A[i][1];t++;}}int d1=(int) Double.POSITIVE_INFINITY,d2=(int) Double.POSITIVE_INFINITY; //设置俩边的距离最小值为较大的数值int dx=0,dy=0,dz=0;for(int i=0;i<=p-2;i++) //得出d1的值for(int j=i+1;j<=p-1;j++) {dx=(B[j][0]-B[i][0])*(B[j][0]-B[i][0])+(B[j][1]-B[i][1])*(B[j][1]-B[i][1]);if(dx<d1) {d1=dx;x1=i; //记录俩个点的坐标x2=j;}}for(int i=0;i<=t-2;i++) //得出d2的值for(int j=i+1;j<=t-1;j++) {dy=(C[j][0]-C[i][0])*(C[j][0]-C[i][0])+(C[j][1]-C[i][1])*(C[j][1]-C[i][1]);if(dy<d2) {d2=dy;x3=i; //记录俩个点的坐标x4=j;}}System.out.println("mid="+mid+" "+"d1="+d1+" "+"d2="+d2);if(d1<d2) {dz=d1;dis1=Math.sqrt(dz);System.out.println("x坐标中的最小距离的俩个点为:"+A[x1][0]+","+A[x1][1]+" "+A[x2][0]+","+A[x2][1]);System.out.println("最小距离为:"+dis1);x=x1;y=x2;}else {dz=d2;dis1=Math.sqrt(dz);System.out.println("x坐标中的最小距离的俩个点为:"+A[x3][0]+","+A[x3][1]+" "+A[x4][0]+","+A[x4][1]);System.out.println("最小距离为:"+dis1);x=x3;y=x4;}int q=0;for(int i=0;i<n;i++) {if((mid-dis1)<=A[i][0] && A[i][0]<=(mid+dis1)) { //中心线左右俩边的最小距离内寻找俩边的最近点D[q][0]=A[i][0];D[q][1]=A[i][1];q++;}}double mind=Double.POSITIVE_INFINITY;//mind设置为正无穷大,作为比较值double dis=0;for(int k=0;k<=q-2;k++)for(int j=k+1;j<=q-1;j++) {dis=(D[j][0]-D[k][0])*(D[j][0]-D[k][0])+(D[j][1]-D[k][1])*(D[j][1]-D[k][1]);if(dis<mind) {mind=dis;y1=k; //记录俩个点的坐标y2=j;}}dis2=Math.sqrt(mind);System.out.println("dis1="+dis1+" "+"dis2="+dis2);if(dis1<dis2) {System.out.println("最小距离为:"+dis1);System.out.print("俩个点分别为:"+"("+A[x][0]+","+A[x][1]+")");System.out.println(" "+"("+A[y][0]+","+A[y][1]+")" );}else {System.out.println("最小距离为:"+dis2);System.out.print("俩个点分别为:"+"("+A[y1][0]+","+A[y1][1]+")");System.out.println(" "+"("+A[y2][0]+","+A[y2][1]+")" );}}
}
五、经典问题
- 二分搜索 [1]
- 大整数乘法
- Strassen矩阵乘法
- 棋盘覆盖
- 合并排序
- 快速排序
- 线性时间选择
- 最接近点对问题
- 循环赛日程表
- 汉诺塔
- 大数据
我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!
参考资料:
- https://blog.csdn.net/xlinsist/article/details/79198842
- https://blog.csdn.net/weixin_42059543/article/details/83001863
程序员的算法课(13)-分治法相关推荐
- c++分治法求最大最小值实现_程序员:算法导论,分治法、归并排序,伪代码和Java实现...
分治法 我们首先先介绍分治法.分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后在合并这些子问题的解来解决原问题的解. 还是拿扑克牌举例子,假设桌上有两堆牌面朝 ...
- 视频教程-程序员必备算法课!(揭秘淘宝购物车算法)-机器学习
程序员必备算法课!(揭秘淘宝购物车算法) CSDN讲师名下集合了诸多业界知名讲师的公开课内容,内容涵盖人工智能.大数据.区块链等诸多热门技术领域的最佳技术实践,聚合美团.滴滴.AWS.科大讯飞等知名企 ...
- 程序员的算法课(2)-排序算法
术语说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面: 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面: 内排序:所有排序操作都在内存中完成: 外排序:由于数 ...
- 程序员的算法课(15)-分治法获取文件中出现频次最高100词
一.问题描述 这个问题在大数据面试中容易出现,问题如下: 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M,要求返回频数最高的100个词. 二.思路 此处1G文 ...
- 程序员的算法课(1)-算法概述
[算法之美]数据结构+算法=程序. 前言 数据结构只是静态的描述了数据元素之间的关系.高效的程序需要在数据结构的基础上设计和选择算法. 高效的程序=恰当的数据结构+合适的算法 算法(Algorithm ...
- 程序员的算法课(5)-动态规划算法
前言 众所周知,递归算法时间复杂度很高为(2^n),而动态规划算法也能够解决此类问题,动态规划的算法的时间复杂度为(n^2).动态规划算法是以空间置换时间的解决方式. 一.什么是动态规划 动态规划(D ...
- 程序员的算法课(4)-二分查找
一个90%的程序员写不对的程序,一个面试高频出现的面试题,一个开发中用之甚广的算法,一个最能体现程序员素质的代码,它就是二分查找. 一.二分查找的定义 [百度百科]二分查找也称折半查找(Binary ...
- 程序员的算法课(3)-递归(recursion)算法
一.什么是递归 递归是一种数学上分而自治的思想. 递归将大型复杂问题转化为与原问题相同但规模较小 的问题进行处理 递归需要有边界条件,当边界条件不满足时,递归继续进行:当边界条件满足时,递归停止 [百 ...
- c 递归下降识别程序_程序员的算法课(3)-递归(recursion)算法
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/ ...
最新文章
- 设置windows xp 调试
- shell下输出字体颜色
- ae渲染出现错误是什么问题_[Switch]解决OPENWRT路由,使用UU加速器等出现相同IP,DNS解析错误问题...
- AirFlow官方入门DAG示例
- 10月25日学习内容整理:数据操作:增加更新删除,单表查询操作
- 免疫优化算法 matlab,基于人工免疫克隆选择算法的调度优化MATLAB源码
- 渗透测试专题二之msf(kali)的攻击教程将DOS操作系统中的本地文件接口“中断13”改造为网络文件系统...
- 数据结构严蔚敏--第三章--栈和队列
- 秀米 html5 编辑工具,秀米编辑器
- 工业机器人编程语言c语言,工业机器人编程语言和编程方式
- 微信小程序----实现YDUI的ScrollTab(滚动选项卡)
- 虚拟机搭建LNMP环境怎么开通80端口
- python海龟绘图颜色_Python:海龟绘图(六)——来点颜色看看
- 外置硬盘一插就卡_为什么电脑一插移动硬盘就卡死了?
- Java在后台获取USB二维码扫描枪扫描的内容
- 阿里云香港节点全面故障给我们的启示
- 黄金分割法python实现
- 自由浏览器 android,百度浏览器发布安卓6.1版 趣味视频弹幕吐槽不停
- EverBox(同步网盘)邀请
- 基于JavaEE的大学生公寓管理系统
热门文章
- Servlet中参数获取方法
- Artech的MVC4框架学习——第八章View的呈现
- SqlServer为什么自动在主键上建立聚集索引
- 转载一篇阅读文章(还算不错吧)
- Dubbo(五)Dubbo 简单监控平台(monitor)搭建
- FileOutputStream 类 和 FileInputStream类的简单介绍,附代码演示。以及一个复制媒体文件的小程序。
- 华为手机连接电脑用什么软件_屏幕镜像怎么连接电脑?使用这款软件,轻松投屏苹果手机到电脑...
- php的Snoopy类
- U盘文件系统类型 和 linux 挂载 和 卸载
- Spring Cloud 微服务实战系列-Spring Boot再次入门(一)