第五天开始呢,我们讲解归并排序和插入排序进行比较的算法,从算法思路,到算法伪代码实现,到复杂度分析,从这里开始我们手把手搭建一个测试平台的基础,根据你自身硬件水平可以对下列代码进行从1000,到千万级测试,其中算法测试时间与你的机器硬件水平和实现的算法有关系,下面是算法的具体讲解。
(1)排序算法的思路
归并(Merge)基本思想:是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
直接插入插排的基本思想是:当插入第i(i >= 1)时,前面的V[0],V[1],……,V[i-1]已经排好序。这时,用V[I]的排序码与V[i-1],V[i-2],…的排序码顺序进行比较,找到插入位置即将V[i]插入,原来位置上的元素向后顺移。
(2)算法伪代码
//直接插入排序:
Insertion_sort(A){
for j←2 to length[A]{
temp = A[j];
i ←j-1;
}
while (i>0 and A[i] > temp){
A[i+1]=A[i];
i--;
A[i+1] = temp;
}
}
//归并排序:
void Merge( T A,T &B, s,m,e)//将A中的A[s..m]和A[m+1..e]合并成B[s..e]
{
for( j = m+1,k = s; j <=e && s<= m;k++)
{
if( A[j] < A[s] ) B[k] = A[j++];
esle B[k] = A[s++];
}
if( s <= m ) B[k..e] = A[s..m];
if( j <= e) B[k..e] = A[j..e];
}
(3)复杂度分析
1.归并算法
T(n)c1代价为1,c2代价为2T(n/2),c3代价为n
故当n=1时,T(n)=Θ(1);
当n>1时,T(n)=2T(n/2)+Θ(n)
即归并排序的时间复杂度为O(nlgn)。
2.插入排序
Tmin(n)= Θ(1)全部有序
Tmax(n)= Θ(n^2)全部无序
即插入排序的时间复杂度为O(n^2)
3. 结论
从时间复杂度平均分析的角度上来看,归并排序优于插入排序,而空间复杂度上两者都为O(1)是一样的
(4)代码主体部分
package runoob;
import java.util.Arrays;
/**
* 归并排序
*/
public class MergeSort {
// 将arr[l...mid]和arr[mid+1...r]两部分进行归并
private static void merge(Comparable[] arr, int l, int mid, int r) {
Comparable[] aux = Arrays.copyOfRange(arr, l, r + 1);// 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
int i = l, j = mid + 1;
for (int k = l; k <= r; k++) {
if (i > mid) { // 左半部分元素全部处理完毕
arr[k] = aux[j - l];
j++;
} else if (j > r) { // 右半部分元素全部处理完毕
arr[k] = aux[i - l];
i++;
} else if (aux[i - l].compareTo(aux[j - l]) < 0) { // 左半部分元素 < 右半部分元素
arr[k] = aux[i - l];
i++;
} else { // 左半部分元素 >= 右半部分元素
arr[k] = aux[j - l];
j++;
}
}
}
private static void sort(Comparable[] arr, int l, int r) {// 递归使用归并排序,对arr[l...r]的范围进行排序
if (l >= r) {
return;
}
int mid = (l + r) / 2;
sort(arr, l, mid);
sort(arr, mid + 1, r);
// 对于arr[mid] <= arr[mid+1]的情况,不进行merge
// 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
if (arr[mid].compareTo(arr[mid + 1]) > 0)
merge(arr, l, mid, r);
}
public static void sort(Comparable[] arr) {
int n = arr.length;
sort(arr, 0, n - 1);
}
// 测试MergeSort
public static void MergeSort_Incompear(long num){//归并插入比较
Integer[] arr = SortHelper.generateRandomArray(num, 0, 100);
Integer[] arr2=Arrays.copyOfRange(arr,0, Math.toIntExact(num));
long start1=System.nanoTime();
sort(arr);
long end1=System.nanoTime();
System.out.println("归并算法花费时间:" + (end1 - start1)/1000 + "(ms)");
long start2=System.nanoTime();
InsertionSort.sort(arr2);//调用往期插入排序内容
long end2=System.nanoTime();
System.out.println("插入算法花费时间:" + (end2 - start2)/1000 + "(ms)");
}
}
对应代码中的SortHelper类我们留一个小小的悬念,留到最后来进行叙说,其中目前来说他的方法generateRandomArray的参数为,(num,left,right)第一个参数参与算法生成的数量级,作为随机生成序列,它可以为千万,因为是long级别,left和right则为生成序列的大小范围,生成的序列为返回值类型为Integer[]。
(5)测试结果如下:
因为算法的时间复杂度比较高,如果笔者有兴趣可以尝试千万级的算法测试,这里便不在赘述。