转载请注明出处:http://blog.csdn.net/ljmingcom304/article/details/50379605 本文出自:【梁敬明的博客】
1、基本思想:
归并排序就是利用归并的思想实现的排序方法。而且充分利用了完全二叉树的深度是的特性,因此效率比较高。其基本原理如下:对于给定的一组记录,利用递归与分治技术将数据序列划分成为越来越小的半子表,在对半子表排序,最后再用递归方法将排好序的半子表合并成为越来越大的有序序列。
经过第一轮比较后得到最小的记录,然后将该记录的位置与第一个记录的位置交换;接着对不包括第一个记录以外的其他记录进行第二次比较,得到最小记录并与第二个位置记录交换;重复该过程,知道进行比较的记录只剩下一个为止。
2、复杂度分析
一趟归并需要将数组 a[]中相邻的长度为h的有序序列进行两两归并.并将结果放到temp[]中,这需要将待排序列中的所有记录扫描一遍,因此耗费O(n),而又完全二叉树的深度可知,整个归并排序需要进行()次,因此总的时间复杂度为O(nlogn),而且这是归并排序算法中最好、最坏、平均的时间性能。
由于归并排序在归并过程中需要与原始序列同样数量的存储空间存放归并结果以及递归时深度为的栈空间,因此空间复杂度为O(n+logn).
另外,对代码进行仔细研究,发现merge函数中有if (a[i] < a[j]) 的语句,说明它需要两两比较,不存在跳跃,因此归并排序是一种稳定的排序算法。
也就是说,归并排序是一种比较占内存,但却效率高且稳定的算法。
3、工作原理:
(1)申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
(2)设定两个指针,最初位置分别为两个已经排序序列的起始位置
(3)比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
(4)重复步骤3直到某一指针达到序列尾
(5)将另一序列剩下的所有元素直接复制到合并序列尾
4、归并排序过程如下:
以数组{50,10,90,30,70,40,80,60,20}为例,
最后的排序结果:
10,20,30,40,50,60,70,80,90
4、Java实现如下:
public class HeapSort {
public static void main(String[] args) {
int[] array = { 89, 18, 7, 36, 5, 4, 3, 2, 1, 0 };
System.out.println("排序前数组:" + Arrays.toString(array));
HeapSort.heapSort(array);
System.out.println("排序后数组:" + Arrays.toString(array));
}
public static void heapSort(int[] a) {
// 循环建立初始堆,若父节点索引为i,那么左节点的索引为i*2+1,即左节点为a.length时,其父节点应当小于a.length/2
for (int i = a.length / 2; i >= 0; i--) {// 遍历存在子节点的父节点
adjustHeap(a, i, a.length - 1);
}
// 进行n-1次循环完成排序
for (int i = a.length - 1; i > 0; i--) {
// 最后一个元素和第一个元素进行交换
int temp = a[i];
a[i] = a[0];
a[0] = temp;
System.out.println("排序中数组:" + Arrays.toString(a));
adjustHeap(a, 0, i);
}
}
// 将数组转换为大根堆,大根堆的根节点为数组中的最大值
public static void adjustHeap(int[] a, int parent, int length) {
int temp = a[parent]; // 父节点的值
int child = 2 * parent + 1;// 左子节点的索引
while (child < length) {// 判断左节点是否为最大索引 //
// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
if (child + 1 < length && a[child] < a[child + 1]) {
child++;// 将左子节点转换为右子节点
}
if (temp > a[child])
break; // 当父节点的值直接大于子节点的值时,直接退出
// 将子节点的值赋值给父节点
a[parent] = a[child];
// 选取子节点的左子节点继续向下筛选
parent = child;
child = 2 * parent + 1;
}
// 若发生交换,此时parent代表子节点索引,没有发生交换,此时parent仍旧代表父节点索引
a[parent] = temp;
}
}