五大常用经典算法—分治算法

article/2025/11/10 2:43:10

原文作者:bigsai

原文地址:五大常用算法:一文搞懂分治算法

目录

前言

分治算法介绍

分治算法经典问题

二分搜索

快速排序

归并排序(逆序数)

最大子序列和

最近点对

结语


前言

分治算法(divide and conquer)是五大常用算法(分治算法、动态规划算法、贪心算法、回溯法、分治界限法)之一,很多人在平时学习中可能只是知道分治算法,但是可能并没有系统的学习分治算法,本篇就带你较为全面的去认识和了解分治算法。在学习分治算法之前,问你一个问题,相信大家小时候都有存钱罐的经历,父母亲人如果给钱都会往自己的宝藏中存钱,我们每隔一段时间都会清点清点钱。但是一堆钱让你处理起来你可能觉得很复杂,因为数据相对于大脑有点庞大了,并且很容易算错,你可能会将它先分成几个小份算,然后再叠加起来计算总和就获得这堆钱的总数了

image-20201130124009617

当然如果你觉得各个部分钱数量还是太大,你依然可以进行划分然后合并,我们之所以这么多是因为:

  • 计算每个小堆钱的方式和计算最大堆钱的方式是相同的(区别在于体量上)
  • 然后大堆钱总和其实就是小堆钱结果之和。这样其实就有一种分治的思想。

当然这些钱都是想出来的……

BACDB95DF648E67CF0576A009697EBD2

分治算法介绍

分治算法是用了分治思想的一种算法,什么是分治?分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)等等。将父问题分解为子问题同等方式求解,这和递归的概念很吻合,所以在分治算法通常以递归的方式实现(当然也有非递归的实现方式)。分治算法的描述从字面上也很容易理解,分、治其实还有个合并的过程:

  • 分(Divide):递归解决较小的问题(到终止层或者可以解决的时候停下)
  • 治(Conquer):递归求解,如果问题够小直接求解。
  • 合并(Combine):将子问题的解构建父类问题

一般分治算法在正文中分解为两个即以上的递归调用,并且子类问题一般是不想交的(互不影响)。当求解一个问题规模很大很难直接求解,但是规模较小的时候问题很容易求解并且这个问题并且问题满足分治算法的适用条件,那么就可以使用分治算法。

image-20201130165303362

那么采用分治算法解决的问题需要 满足那些条件(特征) 呢?

  • 1 . 原问题规模通常比较大,不易直接解决,但问题缩小到一定程度就能较容易的解决。
  • 2 . 问题可以分解为若干规模较小、求解方式相同(似)的子问题。且子问题之间求解是独立的互不影响。
  • 3 . 合并问题分解的子问题可以得到问题的解。

你可能会疑惑分治算法和递归有什么关系?其实分治重要的是一种思想,注重的是问题分、治、合并的过程。而递归是一种方式(工具),这种方式通过方法自己调用自己形成一个来回的过程,而分治可能就是利用了多次这样的来回过程

分治算法经典问题

对于分治算法的经典问题,重要的是其思想,因为我们大部分借助递归去实现,所以在代码实现上大部分都是很简单,而本篇也重在讲述思想。分治算法的经典问题,个人将它分成两大类:子问题完全独立和子问题不完全独立。

  • 1 . 子问题完全独立就是原问题的答案可完全由子问题的结果推出。
  • 2 . 子问题不完全独立,有些区间类的问题或者跨区间问题使用分治可能结果跨区间,在考虑问题的时候需要仔细借鉴下。

二分搜索

二分搜索是分治的一个实例,只不过二分搜索有着自己的特殊性

  • 序列有序
  • 结果为一个值

正常二分将一个完整的区间分成两个区间,两个区间本应单独找值然后确认结果,但是通过有序的区间可以直接确定结果在那个区间,所以分的两个区间只需要计算其中一个区间,然后继续进行一直到结束。实现方式有递归和非递归,但是非递归用的更多一些:

public int searchInsert(int[] nums, int target) {if(nums[0]>=target)return 0;//剪枝if(nums[nums.length-1]==target)return nums.length-1;//剪枝if(nums[nums.length-1]<target)return nums.length;int left=0,right=nums.length-1;while (left<right) {int mid=(left+right)/2;if(nums[mid]==target)return mid;else if (nums[mid]>target) {right=mid;}else {left=mid+1;}}return left;
}

时间复杂度:比如总共有n个元素,每次查找的区间大小就是n,n/2,n/4,…,n/2^k(接下来操作元素的剩余个数),其中k就是循环的次数。 由于n/2^k取整后>=1,即令n/2^k=1, 可得k=log2n,(是以2为底,n的对数),所以时间复杂度可以表示O()=O(logn)

快速排序

