一、归并排序法分析
O(n * log n)的排序算法。
那么nlogn比n^2快多少:
画图分析归并排序法:
将数组分割的时候,分割了三次,那么如果是n个元素,分割需要log(N)次。每层需要处理N个数,所有此算法为O(n * log n)的排序算法。
现在要解决的问题是,如何将一个左边以及右边都排好序的数组,合并为一个完整的排好序的数组:
首先,将数组复制一份,并定义三个索引:
1比2小,所以1放到最终的数组中,同时移动索引:
2比4小,所以2放入到最终的数组中,同时移动索引:
3比4小,所以3放入到最终的数组中,同时移动索引:
4比6小,所以4放入到最终的数组中,同时移动索引:
后面,这样依次类推。
我们将这三个索引分别定义为i、j、k、l、r、m:
二、代码实现归并排序法
package paixu;
import java.util.Arrays;
/*
k表示最终i和j比较之后最终需要放的位置
i和j用来表示当前需要考虑的元素
left表示最左边的元素
right表示最右边的元素
middle表示中间位置元素,放在第一个已经排好序的数组的最后一个位置
*/
public class GuiBingPaiXu {
/*******************测试************************/
public static void main(String[] args) {
int[] nums = { 2, 7, 8, 3, 1, 6, 9, 0, 5, 4 };
mergeSort(nums , 0 , nums.length - 1 );
System.out.println(Arrays.toString(nums));
}
/********************算法************************/
/*
arr:要处理的数组
l:开始位置
r:结束位置
递归对arr[ l ... r ]范围的元素进行排序
*/
private static void mergeSort(int[] arr,int left,int right){
if( left >= right ) //表示已经排序完毕了
return;
int middle = ( left + right ) / 2; //计算中点位置
mergeSort( arr , left , middle ); //不断地对数组的左半边进行对边分
mergeSort( arr , middle+1 , right ); //不断地对数组的右半边进行对半分
merge( arr , left , middle , right ); //最后将已经分好的数组进行归并
}
//将arr[ l... mid ]和arr[ mid ... r ]两部分进行归并
/*
|2, 7, 8, 3, 1 | 6, 9, 0, 5, 4|
*/
private static void merge(int[] arr, int left, int mid, int right) {
int arr1[] = new int[ right - left + 1 ]; //定义临时数组
for( int i = left ; i <= right ; i++ ) //将数组的元素全部复制到新建的临时数组中
arr1[ i - left ] = arr[ i ];
int i = left;
int j = mid + 1; //定义两个索引
for( int k = left;k <= right ; k++){
if( i > mid ) //如果左边都比较完了
{
arr[ k ] = arr1[ j - left ]; //直接将右边的元素都放进去
j++;
}
else if( j > right ){ //右边都比较完了
arr[ k ] = arr1 [i - left ]; //直接将左边的元素放进去
i++;
}
else if( arr1[ i-left ] < arr1[ j-left ] ){
arr[ k ] = arr1[ i - left];
i++;
}
else
{
arr[ k ] = arr1[ j - left];
j++;
}
}
}
}
对近乎有序的数组排序,归并排序的速度比插入排序法慢一些。
那么如何优化呢?我们发现:当左边最大的元素都比右边最小的元素还小的时候就不用归并了。于是代码修改成如下:
public class GuiBingPaiXu {
/*******************测试************************/
public static void main(String[] args) {
int[] nums = { 2, 7, 8, 3, 1, 6, 9, 0, 5, 4 };
mergeSort(nums , 0 , nums.length - 1 );
System.out.println(Arrays.toString(nums));
}
/********************算法************************/
/*
arr:要处理的数组
l:开始位置
r:结束位置
递归对arr[ l ... r ]范围的元素进行排序
*/
private static void mergeSort(int[] arr,int left,int right){
if( left >= right ) //表示已经排序完毕了
return;
int middle = ( left + right ) / 2; //计算中点位置
mergeSort( arr , left , middle ); //不断地对数组的左半边进行对边分
mergeSort( arr , middle+1 , right ); //不断地对数组的右半边进行对半分
if( arr[middle] > arr[middle+1] ) //当左边最大的元素都比右边最小的元素还小的时候就不用归并了
merge( arr , left , middle , right ); //最后将已经分好的数组进行归并
}
//将arr[ l... mid ]和arr[ mid ... r ]两部分进行归并
/*
|2, 7, 8, 3, 1 | 6, 9, 0, 5, 4|
*/
private static void merge(int[] arr, int left, int mid, int right) {
int arr1[] = new int[ right - left + 1 ]; //定义临时数组
for( int i = left ; i <= right ; i++ ) //将数组的元素全部复制到新建的临时数组中
arr1[ i - left ] = arr[ i ];
int i = left;
int j = mid + 1; //定义两个索引
for( int k = left;k <= right ; k++){
if( i > mid ) //如果左边都比较完了
{
arr[ k ] = arr1[ j - left ]; //直接将右边的元素都放进去
j++;
}
else if( j > right ){ //右边都比较完了
arr[ k ] = arr1 [i - left ]; //直接将左边的元素放进去
i++;
}
else if( arr1[ i-left ] < arr1[ j-left ] ){
arr[ k ] = arr1[ i - left];
i++;
}
else
{
arr[ k ] = arr1[ j - left];
j++;
}
}
}
}
当数据足够少的时候,有序的可能性比较大,采用插入排序法,效果会更好。
为此,对代码进行如下的修改:
//if( left >= right ) //表示已经排序完毕了
// return;
if( right - left <= 10 ){
ChaRuPaiXu.ChaRuPaiXuFa2( arr , left ,right);
return;
}
转载请标明出处,原文地址:https://blog.csdn.net/weixin_41835916
总结整理不容易,如果觉得本文对您有帮助,请点击顶支持一下,您的支持是我写作最大的动力,谢谢。