0

0

Java算法设计与分析:分治算法实例详解

王林

王林

发布时间:2023-04-23 11:22:07

|

826人浏览过

|

来源于亿速云

转载

    一、前言

    在学习分治算法之前,问你一个问题,相信大家小时候都有存钱罐的经历,父母亲人如果给钱都会往自己的宝藏中存钱,我们每隔一段时间都会清点清点钱。但是一堆钱让你处理起来你可能觉得很复杂,因为数据相对于大脑有点庞大了,并且很容易算错,你可能会将它先分成几个小份算,然后再叠加起来计算总和就获得这堆钱的总数了

    Java算法设计与分析分治算法实例分析

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

    • 计算每个小堆钱的方式和计算最大堆钱的方式是相同的(区别在于体量上)

    • 然后大堆钱总和其实就是小堆钱结果之和。这样其实就有一种分治的思想。

      立即学习Java免费学习笔记(深入)”;

    二、分治算法介绍

    分治算法是用了分治思想的一种算法,什么是分治?

    分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)等等。

    将父问题分解为子问题同等方式求解,这和递归的概念很吻合,所以在分治算法通常以递归的方式实现(当然也有非递归的实现方式)。

    分治算法的描述从字面上也很容易理解,分、治其实还有个合并的过程:

    • 分(Divide):递归解决较小的问题(到终止层或者可以解决的时候停下)

    • 治(Conquer):递归求解,如果问题够小直接求解。

    • 合并(Combine):将子问题的解构建父类问题

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

    Java算法设计与分析分治算法实例分析

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

    • 1 . 原问题规模通常比较大,不易直接解决,但问题缩小到一定程度就能较容易的解决。

    • 2 . 问题可以分解为若干规模较小、求解方式相同(似)的子问题。且子问题之间求解是独立的互不影响。

    • 3 . 合并问题分解的子问题可以得到问题的解。

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

    三、分治算法经典问题

    对于分治算法的经典问题,重要的是其思想,因为我们大部分借助递归去实现,所以在代码实现上大部分都是很简单,而本篇也重在讲述思想。

    分治算法的经典问题,个人将它分成两大类:子问题完全独立和子问题不完全独立。

    • 1 . 子问题完全独立就是原问题的答案可完全由子问题的结果推出。

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

    3.1、二分搜索

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

    • 序列有序

      酷表ChatExcel
      酷表ChatExcel

      北大团队开发的通过聊天来操作Excel表格的AI工具

      下载
    • 结果为一个值

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

    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) {
          right=mid;
        }
        else {
          left=mid+1;
        }
      }
      return left;
    }

    3.2、快速排序

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

    Java算法设计与分析分治算法实例分析

    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=k)//右侧找到第一个小于k的停止
        {
          high--;
        }
        //这样就找到第一个比它小的了
        a[low]=a[high];//放到low位置
        while(low

    3.3、归并排序(逆序数)

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

    Java算法设计与分析分治算法实例分析

    private static void mergesort(int[] array, int left, int right) {
      int mid=(left+right)/2;
      if(left

    3.4、最大子序列和

    最大子序列和的问题我们可以使用动态规划的解法,但是也可以使用分治算法来解决问题,但是最大子序列和在合并的时候并不是简单的合并,因为子序列和涉及到一个长度的问题,所以正确结果不一定全在最左侧或者最右侧,而可能出现结果的区域为:

    • 完全在中间的左侧

    • 完全在中间的右侧

    • 包含中间左右两个节点的一个序列

    用一张图可以表示为:

    Java算法设计与分析分治算法实例分析

     所以在具体考虑的时候需要将无法递归得到结果的中间那个最大值串的结果也算出来参与左侧、右侧值得比较。

    最大子序和在实现的代码为:

    public int maxSubArray(int[] nums) {
        int max=maxsub(nums,0,nums.length-1);
        return max;
    }
    int maxsub(int nums[],int left,int right)
    {
        if(left==right)
            return  nums[left];
        int mid=(left+right)/2;
        int leftmax=maxsub(nums,left,mid);//左侧最大
        int rightmax=maxsub(nums,mid+1,right);//右侧最大
        int midleft=nums[mid];//中间往左
        int midright=nums[mid+1];//中间往右
        int team=0;
        for(int i=mid;i>=left;i--)
        {
            team+=nums[i];
            if(team>midleft)
                midleft=team;
        }
        team=0;
        for(int i=mid+1;i<=right;i++)
        {
            team+=nums[i];
            if(team>midright)
                midright=team;
        }
        int max=midleft+midright;//中间的最大值
        if(max

    3.5、最近点对

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

    Java算法设计与分析分治算法实例分析

     如果直接分成两部分分治计算你肯定会发现如果最短的如果一个在左一个在右会出现问题。我们可以优化一下。

    在具体的优化方案上,按照x或者y的维度进行考虑,将数据分成两个区域,先分别计算(按照同方法)左右区域内最短的点对。然后根据这个两个中较短的距离向左和向右覆盖,计算被覆盖的左右点之间的距离,找到最小那个距离与当前最短距离比较即可。

    Java算法设计与分析分治算法实例分析

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

    Java算法设计与分析分治算法实例分析

     这样下去就可以节省很多次的计算量。

    但是这种分治会存在一种问题就是二维坐标可能点都聚集某个方法某条轴那么可能效果并不明显(点都在x=2附近对x分割作用就不大),需要注意一下。

    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));
            //Listlist=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=left) {ll--;}
            while(no[rr].y-no[mid].y<=Math.sqrt(minleng)/2&&rr+1<=right) {rr++;}
            for(int i=ll;iminleng) {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(teamcom=new Comparator() {
    
            @Override
            public 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;
            }
        }
    }

    相关文章

    java速学教程(入门到精通)
    java速学教程(入门到精通)

    java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

    下载

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

    相关专题

    更多
    java
    java

    Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

    834

    2023.06.15

    java正则表达式语法
    java正则表达式语法

    java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

    738

    2023.07.05

    java自学难吗
    java自学难吗

    Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

    734

    2023.07.31

    java配置jdk环境变量
    java配置jdk环境变量

    Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

    397

    2023.08.01

    java保留两位小数
    java保留两位小数

    Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

    398

    2023.08.02

    java基本数据类型
    java基本数据类型

    java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

    446

    2023.08.02

    java有什么用
    java有什么用

    java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

    430

    2023.08.02

    java在线网站
    java在线网站

    Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

    16926

    2023.08.03

    高德地图升级方法汇总
    高德地图升级方法汇总

    本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

    2

    2026.01.16

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    Kotlin 教程
    Kotlin 教程

    共23课时 | 2.6万人学习

    C# 教程
    C# 教程

    共94课时 | 6.8万人学习

    Java 教程
    Java 教程

    共578课时 | 46.5万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号