快排也是分治的一个实例,快排每一趟会选定一个数,将比这个数小的放左面,比这个数大的放右面,然后递归分治求解两个子区间,当然快排因为在分的时候就做了很多工作,当全部分到最底层的时候这个序列的值就是排序完的值。这是一种分而治之的体现。

image-20201120133851275

public void quicksort(int [] a,int left,int right)
{int low=left;int high=right;//下面两句的顺序一定不能混,否则会产生数组越界!!!very important!!!if(low>high)//作为判断是否截止条件return;int k=a[low];//额外空间k,取最左侧的一个作为衡量,最后要求左侧都比它小,右侧都比它大。while(low<high)//这一轮要求把左侧小于a[low],右侧大于a[low]。{while(low<high&&a[high]>=k)//右侧找到第一个小于k的停止{high--;}//这样就找到第一个比它小的了a[low]=a[high];//放到low位置while(low<high&&a[low]<=k)//在low往右找到第一个大于k的,放到右侧a[high]位置{low++;}a[high]=a[low];			}a[low]=k;//赋值然后左右递归分治求之quicksort(a, left, low-1);quicksort(a, low+1, right);		
}

归并排序(逆序数)

快排在分的时候做了很多工作,而归并就是相反,归并在分的时候按照数量均匀分,而合并时候已经是两两有序的进行合并的,因为两个有序序列O(n)级别的复杂度即可得到需要的结果。而逆序数在归并排序基础上变形同样也是分治思想求解。

image-20201120173153449

private static void mergesort(int[] array, int left, int right) {int mid=(left+right)/2;if(left<right){mergesort(array, left, mid);mergesort(array, mid+1, right);merge(array, left,mid, right);}
}private static void merge(int[] array, int l, int mid, int r) {int lindex=l;int rindex=mid+1;int team[]=new int[r-l+1];int teamindex=0;while (lindex<=mid&&rindex<=r) {//先左右比较合并if(array[lindex]<=array[rindex]){team[teamindex++]=array[lindex++];}else {				team[teamindex++]=array[rindex++];}}while(lindex<=mid)//当一个越界后剩余按序列添加即可{team[teamindex++]=array[lindex++];}while(rindex<=r){team[teamindex++]=array[rindex++];}	for(int i=0;i<teamindex;i++){array[l+i]=team[i];}
}

最近点对

最近点对是一个分治非常成功的运用之一。在二维坐标轴上有若干个点坐标,让你求出最近的两个点的距离,如果让你直接求那么枚举暴力是个非常非常大的计算量,我们通常采用分治的方法来优化这种问题。

image-20201130204401673

如果直接分成两部分分治计算你肯定会发现如果最短的如果一个在左一个在右会出现问题。我们可以优化一下。在具体的优化方案上,按照x或者y的维度进行考虑,将数据分成两个区域,先分别计算(按照同方法)左右区域内最短的点对。然后根据这个两个中较短的距离向左和向右覆盖,计算被覆盖的左右点之间的距离,找到最小那个距离与当前最短距离比较即可。

image-20201130205950625

这样你就可以发现就这个一次的操作(不考虑子情况),左侧红点就避免和右侧大部分红点进行距离计算(O(n2)的时间复杂度)。事实上,在进行左右区间内部计算的时候,它其实也这样递归的进行很多次分治计算。如图所示:

image-20201130210925059

这样下去就可以节省很多次的计算量。但是这种分治会存在一种问题就是二维坐标可能点都聚集某个方法某条轴那么可能效果并不明显(点都在x=2附近对x分割作用就不大),需要注意一下。杭电1007推荐给大家,ac的代码为:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Main {static int n;public static void main(String[] args) throws IOException {StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));//List<node>list=new ArrayList();while(in.nextToken()!=StreamTokenizer.TT_EOF){n=(int)in.nval;if(n==0) {break;}node no[]=new node[n];for(int i=0;i<n;i++){in.nextToken();double x=in.nval;in.nextToken();double y=in.nval;// list.add(new node(x,y));no[i]=new node(x,y);}Arrays.sort(no, com);double min= search(no,0,n-1);out.println(String.format("%.2f", Math.sqrt(min)/2));out.flush();}         }private static double search(node[] no, int left,int right) {int mid=(right+left)/2;double minleng=0;if(left==right) {return Double.MAX_VALUE;}else if(left+1==right) {minleng= (no[left].x-no[right].x)*(no[left].x-no[right].x)+(no[left].y-no[right].y)*(no[left].y-no[right].y);}else minleng= min(search(no,left,mid),search(no,mid,right));int ll=mid;int rr=mid+1;while(no[mid].y-no[ll].y<=Math.sqrt(minleng)/2&&ll-1>=left) {ll--;}while(no[rr].y-no[mid].y<=Math.sqrt(minleng)/2&&rr+1<=right) {rr++;}for(int i=ll;i<rr;i++){for(int j=i+1;j<rr+1;j++){double team=0;if(Math.abs((no[i].x-no[j].x)*(no[i].x-no[j].x))>minleng) {continue;}else{ team=(no[i].x-no[j].x)*(no[i].x-no[j].x)+(no[i].y-no[j].y)*(no[i].y-no[j].y);if(team<minleng)minleng=team;}}}return minleng;}private static double min(double a, double b) {// TODO 自动生成的方法存根return a<b?a:b;}static Comparator<node>com=new Comparator<node>() {@Overridepublic int compare(node a1, node a2) {// TODO 自动生成的方法存根return a1.y-a2.y>0?1:-1;}};static class node{double x;double y;public node(double x,double y){this.x=x;this.y=y;}}
}

