分治算法: 归并排序(详解)

分治算法:

分治算法和冒泡排序一样有着好听的名字。工作方案如下:
1. 将一个问题划分为同一类型的若干子问题,子问题最好规模相同。
2. 对这些子问题求解(一般用递归)
3. 有必要的话,合并这些子问题,得到原始问题的答案。


归并排序:

根据分治思想,我们可以实现归并排序:要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。这个算法的基本操作是合并两个已排序的表,因为这两个表是有序的,所以可以通过一次遍历来完成(花费线性时间)。

实现分析:
实现归并的一种直接的方法是将两个不同的有序数组归并到第三个数组中,这会导致归并排序的空间复杂度为O(N),这也是归并排序的主要缺点。我们可以修改代码,使得它可以在原地归并,但已有的实现都过于复杂,没有实际意义。
如果在每次递归时都生成一个临时数组,那么在任一时刻就可能用logN个临时数组处在活动期。但实际上,因为merge方法在mergeSort的最后一行,我们可以只使用一个公有的数组。我在实现中把该数组放在类的下面,当然也可以将它放在public型的mergeSort方法中,通过参数传递。

以下是归并排序的自顶向下的实现代码:

    private static Integer[] aux;

    public static void mergeSort(Integer[] r){
        aux = new Integer[r.length];
        mergeSort(r, 0, r.length - 1);
    }

    private static void mergeSort(Integer[] r, int left, int right){
        if(right<=left)//如果数组大小不大于1,不再递归
            return;
        int mid = (left+right)/2;
        mergeSort(r, left, mid);
        mergeSort(r, mid+1, right);
        merge(r, left, mid, right);
    }

    private static void merge(Integer[] r, int left, int mid, int right){
        int i=left, j=mid+1;

        for(int k=left; k<=right; k++)
            aux[k] = r[k];

        for(int k=left; k<=right; k++)
            if(i>mid)//mid是左侧数列的最后一个位置。这里判断i是否溢出
                r[k] = aux[j++];
            else if(j>right)
                r[k] = aux[i++];
            else if(aux[i]<aux[j])//比较元素的大小
                r[k] = aux[i++];
            else
                r[k] = aux[j++];
    }

算法分析:
假设数组的长度N是2的k次方(N=2^k),这样的话我们总可以把它分成两个等长的数组。对于N=1,归并排序所用时间为1;否则,归并排序所有时间为排序两个子数组的时间加上合并的时间
计算过程如下:
归并排序时间复杂度分析

对于N不是2的幂次的数组,答案几乎是一样的。所以归并排序的时间复杂度为O(NlogN),空间复杂度为O(N)

与其他的O(NlogN)排序算法比较,归并排序的运行时间严重依赖于比较元素和在数组中移动元素的相对开销,这和语言有关。如,在Java中比较操作是昂贵的,但移动元素的开销的较小的;而C++通常相反。


总结:

归并排序的时间复杂度为O(NlogN),空间复杂度为O(N)。可以自顶向下或自底向上地实现。对于长度很小的数组,递归地开销还是比较昂贵的,我们可以对小数组使用插入排序这样的算法,以此改进归并排序的性能。总之,归并排序是一种渐进最优的基于比较排序的算法。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
归并排序是一种基于归并操作的有效排序算法,它采用分治法的思想。算法的基本思想是将已有序的子序列合并,得到完全有序的序列。在归并排序中,先将待排序序列通过二分法分为若干个有序子序列,然后再将这些子序列两两合并,直到最终得到完全有序的序列。归并排序通过牺牲空间来换取更高的时间效率。 以下是C语言实现归并排序的具体代码: ```c #include <stdio.h> // 合并两个有序数组 void merge(int arr[], int left, int mid, int right) { int i, j, k; int n1 = mid - left + 1; int n2 = right - mid; // 创建临时数组 int L[n1], R[n2]; // 将数据复制到临时数组中 for (i = 0; i < n1; i++) L[i] = arr[left + i]; for (j = 0; j < n2; j++) R[j] = arr[mid + 1 + j]; // 归并临时数组到原数组中 i = 0; j = 0; k = left; while (i < n1 && j < n2) { if (L[i] <= R[j]) { arr[k] = L[i]; i++; } else { arr[k] = R[j]; j++; } k++; } // 复制剩余的元素 while (i < n1) { arr[k] = L[i]; i++; k++; } while (j < n2) { arr[k] = R[j]; j++; k++; } } // 归并排序 void mergeSort(int arr[], int left, int right) { if (left < right) { int mid = left + (right - left) / 2; // 分 mergeSort(arr, left, mid); mergeSort(arr, mid + 1, right); // 治 merge(arr, left, mid, right); } } int main() { int arr[] = {12, 11, 13, 5, 6, 7}; int n = sizeof(arr) / sizeof(arr[0]); printf("排序前的数组: \n"); for (int i = 0; i < n; i++) printf("%d ", arr[i]); mergeSort(arr, 0, n - 1); printf("\n排序后的数组: \n"); for (int i = 0; i < n; i++) printf("%d ", arr[i]); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值