图解十大排序
4.1 前文回顾
在上篇文章中给大家介绍了第一种时间复杂度为 O(nlog2 n)的排序方法,那么今日就让我们来学习第二种,在介绍之前,大家还记得选择排序嘛,和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。
4.2 归并排序
归并排序 是建立在归并操作上的一种有效的排序算法。该算法是采用分治的思想。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并
官方回答看不懂?没关系,总结来说就是:
通过将当前乱序数组分成长度近似的两份,分别进行 递归 调用,然后再对这两个排好序的数组,利用两个指针,将数据元素依次比较,选择相对较小的元素存到一个 辅助数组 中,再将 辅助数组 中的数据存回 原数组 。
让我们来看一个例子 给定无序数组 : [3, 5, 2, 6, 4, 8, 7, 9]
- 将数组中的元素进行对半分组,第1层分成2个大组,每组n/2个元素; 第2层分成4个小组,每组n/4个元素; 第3层分成8个更小的组,每组n/8个元素; …… 一直到每组只有一个元素。
- 当每个小组内部比较出先后顺序以后,小组之间会展开进一 步的比较和排序,合并成一个大组;大组之间继续比较和排序,再合 并成更大的组……最终,所有元素合并成了一个有序的集合。这就是归并
那我们如何将把两个有序的小集合,归并成一个有序的大集合呢?
第一轮 :额外创建一个数组,用来存储俩个小数组排序之后的结果,数组长度当然就是俩个小数组之和啦
(p1,p2,p是三个辅助指针,用于记录当前操作的位 置。)
第二轮 : 从左到右依次比较俩数组中的元素,把较小的放入大集合中
- 由于2 < 4, 把2放入集合,,指针p1和p各右移一位;
- 由于3 < 4, 把3放入集合,,指针p1和p各右移一位;
- 由于 5 > 4, 把4放入集合,,指针p2和p各右移一位;
- 由于5 < 7, 把5放入集合,,指针p1和p各右移一位;
- 由于6 < 7, 把6放入集合,,指针p1和p各右移一位;
到这个时候大家已经发现了,此时左侧的小集合已经没有元素可用
第三轮 :将另外一个还有剩余元素的集合中, 把剩余元素按顺序复制到大集合尾部去
这样一来,就实现了两个有序的小集合就归并成了一个有序的大集合。
归并排序的代码实现
public static void mergeSort(int[] q, int l, int r) {
int[] temp = new int[r - l + 1]; //创建大集合存放数组
if (l >= r) return;
int mid = l + r >> 1;
mergeSort( q, l, mid);
mergeSort( q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r) {
if (q[i] <= q[j]) temp[k++] = q[i++];
else temp[k++] = q[j++];
}
while (i <= mid) temp[k ++ ] = q[i ++ ];
while (j <= r) temp[k ++ ] = q[j ++ ]; //将剩余的数放进大集合
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = temp[j]; //将大集合中的元素放回之前的数组
}
public static void main(String[] args) {
int [] array = {3, 5, 2, 6, 4, 8, 7, 9};
mergeSort(array, 0, array.length - 1);
System.out.println(Arrays.toString(array));
}
下篇预告: 图解十大排序篇五 快速排序