Java排序算法之归并排序
算法概述
归并排序(Merge)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为两路归并排序算法。
算法思想
归并排序就是利用归并的思想实现的排序方法。而且充分利用了完全二叉树的深度是的特性,因此效率比较高。其基本原理如下:对于给定的一组记录,利用递归与分治技术将数据序列划分成为越来越小的半子表,在对半子表排序,最后再用递归方法将排好序的半子表合并成为越来越大的有序序列。 经过第一轮比较后得到最小的记录,然后将该记录的位置与第一个记录的位置交换;接着对不包括第一个记录以外的其他记录进行第二次比较,得到最小记录并与第二个位置记录交换;重复该过程,知道进行比较的记录只剩下一个为止。
两路归并排序算法思想:
①把 n 个记录看成 n 个长度为1的有序子表;
②进行两两归并使记录关键字有序,得到 n/2 个长度为 2 的有序子表;
③重复第②步直到所有记录归并成一个长度为 n 的有序表为止。
详细步骤:
1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列(申请的内存空间大小必须大于等于待排序数组的长度)。
2、设定两个指针,最初位置分别为两个已经排序序列的起始位置。
3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置。
4、重复步骤3直到某一指针达到序列尾。
5、将另一序列剩下的所有元素直接复制到合并序列尾。
算法源码
/**
* 归并排序
*
* @author Administrator
*
*/
public class MergeSort {
public static void merge_sort(int a[],int first,int last,int temp[]){
if(first < last){//使用递归算法将左、右两部分排好序。
int middle = (first + last)/2;
merge_sort(a,first,middle,temp);//左半部分排好序
System.out.print("左半部分排序结果:");
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + "\t");
}
System.out.println("");
merge_sort(a,middle+1,last,temp);//右半部分排好序
System.out.print("右半部分排序结果:");
for (int j = 0; j < a.length; j++) {
System.out.print(a[j] + "\t");
}
System.out.println("");
mergeArray(a,first,middle,last,temp); //合并左右部分
}
}
//合并 :将两个序列a[first-middle],a[middle+1-end]合并
public static void mergeArray(int a[],int first,int middle,int end,int temp[]){
int i = first;//要排序数组的第一个数下标
int m = middle;//要排序数组的中间数下标
int j = middle+1;
int n = end;//要排序数组的最后一个数下标
int k = 0;
while(i<=m && j<=n){
if(a[i] <= a[j]){
temp[k] = a[i];
k++;
i++;
}else{
temp[k] = a[j];
k++;
j++;
}
}
while(i<=m){
temp[k] = a[i];
k++;
i++;
}
while(j<=n){
temp[k] = a[j];
k++;
j++;
}
for(int ii=0;ii<k;ii++){
a[first + ii] = temp[ii];
}
}
public static void main(String[] args) {
int array[]={100,30, 111,46,99,-1,53,20};
int temp[]=new int[8];//申请分配内存>=array.length
merge_sort(array, 0, array.length-1, temp);
System.out.print("最终排序结果:");
for (int a = 0; a < array.length; a++) {
System.out.print(array[a] + "\t");
}
}
}
测试效果:
左半部分排序结果:100 30 111 46 99 -1 53 20
右半部分排序结果:100 30 111 46 99 -1 53 20
左半部分排序结果:30 100 111 46 99 -1 53 20
左半部分排序结果:30 100 111 46 99 -1 53 20
右半部分排序结果:30 100 111 46 99 -1 53 20
右半部分排序结果:30 100 46 111 99 -1 53 20
左半部分排序结果:30 46 100 111 99 -1 53 20
左半部分排序结果:30 46 100 111 99 -1 53 20
右半部分排序结果:30 46 100 111 99 -1 53 20
左半部分排序结果:30 46 100 111 -1 99 53 20
左半部分排序结果:30 46 100 111 -1 99 53 20
右半部分排序结果:30 46 100 111 -1 99 53 20
右半部分排序结果:30 46 100 111 -1 99 20 53
右半部分排序结果:30 46 100 111 -1 20 53 99
最终排序结果:-1 20 30 46 53 99 100 111
这样效果不是很好,把左半部分排序和右半部分排序分开:
左半部分排序
左半部分排序结果:100 30 111 46 99 -1 53 20
左半部分排序结果:30 100 111 46 99 -1 53 20
左半部分排序结果:30 100 111 46 99 -1 53 20
左半部分排序结果:30 46 100 111 99 -1 53 20
左半部分排序结果:30 46 100 111 99 -1 53 20
左半部分排序结果:30 46 100 111 -1 99 53 20
左半部分排序结果:30 46 100 111 -1 99 53 20
右半部分排序
右半部分排序结果:100 30 111 46 99 -1 53 20
右半部分排序结果:30 100 111 46 99 -1 53 20
右半部分排序结果:30 100 46 111 99 -1 53 20
右半部分排序结果:30 46 100 111 99 -1 53 20
右半部分排序结果:30 46 100 111 -1 99 53 20
右半部分排序结果:30 46 100 111 -1 99 20 53
右半部分排序结果:30 46 100 111 -1 20 53 99
其排序过程完全按照其算法步骤来实现。
算法分析
(1)稳定性
归并排序是一种稳定的排序。
(2)存储结构要求
可用顺序存储结构。也易于在链表上实现。
(3)时间复杂度
对长度为n的文件,需进行趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlgn)
(4)空间复杂度
需要一个辅助向量来暂存两有序子文件归并的结果,故其辅助空间复杂度为O(n),显然它不是就地排序。
注意:若用单链表做存储结构,很容易给出就地的归并排序。
总结
归并排序 (merge sort) 是一类与插入排序、交换排序、选择排序不同的另一种排序方法。归并排序是一种比较占内存,但却效率高且稳定的算法。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。归并排序有多路归并排序、两路归并排序 , 可用于内排序,也可以用于外排序。