算法学习之归并排序

来源:互联网 发布:北京java工程师待遇 编辑:程序博客网 时间:2024/06/05 12:00
归并排序O(n) = NlogN 
归并排序思想:把数组拆分成多个有序的数组,然后将这些有序的数组排序合并
归并排序的优势就在于在问题规模(数组长度)不变的情况下通过减少for循环的执行次数从而降低时间复杂度
以下是对两个有序数组排序的代码:

public void merge(int[] array,int low,int mid,int high){int[] array2 = new int[high - low +1];int l=low;int k = mid+1;int f = 0;while(l <= mid && k <=high){if(array[l] < array[k]){array2[f] = array[l];l++;f++;}else{array2[f] = array[k];k++;f++;}}while(l<=mid){array2[f] = array[l];l++;f++;}while(k<=high){array2[f] = array[k];f++;k++;}for(int i=low,j=0;j<array2.length;j++,i++){array[i] = array2[j];}}
归并排序分为两步:
  1. 拆分有序数组
  2. 把有序数组合并排序
合并数组的算法java代码已经给出,下边是拆分的算法:
拆分后合并的难度在于如何确定for循环的次数,如何确定每次循环时合并前的数组长度(如果合并前数组长度确定了,那么合并后的数组长度就是2倍的合并前数组长度)
对于合并的详细过程我画了如下图:

1.一个无序数组中,最小的有序单位为1,就是说把一个长度为N的数组,分割成N份,那么这N份必然都是有序的
2.将分成的N份合并排序,最终就得到了一个有序的数组
确定合并次数:
我们假设最小有序数组的大小为gap,那么那么每次合并后新数组的大小满足 gap = 2* gap,并且新数组的大小是不能大于原数组大小的那么gap从1开始增找到最接近原数组长度所需要的for循环次数就是合并的次数(考虑到数组长度是奇数还是偶数,可能需要再多一次合并)
确定合并的数组下标:
合并前的数组大小为gap,我们假设当前数组大小(gap)下的合并次数为i,那么两个合并的数组范围就是2* gap* i ~ 2*gap*(i+1)-1
合并次数与合并数组的下标确定后,传递给排序函数的每个参数就确定下来了(mid就是范围中的中间位置,用来分离合并范围2* gap* i ~ 2*gap*(i+1)-1中的两个有序数组),完成以下算法:
public int[] sort(int[] list){for(int gap=1;gap < list.length;gap = 2*gap){this.mergeSort(list, gap);}for(int i=0;i<list.length;i++){        System.out.print(list[i]+",");}return list;}public void mergeSort(int[] array,int gap){for(int i=0;2*gap*i+gap-1<array.length;i++){if(2*gap*(i+1)-1 < array.length){merge(array,2*gap*i,2*gap*i+gap-1,2*gap*(i+1)-1);}else{merge(array,2*gap*i,2*gap*i+gap-1,array.length-1);}}}
除了循环合并,还有递归合并的方式,递归合并的优势就在于不用花很大的计算去确定数组的下标范围,递归算法理解起来比较困难
归并排序的时间复杂度比较低,效率比较高,大数组排序时候有优势,但是算法比较复杂

0 0
原创粉丝点击