经典排序算法--归并排序
基本思想:
归并排序是将两个或两个以上的有序表组合成一个新的有序表。其基本思想是:先将N个数据看成N个长度为1的表,将相邻两个表合并,得到长度为2的N/2个有序表,进一步将相邻的表合并,得到长度为4的N/4个有序表,以此类推,知道所有数据合并成一个长度为N的有序表位置。没一次归并称为一趟。
要解决归并问题,首先要解决两两归并问题(两个有序表合并成一个有序表),其java实现为:
1 2 public static void mergeTwo(int[] data,int first,int mid,int last,int[] tmp){ 3 4 //把data[first]-data[mid]当做第一个有序序列 ,这里设为A 5 //把data[mid+1]-data[last]当做第二个有序序列,这里设为B 6 //将两个有序序列合并,形成的新序列为data[first]-data[last] 7 int i = first, j = mid + 1; 8 int m = mid, n = last; 9 int k = 0; 10 while(i<=m&&j<=n){ 11 //A序列和B序列依次从起始值开始比较 12 //如果A序列值小,就将其移值tmp中 13 //并且A下标i+1;tmp下标k+1 14 if(data[i]<data[j]){ 15 tmp[k++] =data[i++]; 16 }else{ 17 //如果B序列值小,就将其移值tmp中 18 //并且B下标i+1;tmp下标k+1 19 tmp[k++] = data[j++]; 20 } 21 } 22 23 //如果A序列或者B序列已经全部移到tmp中 24 //则剩余的另一个序列依次移到tmp中 25 while(i<=m){ 26 tmp[k++] =data[i++]; 27 } 28 while(j<=n){ 29 tmp[k++] = data[j++]; 30 } 31 32 //遍历tmp,将tmp中元素移会data,此时data[first]-data[last]为有序序列 33 for (i = 0; i < k; i++) { 34 data[first + i] = tmp[i]; 35 } 36 }
实现方法:
归并排序有两种实现方法:
1)自底向上
2)自顶向下
自底向上的基本思想是:第一趟归并排序时,将待排序的文件R[1.....n]看做是n个长度为1的有序文件,将这些子文件两两归并,若n为偶数,则得到n/2个长度为2的有序文件;若n为奇数,则最后一个子文件轮空(不参与归并,直接进入下一趟归并),估本趟归并完成后,前n/2-1个有序子文件长度为2,单最后一个子文件长度仍未1;第二趟归并则是将第一趟归并所得到的n/2个有序文件两两归并,如此反复,知道得到最后长度为n的有序文件位置。
其程序实现:
1 package sortDemo; 2 3 public class MargeSort { 4 5 public static void main(String[] args) { 6 int[] sort ={3,2,1,4,6,5,8,9,10,7} ; 7 System.out.println("排序前:"); 8 print(sort); 9 int[] tmp = new int[sort.length]; 10 mergeSort(sort,0,sort.length-1,tmp); 11 System.out.println("\n排序后:"); 12 print(sort); 13 } 14 15 public static void mergeSort(int[] data,int first,int last,int[] tmp){ 16 if(first<last){ 17 int mid = (last-first)/2+first; 18 //使左侧有序 19 mergeSort(data,first,mid,tmp); 20 //使右侧有序 21 mergeSort(data,mid+1,last,tmp); 22 //合并两个有序的子序列 23 mergeTwo(data, first, mid, last, tmp); 24 } 25 } 26 27 public static void mergeTwo(int[] data,int first,int mid,int last,int[] tmp){ 28 29 //把data[first]-data[mid]当做第一个有序序列 ,这里设为A 30 //把data[mid+1]-data[last]当做第二个有序序列,这里设为B 31 //将两个有序序列合并,形成的新序列为data[first]-data[last] 32 int i = first, j = mid + 1; 33 int m = mid, n = last; 34 int k = 0; 35 while(i<=m&&j<=n){ 36 //A序列和B序列依次从起始值开始比较 37 //如果A序列值小,就将其移值tmp中 38 //并且A下标i+1;tmp下标k+1 39 if(data[i]<data[j]){ 40 tmp[k++] =data[i++]; 41 }else{ 42 //如果B序列值小,就将其移值tmp中 43 //并且B下标i+1;tmp下标k+1 44 tmp[k++] = data[j++]; 45 } 46 } 47 48 //如果A序列或者B序列已经全部移到tmp中 49 //则剩余的另一个序列依次移到tmp中 50 while(i<=m){ 51 tmp[k++] =data[i++]; 52 } 53 while(j<=n){ 54 tmp[k++] = data[j++]; 55 } 56 57 //遍历tmp,将tmp中元素移会data,此时data[first]-data[last]为有序序列 58 for (i = 0; i < k; i++) { 59 data[first + i] = tmp[i]; 60 } 61 } 62 63 public static void print(int[] a){ 64 for (int i = 0; i < a.length; i++) { 65 System.out.print(a[i]+" "); 66 } 67 System.out.println(); 68 } 69 }
算法分析:
(1)稳定性
归并排序是一种稳定的排序。
(2)存储结构要求
可用顺序存储结构,也易于在链表上实现。
(3) 时间复杂度
对长度为n的文件,需要进行[log2n]趟二路归并,没趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是最坏情况下均是O(nlgn).