分治策略

分治策略

1.算法总体思想
1.1分治法的设计思想
	1. 当要求解一个输入规模n相当大的问题时,直接求解往往是非常困难的,甚至没法求出。正确的方法是, 首先应仔细分析问题本身所具有的特性,然后根据这些特性选择适当的设计策略来求解。
	2. 在将这n个输入分成k个不同子集合的情况下,如果能得到k个不同的可独立求解的子问题,而且在求解之后,还可找到适当的方法把它们合并成整个问题的解,那么,可考虑使用分治法来求解。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wsTS9tf9-1590819436128)(C:\Users\rw\AppData\Roaming\Typora\typora-user-images\1590808638882.png)]

1.2分支法的使用条件

分治法所能解决的问题一般具有以下几个特征:

  • **该问题的****规模缩小到一定的程度就可以容易地解决;
  • 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;
  • 利用该问题分解出的子问题的解可以合并为该问题的解;
  • 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。**

通常,由分治法所得到的子问题与原问题具有相同的类型。如果得到的子问题相对来说还太大,则可反复使用分治策略将这些子问题分成更小的同类型子问题,直至产生出不用进一步细分就可求解的子问题。由此可知,分治法求解很自然地可用一个递归过程来表示。

总而言之:分治法作为一种算法设计策略,要求分解所得的子问题是同类问题,并要求原问题的解可以通过组合子问题的解来获取。

1.3分治法的基本步骤

**

divide-and-conquer**(P)—分而治之

  {

​    **if** ( | P | <= n0) **adhoc**(P);   //解决小规模的问题

​    **divide** P into smaller subinstances P1,P2,...,Pk;//分解问题

​    **for** (i=1,i<=k,i++)

​      yi=**divide-and-conquer**(Pi);  //递归的解各子问题

​    **return** merge(y1,...,yk);  //将各子问题的解合并为原问题的解

  }
2.常见使用场景
2.1二分搜索技术

给定已按升序排好序的n个元素a[0:n-1],现要在这n个元素中找出一特定元素x


代码如下 使用语言java

 //返回单个数据
    public static int binary(int[] arr,int left,int right,int num){
        if (left > right){
            return -1;
        }
        int mid = (left + right) / 2;
        if (arr[mid] < num){
            return binary(arr, mid + 1, right, num);
        }else if (arr[mid] > num){
            return binary(arr, left, mid - 1, num);
        }else{
            return mid;
        }
    }


//返回所有数据
    public static ArrayList binaryAll(int[] arr,int left,int right,int num){
        if (left > right){
            return null;
        }
        int mid = (left + right) / 2;
        if (arr[mid] < num){
             return binaryAll(arr, mid + 1, right, num);
        }else if (arr[mid] > num){
             return binaryAll(arr, left, mid - 1, num);
        }else{
            ArrayList list = new ArrayList();

            int i = mid;
            int j = mid;
            //向左扫描
            while (--i >=0 && arr[i] == num){
                list.add(i);
            }
            list.add(mid);
            //向右扫描
            while (++j <= (arr.length - 1) && arr[j] == num){
                list.add(j);
            }
            return list;

        }
    }
2.2合并排序

合并排序算直观地操作如下:

分解:将 n 个元素分成各含 n/2 个元素的子序列;

解决:用合并排序法对两个子序列递归地排序;

合并:合并两个已排序的子序列以得到排序结果。

在对子序列排序时,其长度为 1 时递归结束。单个元素被视为是已排好序的。

img


代码如下 使用语言java

public class MergeSort {

    public static void main(String[] args) {
        int[] arr = new int[100000000];
        for (int i = 0; i <100000000; i++) {
            arr[i] = (int)(Math.random() * 800000000);
        }
        Date date1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy--mm-dd HH:mm:ss");
        String d1 = simpleDateFormat.format(date1);
        System.out.println(d1);

        int[] temp = new int[arr.length];
        mergeSort(arr, 0, arr.length - 1, temp);

        Date date2 = new Date();
        String d2 = simpleDateFormat.format(date2);
        System.out.println(d2);
    }

