排序算法--归并排序(弱分治)

1,归并排序是建立在归并操作上的一种有效的排序算法。该算法也是采用分治法(Divide and Conquer)的一个非常典型的应用。

难点在于分治,分治是将原有数列分成一个个小的的有序数列,然后再合并。

2,例如集合{7,6,5,4,3,2,1},分治归并的逻辑如下:

groupsize = 1:

建立一个临时数组,将{7,6}进行排序

{6,7}

 ↓

写入到原有数列

{6,7,5,4,3,2,1}

 

新建一个临时数组,将{5,4}进行排序

{4,5}

 ↓

写入到原有数列

{6,7,4,5,3,2,1}

 

新建一个临时数组,将{3,2}进行排序

{2,3}

 ↓

写入到原有数列

{6,7,4,5,2,3,1}

 

新建一个临时数组,将{1}进行排序

 {1}

 ↓

写入到原有数列

{6,7,4,5,2,3,1}

 

groupsize = 2:

 新建一个临时数组,将{6,7,4,5}进行排序

 {4,5,6,7}

 ↓

写入到原有数列

{4,5,6,7,2,3,1}

 

新建一个临时数组,将{2,3,1}进行排序

 {1,2,3}

  ↓

写入到原有数列

{4,5,6,7,1,2,3}

 

groupsize = 4:

 新建一个临时数组,将{4,5,6,7,1,2,3}进行排序

{1,2,3,4,5,6,7}

  ↓

写入到原有数列

{1,2,3,4,5,6,7}

 

3,详见代码并解析

  

 第一轮:groupSize = 1,autoArray.length = 2;(临时的集合用来放置将要排序的分治的数列)

 {13,12,11,10,9,8,7,6,5,4,3,2,1}

                      ↓

 {12,13,11,10,9,8,7,6,5,4,3,2,1}

                      ↓

 {12,13,10,11,9,8,7,6,5,4,3,2,1}

                      ↓

 {12,13,10,11,8,9,7,6,5,4,3,2,1}

                      ↓

 {12,13,10,11,8,9,6,7,5,4,3,2,1}

                      ↓

 {12,13,10,11,8,9,6,7,4,5,3,2,1}

                      ↓

 {12,13,10,11,8,9,6,7,4,5,2,3,1}

                      ↓

 {12,13,10,11,8,9,6,7,4,5,2,3,1autoArray.length = 1; 只剩下最后一个数了

 

第二轮: groupSize = 2,autoArray.length = 4

                      ↓

 {10,11,12,13,8,9,6,7,4,5,2,3,1}

                      ↓

 {10,11,12,13,6,7,8,9,4,5,2,3,1}

                      ↓

 {10,11,12,13,6,7,8,9,2,3,4,5,1}

                    ↓

 {10,11,12,13,6,7,8,9,2,3,4,5,1} autoArray.length = 1; 只剩下最后一个数了

 

第三轮:groupSize = 4,autoArray.length = 8

                    ↓

{6,7,8,9,10,11,12,13,2,3,4,5,1}

                    ↓

{6,7,8,9,10,11,12,13,1,2,3,4,5} autoArray.length = 5; 只剩下最后五个数了

 

第四轮:groupSize = 8,autoArray.length = 13

{1,2,3,4,5,6,7,8,9,10,11,12,13,}

 

 

上部分代码是视为分治,下部分视为归并。

归并是建立在分治的基础上,归并的前提是两个要归并的数列都是两个按要求排序的有序数列。

因此刚开始的归并是从两个长度各自为1 开始的。

private static void merge(int[] array, int low, int middle, int high) {

 

}

array:源数列

low:源数列中即将排序的两个有序数列中最小的下标

middle:前一个有序数列中最后一位数的下标

high:源数列中即将排序的两个有序数列中最大的下标

 

例如:

2,6     4,554

low:0

middle:1

high:3

 

再如:

2,4,5,8     3,7

low:0

middle:3

high:5

 

所以在第一轮merge的时候,是以两个长度分别为1的有序数列开始的

13,12

merge的时候,会建一个数组,大小为两个数列长度之和,所以本次数组长度为2,

比较两个数列首部的下标index1  index2

 

源数列        【】                                    【】                   

                   index1/low /middle               index2/high              

 

临时数组    【】  【】

 

归并方法:       比较index1 和 index2  两个数列中最小的值,因为两个数列的长度可能不一样,所以需要限定条件,

index1<=middle  同时 index2 <= high 的情况

刚开始一定是满足这个条件的,找出最小的之后,放入到临时数组中,同时下标加1,下次就不会再比较,可能一个数列全部都比较小,然后就全部放入到临时数组中了,

那另一个数列也要将剩下的放进去,所以需要有两个判断条件

当:index1<= middle,将左边的剩余数全部写入

当:index2 <= high,将右边的剩余数全部写入

 

 

分治中:middle:前一个有序数列中最后一位数的下标,所以一般是low + groupSize - 1

但是当low + groupSize - 1 >high 的时候,说明已经超过了源数列的长度,

这时候这部分分治应该是剩余的的最后一段中,且在一个有序的集合中

{6,7,8,9,10,11,12,13,2,3,4,5,1}

下次排序的时候两个数列分别是2,3,4,5 和1

low应该是8

middle 应该是 11

high应该是12

如果用low+high/2就错了。

 

但是当

 {10,11,12,13,6,7,8,9,2,3,4,5,1}

下次排序的时候两个数列分别是1

low应该是12

middle应该是12

high 是12

如果用low + groupSize - 1就错了

所以需要分类讨论下。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

           

 

posted @ 2018-08-30 11:24  Chris,Cai  阅读(206)  评论(0编辑  收藏  举报