当前位置:

首页 > 编程开发 > Java算法设计与分析分治算法实例分析

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

一、前言在学习分治算法之前,问你一个问题,相信大家小时候都有存钱罐的经历,父母亲人如果给钱都会往自己的宝藏中存钱,我们每隔一段时间都会清点清点钱。但是一堆钱让你处理起来你可能觉得很复杂,因为数据相对于大脑有点庞大了,并且很容易算错,你可能会将它先分成几个小份算,然后再叠加起来计算总和就获得这堆钱的总数了当然如果你觉得各个部分钱数量还是太大,你依然可以进行划分然后合并,我们之所以这么多是因为:计算每个小堆钱的方式和计算最大堆钱的方式是相同的(区别在于体量上)然后大堆钱总和其实就是小堆钱结果之和。这样其实就有

本文目录

      一、前言

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

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

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

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

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

      二、分治算法介绍

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      三、分治算法经典问题

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

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

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

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

      3.1、二分搜索

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

      • 序列有序

      • 结果为一个值

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

      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;
              }
          }
      }
      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Java
      相关文章 更多
      Java单元测试入门:使用JUnit 5编写第一个测试用例
      Java单元测试入门:使用JUnit 5编写第一个测试用例

      面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

      Java变量未初始化报错解析:局部变量与成员变量默认值差异
      Java变量未初始化报错解析:局部变量与成员变量默认值差异

      深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

      Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
      Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

      详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

      Java中final与static final常量的定义及初始化规则
      Java中final与static final常量的定义及初始化规则

      深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

      Java抽象方法与接口方法的区别及选型建议
      Java抽象方法与接口方法的区别及选型建议

      深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

      Java实现HTTP请求与JSON解析的完整代码示例
      Java实现HTTP请求与JSON解析的完整代码示例

      本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

      Java递归出现StackOverflowError的原因分析及栈空间优化方案
      Java递归出现StackOverflowError的原因分析及栈空间优化方案

      详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

      Java空指针异常(NPE)的代码排查与修复实例
      Java空指针异常(NPE)的代码排查与修复实例

      针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

      selenium上传文件教程本地文件路径设置方法
      selenium上传文件教程本地文件路径设置方法

      深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

      针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

      查看更多
      精品专题 更多
      装机必备
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows
      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。