结语

到这里,分治算法就讲这么多了,因为分治算法重要在于理解其思想,还有一些典型的分治算法解决的问题,例如大整数乘法、Strassen矩阵乘法、棋盘覆盖、线性时间选择、循环赛日程表、汉诺塔等问题你可以自己研究其分治的思想和原理。


http://chatgpt.dhexx.cn/article/qxr5fmik.shtml

相关文章

十大经典算法

十大经典排序算法&#xff08;动图演示&#xff09; 0、算法概述 0.1 算法分类 十种常见排序算法可以分为两大类&#xff1a; 非线性时间比较类排序&#xff1a;通过比较来决定元素间的相对次序&#xff0c;由于其时间复杂度不能突破O(nlogn)&#xff0c;因此称为非线性时间比…

OpenX系列标准:OpenDRIVE标准简述

1.概述 ​ 作为一个完整的仿真测试场景描述方案&#xff0c;OpenX系列标准包括&#xff1a;OpenDRIVE、OpenSCENARIO和OpenCRG。 标准文件格式文件内容OpenDRIVE.xodr静态部分&#xff08;如道路拓扑结构、交通标志标线等&#xff09;OpenDRIVE.tdo保存ROD项目时生成的文件&a…

OpenDRIVE坐标系解读

几种坐标系简述 opendrive标准主要包括三种坐标系&#xff1a;inertial(x, y, z)、reference line(s, t, h)、local(u, v, z) 下面这张图片笔者认为还是比较清晰的展示了三种坐标系的关系的&#xff1a; 惯性坐标系&#xff08;Inertial&#xff09; 惯性坐标系最简单&am…

《OpenDRIVE1.6规格文档》6

目录 12 标志12.1 针对标志的车道有效性12.2 标志依赖12.3 标志与物体之间的链接12.4 标志放置12.5 标志信息的复用12.6 控制器 13 铁路13.1 铁轨13.2 转辙器13.2.1 主轨道13.2.2 次轨道13.2.3 搭档转辙器 13.3 车站13.3.1 站台13.3.2 段 14 插图目录15 表格目录 12 标志 如图…

《OpenDRIVE1.6规格文档》4

目录 9.5.7 车道高度9.5.8 从道路超高程中排除车道 9.6 道路标识9.6.1 路标类型和线条9.6.2 显性路标类型和线条9.6.3 路标偏移 9.7 特定车道规则 10 交叉口10.1 来路10.2 联接道路10.2.1 交叉口中联接道路的优先级10.2.2 联接道路的方向 10.3 交叉口内的道路表面10.4 虚拟交叉…

opendrive地理坐标

通过使用基于PROJ&#xff08;一种用于两个坐标系之间数据交换的格式&#xff09;的投影字符串来完成对大地基准的描述。该数据应标为CDATA&#xff0c;因为其可能包含会干预元素属性XML语义的字符。 具体参数参考官方文档&#xff1a;Quick start — PROJ 9.2.0 documentatio…

OpenDRIVE地图图形化

OpenDRIVE地图图形化 前言一、 p l a n V i e w planView planView参数三次曲线弧线螺旋线 二、 e l e v a t i o n P r o f i l e elevationProfile elevationProfile三、 l a t e r a l P r o f i l e lateralProfile lateralProfile总结 前言 关于 O p e n D R I V E OpenD…

opendrive中的几何形状

道路的走向可以是多种多样的&#xff0c;可以是空旷地面上的直线、高速公路上细长的弯道、亦或山区狭窄的转弯。为从数学角度对所有这些道路线进行正确建模&#xff0c;OpenDRIVE提供了多种几何形状元素。 图19展示了五种定义道路参考线几何形状的可行方式&#xff1a; 直线螺…

opendrive中的Road

路网在OpenDRIVE中用 <road> 元素来表示。每条道路都沿一条道路参考线延伸。一条道路必须拥有至少一条宽度大于0的车道。 OpenDrive中的道路可以与真实路网中或为应用而设的路网中的道路相提并论。每条道路由一个或多个 <road> 元素描述。一个 <road> 元素可…

