分治思想(多图)——快排排序和归并排序经典图解

分治思想

  分治法(divide and conquer,D&C) :将原问题划分成若干个规模较小而结构与原问题一致的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。
➢容易确定运行时间,是分治算法的优点之一。
➢分治模式在每一层递归上都有三个步骤:

  • 一分解(Divide) :将原问题分解成一系列子问题;
  • 一解决(Conquer):递归地解各子问题。若子问题足够小,则直接有解;
  • 一合并(Combine):将子问题的结果合并成原问题的解。

分治关键点

  • 原问题可以一直分解为形式相同子问题,当子问题规模较小时,可自然求解,如一个元素本身有序。
  • 子问题的解通过合并可以得到原问题的解。
  • 子问题的分解以及解的合并一定是比较简单的,否则分解和合并所花的时间可能超出暴力解法,得不偿失。
快排排序

  快排是Java里内部封装的算法思想。他的事件复杂度是O(nlgn)级别的,相对于O(n^2)快了很多。快排将一个数组的排序分为两个子数组的排序问题,每个子数组的问题规模是O(n)级别的。所以事件复杂度为O(nlgn)。取数组的主元,令主元左边的值都小于主元,主元右边的值都大于主元。以主元为界限分为两个子数组,对两个子数组继续按此取主元,直至单元素数组。找主元位置介绍两种方法,第一种是单向法,拿图举个例子:
在这里插入图片描述
每次主元取第一个元素,然后分别设立头尾指针。每次检测头指针所指数值和主元比较,如果小于主元,头指针右移。如果大于主元,和尾指针所指数之交换数值,尾指针左移。图中2<6,所以右移头:
在这里插入图片描述
头指针的8>6,与尾指针所指7交换位置,尾指针左移。
在这里插入图片描述
头指针的7>6,与尾指针所指9交换位置,尾指针左移。
在这里插入图片描述
头指针的9>6,与尾指针所指4交换位置,尾指针左移。
在这里插入图片描述
头指针的4<6,头指针右移。
在这里插入图片描述
头指针的1<6,头指针右移。
在这里插入图片描述
头指针的5<6,头指针右移。
在这里插入图片描述
头指针的3<6,头指针右移。
在这里插入图片描述
头指针小于尾指针,此时,主元指针的值与尾指针的值交换数值。
在这里插入图片描述
这时,一轮找主元就结束了,我们需要返回主元6的索引,可以看到,6的左边全都是小于6的,6的右边都是大于6的值,分成了两块,但是两个子数组内部没有顺序,我们可以再分别对两个子数组找主元,最后会达成整体有序的结果。

	public static void quickSort(int[] A,int p,int r){
        if (p < r) {
            //找到主元位置
            int q = partition(A, p, r);
            //主元左边排序
            quickSort(A, p, q - 1);
           	//主元右边排序
            quickSort(A, q + 1, r);
        }
    }
	// 单向扫描
    private static int partition(int[] a, int p, int r) {
        int pivot = a[p];
        int sp = p + 1;
        int bigger = r;
        while (sp <= bigger) {
            if (a[sp] <= pivot) {
                sp++;
            }
            else {
                Util.swap(a, sp, bigger);
                bigger--;
            }
        }
        Util.swap(a, p, bigger);//自写的交换数值方法
        return bigger;
    }

另外一种找主元的方法是双向法,我们还是用上述例子演示一轮。
还是先定数组第一个值为主元,然后定首尾指针。头指针向右移,检测到大于主元值停下,尾指针向左移,检测到小于主元值停下,交换头尾指针对应的数组数值。
在这里插入图片描述
头指针左移动。
在这里插入图片描述
8>6头指针停,开始移动尾指针。
在这里插入图片描述
4<6,尾指针停止,交换两数数值。
在这里插入图片描述
继续向左移动头指针。
在这里插入图片描述
8>6,头指针停止,移动尾指针。
在这里插入图片描述
检测尾指针小于头指针,遍历数组结束,交换主元和尾指针数值。
在这里插入图片描述
双向法,需时刻检测头尾指针是否满足头>尾。满足情况,就代表这一轮已经结束。

	public static void quickSort(int[] A,int p,int r){
        if (p < r) {
            int q = partition2(A, p, r);
            quickSort(A, p, q - 1);
            quickSort(A, q + 1, r);
        }
    }
    //    双向扫描
    public static int partition2(int[] A,int p,int r){
        int pivot=A[p];
        int sp = p + 1;
        int bigger = r;
        while (sp<=bigger){
            while (sp<=bigger&&A[sp]<=pivot) {
                sp++;
            }
            while (sp<=bigger&&A[bigger]>pivot) {
                bigger--;
            }
            if (sp<bigger){
                Util.swap(A,sp,bigger);
            }
        }
        Util.swap(A,p,bigger);
        return bigger;
    }

快排这个方法思想对我们写算法有很大的帮助,如何去把一个O(n^2)的算法优化,我们可以使用分治思想,把一个大问题变成小问题再合并,小问题的规模只要够小,我们就可以写出不错的算法。

归并排序

  归并排序就是一个非常标准的分治思想了,取数组的中间位置,分别对前后排序,然后再合前后。

	private static int[] helper;
    public static void Sort(int[] arr){
        helper = new int[arr.length];
        Sort(arr,0,arr.length-1);
    }
    //归并排序
    public static void Sort(int[] A,int low,int high){
        if (low < high) {
            int middle=low+((high-low)>>1);
            Sort(A, low, middle);
            Sort(A, middle + 1, high);
            Merage(A,low,high,middle);
        }
    }
	//合并
    private static void Merage(int[] a, int low, int high, int middle) {
        System.arraycopy(a,low,helper,low,high-low+1);
        int left=low;
        int right = middle + 1;
        int index=low;
        while (left<=middle&&right<=high){
            if (helper[left] >= helper[right]) {
                a[index++]=helper[right++];
            } else if (helper[left] < helper[right]) {
                a[index++] = helper[left++];
            }
        }
        while (left <= middle) {
            a[index++]=helper[left++];
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是矩阵相乘的分治法流程: 1. 将两个矩阵 A 和 B 分别划分为四个子矩阵,即 A11, A12, A21, A22 和 B11, B12, B21, B22。 2. 对于每个子矩阵,递归地计算它们的乘积,即 C11 = A11 x B11 + A12 x B21,C12 = A11 x B12 + A12 x B22,C21 = A21 x B11 + A22 x B21,C22 = A21 x B12 + A22 x B22。 3. 将计算得到的子矩阵 C11, C12, C21, C22 组合成一个新的矩阵 C。 4. 返回矩阵 C。 下面是对应的流程: ``` ------------------------ | A x B | ------------------------ | | ------------------------------------------------- | | | | | | ----------------- ----------------- ----------------- | A11 | | A12 | | A21 | ----------------- ----------------- ----------------- | A21 | | A22 | | A22 | ----------------- ----------------- ----------------- | | | | | | ----------------- ----------------- ----------------- | B11 | | B12 | | B21 | ----------------- ----------------- ----------------- | B21 | | B22 | | B22 | ----------------- ----------------- ----------------- | | | | | | ----------------- ----------------- ----------------- | C11=A11xB11 | | C12=A11xB12 | | C21=A21xB11 | ----------------- ----------------- ----------------- | C21=A21xB12 | | C22=A22xB22 | | C22=A22xB22 | ----------------- ----------------- ----------------- | | ------------------ | C = AB | ------------------ ``` 这个流程展示了一个简单的矩阵相乘的分治法,该算法的时间复杂度为 O(n^3),其中 n 是矩阵的大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值