    //分治
    public static void mergeSort(int[] arr,int left,int right,int[] temp){
       if (left < right){
           int mid = (left + right) / 2;
           mergeSort(arr, left, mid, temp);//左边递归
           mergeSort(arr,mid + 1,right,temp);//右边递归
           merge(arr, left, mid, right, temp);
       }
    }
    //合并
    public static void merge(int[] arr,int left,int mid,int right,int[] temp){
        //第一步 排序到temp数组
        int i = left;//左指针
        int j = mid + 1;//左二指针
        int t = 0;//右指针
        while (i <= mid && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[t] = arr[i];
                t++;
                i++;
            } else {
                temp[t] = arr[j];
                t++;
                j++;
            }
            }
            //第二步将剩余组数据 填充到temp
                while (i <= mid){
                    temp[t] = arr[i];
                    t++;
                    i++;
                }

                while (j <= right){
                    temp[t] = arr[j];
                    t++;
                    j++;
                }

            //将temp中元素拷贝到原数组
            t = 0;
            int tempLeft = left;
            while (tempLeft <= right){
                arr[tempLeft] = temp[t];
                t++;
                tempLeft++;
            }
        }

    }

程序运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GkOJOPaX-1590819436132)(C:\Users\rw\AppData\Roaming\Typora\typora-user-images\1590818675755.png)]

一亿条数据排序使用时间为21秒,综合性能较好,

**最坏时间复杂度:**O(nlogn)

**平均时间复杂度:**O(nlogn)

**辅助空间:**O(n)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o6kAXex9-1590819436134)(C:\Users\rw\AppData\Roaming\Typora\typora-user-images\1590818759883.png)]

2.3快速排序

快速排序算法基本思想是:对输入的子数组a[p:r],按以下三个步骤进行排序。

(1) 分解(Divide):以a[p]为基准元素将a[p:r]划分成3段a[p:q-1],a[q]和a[q+1:r],使得a[p:q-1]中任何一个元素小于等于a[q],而a[q+1:r]中任何一个元素大于等于a[q]。下标q在划分过程中确定。

(2) 递归求解(Conquer):通过递归调用快速排序算法分别对a[p:q-1]和a[q+1:r]进行排序。

(3) 合并(Merge):由于对a[p:q-1]和a[q+1:r]的排序是就地进行,所以在a[p:q-1]和a[q+1:r]都已排好的序后,不需要执行任何计算,a[p:r]就已排好序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJtFpVqd-1590819436153)(C:\Users\rw\AppData\Roaming\Typora\typora-user-images\1590819049364.png)]


代码如下 使用语言java

public class QuickSort{

    public static void main(String[] args) {
        int[] arr = new int[100000000];
        for (int i = 0; i <100000000; i++) {
            arr[i] = (int)(Math.random() * 80000000);
        }
        Date date1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy--mm-dd HH:mm:ss");
        String d1 = simpleDateFormat.format(date1);
        System.out.println(d1);

        quickSort(arr);

        Date date2 = new Date();
        String d2 = simpleDateFormat.format(date2);
        System.out.println(d2);
    }

    public static void quickSort(int[] arr){
        if (arr == null || arr.length == 0 || arr.length == 1){
            return;
        }
        sort(arr, 0, arr.length - 1);
    }

    //核心算法
    public static void sort(int[] arr,int left,int right){
        //退出判断
        if (left >= right){
            return;
        }

        int base = arr[left];//基数
        int temp;
        int i = left;
        int j = right;

        while (i < j){

            //寻找右指针位置
            while (j > i && arr[j] >= base){
                j--;
            }
            //寻找左指针位置
            while (i < j && arr[i] <= base){
                i++;
            }
            if (i < j){
                temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        //交换base位置
        arr[left] = arr[i];
        arr[i] = base;

        sort(arr, left, i - 1);
        sort(arr, i + 1, right);
    }
}

程序运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rD0XSN08-1590819436163)(C:\Users\rw\AppData\Roaming\Typora\typora-user-images\1590819178409.png)]

一亿条数据使用时间为17秒

&最坏时间复杂度:O(n2**)**

&**平均时间复杂度:**O(nlogn)

&**辅助空间:**O(n)O(logn)

在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值