排序算法——分治思想与归并排序

1、分治法思想

    分治思想主要通过递归来实现,每层递归中主要包括三个步骤:

  1. 分解:即将原问题划分为若干子问题,子问题的形式要保证和原问题一致,但规模更小。
  2. 解决:当划分子问题的规模足够小时,停止递归,求解子问题,获取子问题的解决结果并返回给上一层。
  3. 合并:将子问题的解决结果进行合并,得到原问题的解。

    也就是说,分治思想就是将一个大问题用过递归层层分解为无数个子问题,这些子问题要保证与与原问题是一个类型,只不过规模更小更加容易求解,但求解过程相同。

2、归并排序算法思想

    1、思考:如果给了我们两个已经做好排序的数组(相同排序规则)A和B,我们该如何将其合并到一个有序数组C?

    这个问题很简单,依次从两个数组中取元素比较然后放进C中即可。而如果A、B数组都只有一个元素时,他们都成为了一个有序数组,则可以直接进行比较放在数组C中,返回有序的数组C。

    2、通过分治思想对无序数组排序问题进行划分:对于一个无序的数组array

(1)分解:通过递归将无序数组进行划分,每次从array.length/2处划分为两个数组,直到划分后的子数组规模为1。

(2)解决:当子数组规模为1时,此时的最小规模数组已经自动变为一个有序数组,递归终止返回有序数组结果。

(3)合并:接收下一层递归所返回的两个有序子数组,将这两个有序子数组的数据进行合并为一个新的有序数组,然后返回该有序数组给上一层递归。

    3、排序实例:为方便理解递归划分数组,可以建立一个递归树形图(理解递归比较方便)。以数组int[] arr = {3,23,4,13,4,5,63,6,2}为例,其数组划分树形图为

efb0c89ca179efda22fb6467957876ea196.jpg

 

    那么接下来就是将叶子节点已经排序好的数组返回给上一层递归,在上一层中处理合并一个新的有序数组再返回给上一层,重复该步骤直到根节点层就可以将原数组变为一个有序数组。

1525233026764472.png

    也就是说,归并算法中递归调用的方法主要为两部分,分别是分割数组以及数组合(将两个有序数组进行合并)。伪代码如下

merge(int[] arr){
    if(arr.length < 2){
        return arr;//如果传入的数组长度为1那么就直接返回
    }
    int[] left = splitLeft(arr);//划分获取左子数组
    int[] right = spliteRight(arr);//划分获取右子数组
    
    left = merge(left);//递归调用,获得排序后的左子数组
    right = merge(right);//递归调用,获得排序后的右子数组
    
    arr = sort(left,right);//将左右有序子数组合并为一个有序数组
    return arr;//返回
}

    具体代码实现(个人写的依据算法逻辑思想写的一个示例,划分左右子数组都是新创建数组进行赋值,空间复杂度很高,而且提高了时间复杂度,但是主要是便于理解)

个人实现:

/**
 * 
 * @Description:分治算法,利用递归,将一个数组元素不断的分割为两个数组,直到数组分割到只剩下一个元素
 * 然后返回给上一层,主要是保证返回给上一层中的两个数组是已经排好序的数组,在上一层中进行排序合并,然后再返回
 * 就可以最终得到一个有序数组
 */
public class Sort {
    public static int[] merge(int[] arr){
        
        int left = 0;
        int right = arr.length;
        if (right == 1) {
            int[] endArr = {arr[left]};
            return endArr;
        }
        
        //数组划分
        int mid = (left + right) / 2;
        int[] leftArr = new int[mid-left];
        int[] rightArr = new int[right - mid];
        for (int i = 0; i <= mid-1; i++) {
            leftArr[i] = arr[i];
        }
        for (int i = 0,j=mid; i < rightArr.length && j < arr.length; i++,j++) {
            rightArr[i] = arr[j];
        }
        
        //递归调用,获取排序完成的左右子数组
        leftArr = merge(leftArr);
        rightArr = merge(rightArr);
        
        //合并两个数组并排序
        return sort(arr, leftArr, rightArr);
    }
    public static int[] sort(int[] arr, int[] left, int[] right){
        int i = 0;
        int j = 0;
        for (int index = 0; index < arr.length; index++){
            if (i >= left.length && j < right.length) {
                arr[index] = right[j];
                j++;
                continue;
            }
            if (j >= right.length && i < left.length) {
                arr[index] = left[i];
                i++;
                continue;
            }
            if (left[i] > right[j]){
                arr[index] = right[j];
                j++; 
            } else {
                arr[index] = left[i];
                i++;
            }
        }
        return arr;
    }
    public static void main(String[] args) {
        int[] arr = {3,23,4,13,4,5,63,6,2};
        for(int i:merge(arr)) {
            System.out.println(i);
        }
    }
}

较好的实现:

public class Solution {
    /*
     * @param A:an integer array
     * @return:
     */
    public voidsortIntegers2(int[] A) {
        // writeyour code here
        //利用归并排序对数组A进行排序
       mergeSort(A,0,A.length-1);
    }
    //归并排序
    public void mergeSort(int[] A,int start,int end){
       if(start>=end) return;
        int middle= (start+end)/2;
       mergeSort(A,start,middle);
       mergeSort(A,middle+1,end);
        //归并排序需要分配的临时数组
        //这是归并排序的核心
        int []temp  = new int[end-start+1];
        inti=start;
        int j =middle+1;
        intindex=0;
       while(i<=middle&&j<=end){
           if(A[i]<=A[j]){
               temp[index++] = A[i++];
            }else{
               temp[index++] = A[j++];
            }
        }
       while(i<=middle){
           temp[index++] =A[i++];
        }
       while(j<=end){
           temp[index++] =A[j++];
        }
        i=start;
        index = 0;
       for(;i<=end;i++){
            A[i] =temp[index++];
        }
    }
}

 

转载于:https://my.oschina.net/ProgramerLife/blog/3079597

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值