排序算法之六 - 合并排序

2014-11-24 00:34:59 · 作者: · 浏览: 0


一、分治法

归并排序是基于分治法思想来解决排序的问题,分治模式在每一层递归上都有三个步骤:
分解(divide):将原问题分解成一系列子问题;
解决(conquer):递归地解各子问题。若子问题足够小,则直接求解;
合并:将子问题的结果合并成原问题的解。
\


二、归并排序分析与实现

\
输入数组: {6, 5, 3, 1, 8, 7, 2, 4}
\

1. 递归进行分解操作,将n个元素分成各含n/2个元素的子序列;

下面按照数组分解操作先后排列: 第一步 [6,5,3,1] 第二步 [6,5] 第三步 [6] 第四步 [5] 第五步 [3,1] 第六步 [3] 第七步 [1] 第八步 [8,7,2,4] 第九步 [8,7] 第十步 [8] 第十一步 [7] 第十二步 [2,4] 第十三步 [2] 第十四步 [4]

2. 分解操作源码

       private void mergeSort(int a[], int first, int last, int temp[]) {
             if (first < last) {
                   int mid = (first + last) / 2;
                  mergeSort(a, first, mid, temp); // 数组左侧区域进行排序
                  mergeSort(a, mid+1, last, temp); // 数组左侧区域进行排序
                  mergeArray(a, first, mid, last, temp); // 合并以上已排序数组
            }
      }

3. 递归进行解决操作,用合并排序法对两个子序列进行递归地排序;
4. 递归进行合并操作,合并两个已排序的子序列以得到排序结果。

下面的处理是直接把解决与合并操作放到了一起执行,按照数组解决、合并操作先后顺序排列: 第一步 [6] [5] -> [5,6]
第二步 [3] [1] -> [1,3] 第三步 [5,6] [1,3] -> [1,3,5,6] 第四步 [8] [7] -> [7,8] 第五步 [2] [4] -> [2,4] 第六步 [7,8][2,4] -> [2,4,7,8] 第七步 [1,3,5,6] [2,4,7,8] -> [1,2,3,4,5,6,7,8]
具体执行次序: 第一步在分解操作第四步与第五步之间执行
第二步在分解操作第七步与第八步之间执行
第三步在解决第二步后面执行
第四步在分解操作第十一步与第十二步之间执行
第五步在分解操作第十四步之后执行
第六步在解决第五步后面执行
第七步在解决第六步后面执行

5. 解决与合并操作源码

对分组进行排序与合并有多种方式 1) 《算法导论》中介绍的是分别放入两个临时数组中,再分别按大小排序从两个数组中取出来。 2) 使用一个临时数组,把逻辑上分为两部分的元素按大小排序放入临时数组中,把排序好的临时数组元素依次放入最终结果数组中。 3) 可以不使用临时数组,直接对逻辑上分为两部分的元素进行插入排序。
代码基于上面的第2种方法实现的,感兴趣的话可以自己尝试使用另外两种方式或者不用递归实现此函数。
       private void mergeArray(int a[], int first, int mid, int last, int temp[]) {
             int i = first;
             int j = mid + 1;
             int k = 0;
            
             while (i <= mid && j <= last) { // 左右都没走出边界
                   if (a[i] <= a[j]) { // 右小到大依次放入临时数组
                        temp[k++] = a[i++];
                  } else {
                        temp[k++] = a[j++];
                  }
            }
            
             // 左右最少有一个已经执行完
            
             while (i <= mid) {
                   // 左边剩余依次放入临时数组
                  temp[k++] = a[i++];
            }
             while (j <= last) {
                   // 右边剩余依次放入临时数组
                  temp[k++] = a[j++];
            }
            
             // 排序好的数组放入最终数组中
             for (i = 0; i < k; i++) {
                  a[first+i] = temp[i];
            }
      }

6. mian函数用于测试

       public static void main(String[] args) {
             int [] array = {6, 5, 3, 1, 8, 7, 2, 4};
            System. out .println( Arrays.toString(array) );
            
            MergeSort mergeSort = new MergeSort();
            mergeSort.mergeSort(array);
            System. out .println( Arrays.toString(array) );
      }
      
       public void mergeSort( int a[]) {
            mergeSort(a, a. length );
      }
      
       public void mergeSort( int a[], int n) {
             if (a == null) {
                   throw new NullPointerException();
            }
             if (n > a.length ) {
                   throw new ArrayIndexOutOfBoundsException();
            }
            
             int [] temp = new int[n];
            mergeSort(a, 0, n-1, temp);
            
            temp = null ;
      }

输入数组:[6, 5, 3, 1, 8, 7, 2, 4]
输出结果:[1, 2, 3, 4, 5, 6, 7, 8]

三、时间空间复杂度

最差时间复杂度:O(nlogn)
最优时间复杂度:O(n) 平均时间复杂度:O(nlogn) 最差空间复杂度:O(n)

四、参考资料

《白话经典算法系列之五 归并排序的实现》
《算法导论》 2.3 算法设计 《归并排序 - 维基百科》 《从零开始学算法:十种排序算法介绍(中)》 《经典排序算法 - 归并排序Merge sort》


<< 返回 Java 学习文章 - 索引