opendrive文件结构

1、 文件结构 OpenDRIVE数据存储于XML文件中&#xff0c;文件拓展名为.xodr。OpenDRIVE压缩文件的拓展名为".xodrz"&#xff08;压缩格式gzip&#xff09;。 OpenDRIVE文件的结构符合XML规则&#xff1b;关联的模式文件在XML中得到引用。用于OpenDRIVE格式的模式文…

opendrive中的Lanes

在OpenDRIVE中&#xff0c;所有道路都包含了车道。每条道路必须拥有至少一条宽度大于0的车道&#xff0c;并且每条道路的车道数量不受限制。 需要使用中心车道对OpenDRIVE中的车道进行定义和描述。中心车道没有宽度&#xff0c;并被用作车道编号的参考&#xff0c;自身的车道编…

opendrive坐标系

1 opendrive坐标系概况 OpenDRIVE使用三种类型的坐标系&#xff0c;如下图所示&#xff1a; 惯性x/y/z轴坐标系参考线s/t/h轴坐标系局部u/v/z轴坐标系 若无另外说明&#xff0c;对局部坐标系的查找与定位将相对于参考线坐标系来进行。对参考线坐标系位置与方向的设定则相对于…

OpenDrive里XY和ST

1.坐标系 根据维基百科&#xff0c;坐标系是指&#xff1a;定义一个n维系统&#xff0c;能够使每一个点和一组n个标量组成一一对应的系统。[1] 在坐标系里&#xff0c;有几个关键概念。 第一个关键概念是维度&#xff08;Dimension&#xff09;。维度是指通过一定标准&#xff…

[OpenDrive] OpenDrive学习笔记

文章目录 OpenDRIVEreference linelaneslane offsetlane sectionslane propertiessuperelevation and crossfalllateral profileroad linkagejunctionsneighbors 总体结构Apollo OpenDRIVEApollo OpenDRIVE结构 OpenDRIVE OpenDRIVE是对路网结构的描述性文件&#xff0c;于200…

opendrive简介

1、概要 ASAM OpenDRIVE描述了自动驾驶仿真应用所需的静态道路交通网络&#xff0c;并提供了标准交换格式说明文档。该标准的主要任务是对道路及道路上的物体进行描述。OpenDRIVE说明文档涵盖对如道路、车道、交叉路口等内容进行建模的描述&#xff0c;但其中并不包含动态内容…

如何使用OpenDRIVE

文章目录 OpenDRIVE Notes#1 前言#2 OpenDRIVE结构#2.1 Road#2.1.1 道路属性#2.1.2 道路联接#2.1.3 参考线 #2.2 laneSection#2.3 laneOffset#2.4 junction#2.4.1 路口的联接 #2.5 poly3(三次多项式) #3 解析#3.1 数据结构#3.1.1 ID#3.1.2 Point #4 构建topo#5 邻接点#6 路径规…

《OpenDRIVE1.6规格文档》1

目录 1 前言1.1 说明文档的可交付内容 2 介绍2.1 概要2.2 规范和非规范的声明与可交付内容2.3 惯例2.3.1 命名惯例2.3.2 单位2.3.3 情态动词2.3.4 拼写惯例2.3.5 ID的使用2.3.6 曲率 3 与其它标准的关联(初步)3.1 ASAM OpenDRIVE在ASAM标准系列中的角色3.2 OpenDRIVE与OpenCRG以…

万字详解OpenDRIVE文件

opendrive简介_whuzhang16的博客-CSDN博客_opendrive一文读懂opendrive的xodr文件内容_布拉德先生的博客-CSDN博客_xodr格式自动驾驶场景仿真标准&#xff08;一&#xff09;- OpenDRIVE - 知乎 (zhihu.com)opendrive坐标系_whuzhang16的博客-CSDN博客_opendrive坐标系 1 Open…

OpenX系列标准介绍(1):OpenDRIVE介绍

|作者版权所有&#xff0c;未经许可谢绝转载&#xff0c;转载请联系adsimtest163.com。 “ 本系列尝试对ASAM OpenX系列标准进行介绍。这是第一篇&#xff1a;介绍OpenDRIVE地图数据格式所能描述的内容和思路。” 01 概述 作为一个完整的仿真测试场景描述方案&#xff0c;Op…

完美解析Opendrive地图格式数据

1.前言 高精度电子地图也称为高分辨率地图(HD Map&#xff0c;High Definition Map)&#xff0c;是一种专门为无人驾驶服务的地图。与传统导航地图不同的是&#xff0c;高精度地图除了能提供的道路(Road)级别的导航信息外&#xff0c;还能够提供车道(Lane)级别的导航信息。无论…