分治算法

分治算法简介

在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,简单来说就是把一个问题分解为很多的子问题,然后再通过子问题的合并来获得最终的结果。如:排序算法(归并排序,快速排序),傅立叶变换都属于典型的分治算法的案例。

基本思路

将一个个大问题,拆分成小问题,然后各个击破,把小问题的答案再合并成最终的答案。
如果一个问题能够被拆成k个小问题,并且这k个小问题的答案能够合并成最终的答案,那么就可以用分治算法来解决。

实际案例

案例一(归并排序)

加入有一个无序的数组{5,4,3,2,1,6,8,7,10,1},要对他们进行归并排序:
1.把数组持续拆分,直到只剩下一个元素为止
2.递归的合并拆分后的数组,直到最后得到排序后的结果
代码实现:
1.拆分数组:

 public static int[] sort(int[] arr,int left,int right){
        int index = left + (right - left)/2;
        if(left == right){
            int[] temp = new int[1];
            temp[0] = arr[left];
            return temp;
        } else{
            return compareSort(sort(arr,left,index),sort(arr,index+1,right));
        }
    }

2.合并数组:

public static int[] compareSort(int[] arr1,int[] arr2){
        int length = arr1.length + arr2.length;
        int index1 = 0, index2 = 0;
        int[] temp = new int[length];
        int index = 0;
        while(index1 < arr1.length && index2 < arr2.length){
            if(arr1[index1] < arr2[index2]){
                temp[index++] = arr1[index1++];
            }else{
                temp[index++] = arr2[index2++];
            }
        }
        while(index1 < arr1.length){
            temp[index++] = arr1[index1++];
        }
        while(index2 < arr2.length){
            temp[index++] = arr2[index2++];
        }
        return temp;
    }
案例二(快速排序)

从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去,具体步骤:
1.拆分数组

public int[] quickSort(int[] arr, int left, int right) {
        int partitionIndex;
        if (left < right) {
            partitionIndex = partition(arr, left, right);
            quickSort(arr, left, partitionIndex - 1);
            quickSort(arr, partitionIndex + 1, right);
        }
        return arr;
    }

2.分区操作

public int partition(int[] arr, int left, int right) {     // 分区操作
        int pivot = left,                      // 设定基准值(pivot)
                index = pivot + 1;
        for (int i = index; i <= right; i++) {
            if (arr[i] < arr[pivot]) {
                swap(arr, i, index);
                index++;
            }
        }
        swap(arr, pivot, index - 1);
        return index - 1;
    }
案例二(最近点问题)

假如平面上有N个散列的点,找到距离最短的两个点。正常方法是,计算每任意两个点之间的距离,这样的话,时间是N(N-1)/2,
也就是O(n
n);但是有一种方法,对于密集的点,能够把效率提升为O(NlogN ),这种方式就是分治算法。
假如如下图,有下列密集点:

WX20190412-104120@2x.png

假如这些点已经按照x轴进行排序,我们可以画一条垂直的直线,把点集分成两半,PL,PR,因此可以得出,整个点击最近的两个点一定是PL中最小的两个点,和PR中距离最小的两个点,和PL中一个点和PR中一个点组成的一条最小距离的直线。正如下图所示,整个点击最小距离的点一定是dL,dR,和dc中最小的那一条。


WX20190412-104749@2x.png

具体实现步骤:
1.通过不断的递归从中拆分,直到左右两边只剩下两个点为止。这样的话,dL,dR的距离直接通过计算可以得出,因此,可以设置 s = min(dL,dR);
2.计算dc的最短距离,由于我们知道中线的位置,因此只需要计算 (mid-s)和(mid+s)中所有点的距离即可。
3.最后整个区域的最小值则为min(dL,dR,dC)

具体代码实现:
1.定义平面点的属性:


public class Points{
    private double x,y;

    public void setX(double x) {
        this.x = x;
    }

    public double getX() {
        return x;
    }

    public void setY(double y) {
        this.y = y;
    }

    public double getY() {
        return y;
    }
}

2.递归拆分所有的点

public static double divPoints(Points[] points, int left, int right){
        if(right - left == 1){
            return distance(points[left],points[right]);
        }else if(right - left == 2){
            return distance(points[left],points[left+1],points[left+2]);
        }else{
            int mid = left + (right - left)/2;
            double leftP = divPoints(points,left,mid);
            double rightP = divPoints(points,mid,right);
            double minLR = Math.min(leftP,rightP);
            ArrayList<Points> dLeft = new ArrayList();
            ArrayList<Points> dRight = new ArrayList();
            for(int i = left; i <= right; i++){
                if(i != mid){
                    if(points[i].getX() < points[mid].getX() && (points[mid].getX() - points[i].getX()) < minLR ){
                        dLeft.add(points[i]);
                    }else if(points[i].getX() >= points[mid].getX() && (points[i].getX() - points[mid].getX()) < minLR){
                        dRight.add(points[i]);
                    }
                }
            }


            for(int i = 0;i<dLeft.size();i++){
                for(int j = 0;j<dRight.size();j++){
                    double mLR = distance(dLeft.get(i),dRight.get(j));
                    if(mLR < minLR){
                        minLR = mLR;
                    }
                }
            }
            return minLR;
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 分冶算法的基本思想是将原问题分解为几个规模较小的但类似原问题的子问题,递归地求解这些了问题,然后再合并这些子问题的...
    某昆阅读 1,668评论 0 6
  • Divide-and-Conquer算法的设计 设计过程分为三个阶段: Divide:整个问题划分为多个子问题 C...
    三三de酒阅读 3,257评论 0 4
  • 从分治算法说起 要说 MapReduce 就不得不说分治算法,而分治算法其实说白了,就是四个字 分而治之 。其实就...
    大数据_zzzzMing阅读 602评论 0 0
  • https://www.cnblogs.com/steven_oyj/archive/2010/05/22/174...
    麒麟楚庄王阅读 572评论 0 0
  • 排序算法说明 (1)排序的定义:对一序列对象根据某个关键字进行排序; 输入:n个数:a1,a2,a3,…,an 输...
    code武阅读 655评论 0 0